module GHC.Unit.State (
module GHC.Unit.Info,
UnitState(..),
UnitDatabase (..),
UnitErr (..),
emptyUnitState,
initUnits,
readUnitDatabases,
readUnitDatabase,
getUnitDbRefs,
resolveUnitDatabase,
listUnitInfo,
UnitInfoMap,
lookupUnit,
lookupUnit',
unsafeLookupUnit,
lookupUnitId,
lookupUnitId',
unsafeLookupUnitId,
lookupPackageName,
improveUnit,
searchPackageId,
listVisibleModuleNames,
lookupModuleInAllUnits,
lookupModuleWithSuggestions,
lookupPluginModuleWithSuggestions,
requirementMerges,
LookupResult(..),
ModuleSuggestion(..),
ModuleOrigin(..),
UnusableUnitReason(..),
pprReason,
closeUnitDeps,
closeUnitDeps',
mayThrowUnitErr,
ShHoleSubst,
renameHoleUnit,
renameHoleModule,
renameHoleUnit',
renameHoleModule',
instUnitToUnit,
instModuleToModule,
pprFlag,
pprUnits,
pprUnitsSimple,
pprUnitIdForUser,
pprUnitInfoForUser,
pprModuleMap,
pprWithUnitState,
unwireUnit
)
where
#include "HsVersions.h"
import GHC.Prelude
import GHC.Driver.Session
import GHC.Platform
import GHC.Platform.Ways
import GHC.Unit.Database
import GHC.Unit.Info
import GHC.Unit.Ppr
import GHC.Unit.Types
import GHC.Unit.Module
import GHC.Unit.Home
import GHC.Types.Unique.FM
import GHC.Types.Unique.DFM
import GHC.Types.Unique.Set
import GHC.Types.Unique.DSet
import GHC.Utils.Misc
import GHC.Utils.Panic
import GHC.Utils.Outputable as Outputable
import GHC.Data.Maybe
import System.Environment ( getEnv )
import GHC.Data.FastString
import qualified GHC.Data.ShortText as ST
import GHC.Utils.Logger
import GHC.Utils.Error
import GHC.Utils.Exception
import System.Directory
import System.FilePath as FilePath
import Control.Monad
import Data.Graph (stronglyConnComp, SCC(..))
import Data.Char ( toUpper )
import Data.List ( intersperse, partition, sortBy, isSuffixOf )
import Data.Map (Map)
import Data.Set (Set)
import Data.Monoid (First(..))
import qualified Data.Semigroup as Semigroup
import qualified Data.Map as Map
import qualified Data.Map.Strict as MapStrict
import qualified Data.Set as Set
data ModuleOrigin =
ModHidden
| ModUnusable UnusableUnitReason
| ModOrigin {
fromOrigUnit :: Maybe Bool
, fromExposedReexport :: [UnitInfo]
, fromHiddenReexport :: [UnitInfo]
, fromPackageFlag :: Bool
}
instance Outputable ModuleOrigin where
ppr ModHidden = text "hidden module"
ppr (ModUnusable _) = text "unusable module"
ppr (ModOrigin e res rhs f) = sep (punctuate comma (
(case e of
Nothing -> []
Just False -> [text "hidden package"]
Just True -> [text "exposed package"]) ++
(if null res
then []
else [text "reexport by" <+>
sep (map (ppr . mkUnit) res)]) ++
(if null rhs
then []
else [text "hidden reexport by" <+>
sep (map (ppr . mkUnit) res)]) ++
(if f then [text "package flag"] else [])
))
fromExposedModules :: Bool -> ModuleOrigin
fromExposedModules e = ModOrigin (Just e) [] [] False
fromReexportedModules :: Bool -> UnitInfo -> ModuleOrigin
fromReexportedModules True pkg = ModOrigin Nothing [pkg] [] False
fromReexportedModules False pkg = ModOrigin Nothing [] [pkg] False
fromFlag :: ModuleOrigin
fromFlag = ModOrigin Nothing [] [] True
instance Semigroup ModuleOrigin where
x@(ModOrigin e res rhs f) <> y@(ModOrigin e' res' rhs' f') =
ModOrigin (g e e') (res ++ res') (rhs ++ rhs') (f || f')
where g (Just b) (Just b')
| b == b' = Just b
| otherwise = pprPanic "ModOrigin: package both exposed/hidden" $
text "x: " <> ppr x $$ text "y: " <> ppr y
g Nothing x = x
g x Nothing = x
x <> y = pprPanic "ModOrigin: hidden module redefined" $
text "x: " <> ppr x $$ text "y: " <> ppr y
instance Monoid ModuleOrigin where
mempty = ModOrigin Nothing [] [] False
mappend = (Semigroup.<>)
originVisible :: ModuleOrigin -> Bool
originVisible ModHidden = False
originVisible (ModUnusable _) = False
originVisible (ModOrigin b res _ f) = b == Just True || not (null res) || f
originEmpty :: ModuleOrigin -> Bool
originEmpty (ModOrigin Nothing [] [] False) = True
originEmpty _ = False
type PreloadUnitClosure = UniqSet UnitId
type VisibilityMap = Map Unit UnitVisibility
data UnitVisibility = UnitVisibility
{ uv_expose_all :: Bool
, uv_renamings :: [(ModuleName, ModuleName)]
, uv_package_name :: First FastString
, uv_requirements :: Map ModuleName (Set InstantiatedModule)
, uv_explicit :: Bool
}
instance Outputable UnitVisibility where
ppr (UnitVisibility {
uv_expose_all = b,
uv_renamings = rns,
uv_package_name = First mb_pn,
uv_requirements = reqs,
uv_explicit = explicit
}) = ppr (b, rns, mb_pn, reqs, explicit)
instance Semigroup UnitVisibility where
uv1 <> uv2
= UnitVisibility
{ uv_expose_all = uv_expose_all uv1 || uv_expose_all uv2
, uv_renamings = uv_renamings uv1 ++ uv_renamings uv2
, uv_package_name = mappend (uv_package_name uv1) (uv_package_name uv2)
, uv_requirements = Map.unionWith Set.union (uv_requirements uv1) (uv_requirements uv2)
, uv_explicit = uv_explicit uv1 || uv_explicit uv2
}
instance Monoid UnitVisibility where
mempty = UnitVisibility
{ uv_expose_all = False
, uv_renamings = []
, uv_package_name = First Nothing
, uv_requirements = Map.empty
, uv_explicit = False
}
mappend = (Semigroup.<>)
data UnitConfig = UnitConfig
{ unitConfigPlatformArchOS :: !ArchOS
, unitConfigWays :: !Ways
, unitConfigAllowVirtual :: !Bool
, unitConfigProgramName :: !String
, unitConfigGlobalDB :: !FilePath
, unitConfigGHCDir :: !FilePath
, unitConfigDBName :: !String
, unitConfigAutoLink :: ![UnitId]
, unitConfigDistrustAll :: !Bool
, unitConfigHideAll :: !Bool
, unitConfigHideAllPlugins :: !Bool
, unitConfigDBCache :: Maybe [UnitDatabase UnitId]
, unitConfigFlagsDB :: [PackageDBFlag]
, unitConfigFlagsExposed :: [PackageFlag]
, unitConfigFlagsIgnored :: [IgnorePackageFlag]
, unitConfigFlagsTrusted :: [TrustFlag]
, unitConfigFlagsPlugins :: [PackageFlag]
}
initUnitConfig :: DynFlags -> Maybe [UnitDatabase UnitId] -> UnitConfig
initUnitConfig dflags cached_dbs =
let !hu_id = homeUnitId_ dflags
!hu_instanceof = homeUnitInstanceOf_ dflags
!hu_instantiations = homeUnitInstantiations_ dflags
autoLink
| not (gopt Opt_AutoLinkPackages dflags) = []
| otherwise = filter (hu_id /=) [baseUnitId, rtsUnitId]
allow_virtual_units = case (hu_instanceof, hu_instantiations) of
(Just u, is) -> u == hu_id && any (isHoleModule . snd) is
_ -> False
in UnitConfig
{ unitConfigPlatformArchOS = platformArchOS (targetPlatform dflags)
, unitConfigProgramName = programName dflags
, unitConfigWays = ways dflags
, unitConfigAllowVirtual = allow_virtual_units
, unitConfigGlobalDB = globalPackageDatabasePath dflags
, unitConfigGHCDir = topDir dflags
, unitConfigDBName = "package.conf.d"
, unitConfigAutoLink = autoLink
, unitConfigDistrustAll = gopt Opt_DistrustAllPackages dflags
, unitConfigHideAll = gopt Opt_HideAllPackages dflags
, unitConfigHideAllPlugins = gopt Opt_HideAllPluginPackages dflags
, unitConfigDBCache = cached_dbs
, unitConfigFlagsDB = packageDBFlags dflags
, unitConfigFlagsExposed = packageFlags dflags
, unitConfigFlagsIgnored = ignorePackageFlags dflags
, unitConfigFlagsTrusted = trustFlags dflags
, unitConfigFlagsPlugins = pluginPackageFlags dflags
}
type ModuleNameProvidersMap =
Map ModuleName (Map Module ModuleOrigin)
data UnitState = UnitState {
unitInfoMap :: UnitInfoMap,
preloadClosure :: PreloadUnitClosure,
packageNameMap :: UniqFM PackageName IndefUnitId,
wireMap :: Map UnitId UnitId,
unwireMap :: Map UnitId UnitId,
preloadUnits :: [UnitId],
explicitUnits :: [Unit],
moduleNameProvidersMap :: !ModuleNameProvidersMap,
pluginModuleNameProvidersMap :: !ModuleNameProvidersMap,
requirementContext :: Map ModuleName [InstantiatedModule],
allowVirtualUnits :: !Bool
}
emptyUnitState :: UnitState
emptyUnitState = UnitState {
unitInfoMap = Map.empty,
preloadClosure = emptyUniqSet,
packageNameMap = emptyUFM,
wireMap = Map.empty,
unwireMap = Map.empty,
preloadUnits = [],
explicitUnits = [],
moduleNameProvidersMap = Map.empty,
pluginModuleNameProvidersMap = Map.empty,
requirementContext = Map.empty,
allowVirtualUnits = False
}
data UnitDatabase unit = UnitDatabase
{ unitDatabasePath :: FilePath
, unitDatabaseUnits :: [GenUnitInfo unit]
}
type UnitInfoMap = Map UnitId UnitInfo
lookupUnit :: UnitState -> Unit -> Maybe UnitInfo
lookupUnit pkgs = lookupUnit' (allowVirtualUnits pkgs) (unitInfoMap pkgs) (preloadClosure pkgs)
lookupUnit' :: Bool -> UnitInfoMap -> PreloadUnitClosure -> Unit -> Maybe UnitInfo
lookupUnit' allowOnTheFlyInst pkg_map closure u = case u of
HoleUnit -> error "Hole unit"
RealUnit i -> Map.lookup (unDefinite i) pkg_map
VirtUnit i
| allowOnTheFlyInst
->
fmap (renameUnitInfo pkg_map closure (instUnitInsts i))
(Map.lookup (indefUnit (instUnitInstanceOf i)) pkg_map)
| otherwise
->
Map.lookup (virtualUnitId i) pkg_map
lookupUnitId :: UnitState -> UnitId -> Maybe UnitInfo
lookupUnitId state uid = lookupUnitId' (unitInfoMap state) uid
lookupUnitId' :: UnitInfoMap -> UnitId -> Maybe UnitInfo
lookupUnitId' db uid = Map.lookup uid db
unsafeLookupUnit :: HasDebugCallStack => UnitState -> Unit -> UnitInfo
unsafeLookupUnit state u = case lookupUnit state u of
Just info -> info
Nothing -> pprPanic "unsafeLookupUnit" (ppr u)
unsafeLookupUnitId :: HasDebugCallStack => UnitState -> UnitId -> UnitInfo
unsafeLookupUnitId state uid = case lookupUnitId state uid of
Just info -> info
Nothing -> pprPanic "unsafeLookupUnitId" (ppr uid)
lookupPackageName :: UnitState -> PackageName -> Maybe IndefUnitId
lookupPackageName pkgstate n = lookupUFM (packageNameMap pkgstate) n
searchPackageId :: UnitState -> PackageId -> [UnitInfo]
searchPackageId pkgstate pid = filter ((pid ==) . unitPackageId)
(listUnitInfo pkgstate)
mkUnitInfoMap :: [UnitInfo] -> UnitInfoMap
mkUnitInfoMap infos = foldl' add Map.empty infos
where
mkVirt p = virtualUnitId (mkInstantiatedUnit (unitInstanceOf p) (unitInstantiations p))
add pkg_map p
| not (null (unitInstantiations p))
= Map.insert (mkVirt p) p
$ Map.insert (unitId p) p
$ pkg_map
| otherwise
= Map.insert (unitId p) p pkg_map
listUnitInfo :: UnitState -> [UnitInfo]
listUnitInfo state = Map.elems (unitInfoMap state)
initUnits :: Logger -> DynFlags -> Maybe [UnitDatabase UnitId] -> IO ([UnitDatabase UnitId], UnitState, HomeUnit)
initUnits logger dflags cached_dbs = do
let forceUnitInfoMap (state, _) = unitInfoMap state `seq` ()
let ctx = initSDocContext dflags defaultUserStyle
let printer = debugTraceMsg logger dflags
(unit_state,dbs) <- withTiming logger dflags (text "initializing unit database")
forceUnitInfoMap
$ mkUnitState ctx printer (initUnitConfig dflags cached_dbs)
dumpIfSet_dyn logger dflags Opt_D_dump_mod_map "Module Map"
FormatText (updSDocContext (\ctx -> ctx {sdocLineLength = 200})
$ pprModuleMap (moduleNameProvidersMap unit_state))
let home_unit = mkHomeUnit unit_state
(homeUnitId_ dflags)
(homeUnitInstanceOf_ dflags)
(homeUnitInstantiations_ dflags)
return (dbs,unit_state,home_unit)
mkHomeUnit
:: UnitState
-> UnitId
-> Maybe UnitId
-> [(ModuleName, Module)]
-> HomeUnit
mkHomeUnit unit_state hu_id hu_instanceof hu_instantiations_ =
let
wmap = wireMap unit_state
hu_instantiations = map (fmap (upd_wired_in_mod wmap)) hu_instantiations_
in case (hu_instanceof, hu_instantiations) of
(Nothing,[]) -> DefiniteHomeUnit hu_id Nothing
(Nothing, _) -> throwGhcException $ CmdLineError ("Use of -instantiated-with requires -this-component-id")
(Just _, []) -> throwGhcException $ CmdLineError ("Use of -this-component-id requires -instantiated-with")
(Just u, is)
| all (isHoleModule . snd) is && u == hu_id
-> IndefiniteHomeUnit u is
| otherwise
-> DefiniteHomeUnit hu_id (Just (u, is))
readUnitDatabases :: (Int -> SDoc -> IO ()) -> UnitConfig -> IO [UnitDatabase UnitId]
readUnitDatabases printer cfg = do
conf_refs <- getUnitDbRefs cfg
confs <- liftM catMaybes $ mapM (resolveUnitDatabase cfg) conf_refs
mapM (readUnitDatabase printer cfg) confs
getUnitDbRefs :: UnitConfig -> IO [PkgDbRef]
getUnitDbRefs cfg = do
let system_conf_refs = [UserPkgDb, GlobalPkgDb]
e_pkg_path <- tryIO (getEnv $ map toUpper (unitConfigProgramName cfg) ++ "_PACKAGE_PATH")
let base_conf_refs = case e_pkg_path of
Left _ -> system_conf_refs
Right path
| not (null path) && isSearchPathSeparator (last path)
-> map PkgDbPath (splitSearchPath (init path)) ++ system_conf_refs
| otherwise
-> map PkgDbPath (splitSearchPath path)
return $ reverse (foldr doFlag base_conf_refs (unitConfigFlagsDB cfg))
where
doFlag (PackageDB p) dbs = p : dbs
doFlag NoUserPackageDB dbs = filter isNotUser dbs
doFlag NoGlobalPackageDB dbs = filter isNotGlobal dbs
doFlag ClearPackageDBs _ = []
isNotUser UserPkgDb = False
isNotUser _ = True
isNotGlobal GlobalPkgDb = False
isNotGlobal _ = True
resolveUnitDatabase :: UnitConfig -> PkgDbRef -> IO (Maybe FilePath)
resolveUnitDatabase cfg GlobalPkgDb = return $ Just (unitConfigGlobalDB cfg)
resolveUnitDatabase cfg UserPkgDb = runMaybeT $ do
dir <- versionedAppDir (unitConfigProgramName cfg) (unitConfigPlatformArchOS cfg)
let pkgconf = dir </> unitConfigDBName cfg
exist <- tryMaybeT $ doesDirectoryExist pkgconf
if exist then return pkgconf else mzero
resolveUnitDatabase _ (PkgDbPath name) = return $ Just name
readUnitDatabase :: (Int -> SDoc -> IO ()) -> UnitConfig -> FilePath -> IO (UnitDatabase UnitId)
readUnitDatabase printer cfg conf_file = do
isdir <- doesDirectoryExist conf_file
proto_pkg_configs <-
if isdir
then readDirStyleUnitInfo conf_file
else do
isfile <- doesFileExist conf_file
if isfile
then do
mpkgs <- tryReadOldFileStyleUnitInfo
case mpkgs of
Just pkgs -> return pkgs
Nothing -> throwGhcExceptionIO $ InstallationError $
"ghc no longer supports single-file style package " ++
"databases (" ++ conf_file ++
") use 'ghc-pkg init' to create the database with " ++
"the correct format."
else throwGhcExceptionIO $ InstallationError $
"can't find a package database at " ++ conf_file
let
conf_file' = dropTrailingPathSeparator conf_file
top_dir = unitConfigGHCDir cfg
pkgroot = takeDirectory conf_file'
pkg_configs1 = map (mungeUnitInfo top_dir pkgroot . mapUnitInfo (\(UnitKey x) -> UnitId x) . mkUnitKeyInfo)
proto_pkg_configs
return $ UnitDatabase conf_file' pkg_configs1
where
readDirStyleUnitInfo conf_dir = do
let filename = conf_dir </> "package.cache"
cache_exists <- doesFileExist filename
if cache_exists
then do
printer 2 $ text "Using binary package database:" <+> text filename
readPackageDbForGhc filename
else do
printer 2 $ text "There is no package.cache in"
<+> text conf_dir
<> text ", checking if the database is empty"
db_empty <- all (not . isSuffixOf ".conf")
<$> getDirectoryContents conf_dir
if db_empty
then do
printer 3 $ text "There are no .conf files in"
<+> text conf_dir <> text ", treating"
<+> text "package database as empty"
return []
else
throwGhcExceptionIO $ InstallationError $
"there is no package.cache in " ++ conf_dir ++
" even though package database is not empty"
tryReadOldFileStyleUnitInfo = do
content <- readFile conf_file `catchIO` \_ -> return ""
if take 2 content == "[]"
then do
let conf_dir = conf_file <.> "d"
direxists <- doesDirectoryExist conf_dir
if direxists
then do printer 2 (text "Ignoring old file-style db and trying:" <+> text conf_dir)
liftM Just (readDirStyleUnitInfo conf_dir)
else return (Just [])
else return Nothing
distrustAllUnits :: [UnitInfo] -> [UnitInfo]
distrustAllUnits pkgs = map distrust pkgs
where
distrust pkg = pkg{ unitIsTrusted = False }
mungeUnitInfo :: FilePath -> FilePath
-> UnitInfo -> UnitInfo
mungeUnitInfo top_dir pkgroot =
mungeDynLibFields
. mungeUnitInfoPaths (ST.pack top_dir) (ST.pack pkgroot)
mungeDynLibFields :: UnitInfo -> UnitInfo
mungeDynLibFields pkg =
pkg {
unitLibraryDynDirs = case unitLibraryDynDirs pkg of
[] -> unitLibraryDirs pkg
ds -> ds
}
applyTrustFlag
:: UnitPrecedenceMap
-> UnusableUnits
-> [UnitInfo]
-> TrustFlag
-> MaybeErr UnitErr [UnitInfo]
applyTrustFlag prec_map unusable pkgs flag =
case flag of
TrustPackage str ->
case selectPackages prec_map (PackageArg str) pkgs unusable of
Left ps -> Failed (TrustFlagErr flag ps)
Right (ps,qs) -> Succeeded (map trust ps ++ qs)
where trust p = p {unitIsTrusted=True}
DistrustPackage str ->
case selectPackages prec_map (PackageArg str) pkgs unusable of
Left ps -> Failed (TrustFlagErr flag ps)
Right (ps,qs) -> Succeeded (distrustAllUnits ps ++ qs)
applyPackageFlag
:: UnitPrecedenceMap
-> UnitInfoMap
-> PreloadUnitClosure
-> UnusableUnits
-> Bool
-> [UnitInfo]
-> VisibilityMap
-> PackageFlag
-> MaybeErr UnitErr VisibilityMap
applyPackageFlag prec_map pkg_map closure unusable no_hide_others pkgs vm flag =
case flag of
ExposePackage _ arg (ModRenaming b rns) ->
case findPackages prec_map pkg_map closure arg pkgs unusable of
Left ps -> Failed (PackageFlagErr flag ps)
Right (p:_) -> Succeeded vm'
where
n = fsPackageName p
reqs | UnitIdArg orig_uid <- arg = collectHoles orig_uid
| otherwise = Map.empty
collectHoles uid = case uid of
HoleUnit -> Map.empty
RealUnit {} -> Map.empty
VirtUnit indef ->
let local = [ Map.singleton
(moduleName mod)
(Set.singleton $ Module indef mod_name)
| (mod_name, mod) <- instUnitInsts indef
, isHoleModule mod ]
recurse = [ collectHoles (moduleUnit mod)
| (_, mod) <- instUnitInsts indef ]
in Map.unionsWith Set.union $ local ++ recurse
uv = UnitVisibility
{ uv_expose_all = b
, uv_renamings = rns
, uv_package_name = First (Just n)
, uv_requirements = reqs
, uv_explicit = True
}
vm' = Map.insertWith mappend (mkUnit p) uv vm_cleared
vm_cleared | no_hide_others = vm
| (_:_) <- rns = vm
| otherwise = Map.filterWithKey
(\k uv -> k == mkUnit p
|| First (Just n) /= uv_package_name uv) vm
_ -> panic "applyPackageFlag"
HidePackage str ->
case findPackages prec_map pkg_map closure (PackageArg str) pkgs unusable of
Left ps -> Failed (PackageFlagErr flag ps)
Right ps -> Succeeded $ foldl' (flip Map.delete) vm (map mkUnit ps)
findPackages :: UnitPrecedenceMap
-> UnitInfoMap
-> PreloadUnitClosure
-> PackageArg -> [UnitInfo]
-> UnusableUnits
-> Either [(UnitInfo, UnusableUnitReason)]
[UnitInfo]
findPackages prec_map pkg_map closure arg pkgs unusable
= let ps = mapMaybe (finder arg) pkgs
in if null ps
then Left (mapMaybe (\(x,y) -> finder arg x >>= \x' -> return (x',y))
(Map.elems unusable))
else Right (sortByPreference prec_map ps)
where
finder (PackageArg str) p
= if matchingStr str p
then Just p
else Nothing
finder (UnitIdArg uid) p
= case uid of
RealUnit (Definite iuid)
| iuid == unitId p
-> Just p
VirtUnit inst
| indefUnit (instUnitInstanceOf inst) == unitId p
-> Just (renameUnitInfo pkg_map closure (instUnitInsts inst) p)
_ -> Nothing
selectPackages :: UnitPrecedenceMap -> PackageArg -> [UnitInfo]
-> UnusableUnits
-> Either [(UnitInfo, UnusableUnitReason)]
([UnitInfo], [UnitInfo])
selectPackages prec_map arg pkgs unusable
= let matches = matching arg
(ps,rest) = partition matches pkgs
in if null ps
then Left (filter (matches.fst) (Map.elems unusable))
else Right (sortByPreference prec_map ps, rest)
renameUnitInfo :: UnitInfoMap -> PreloadUnitClosure -> [(ModuleName, Module)] -> UnitInfo -> UnitInfo
renameUnitInfo pkg_map closure insts conf =
let hsubst = listToUFM insts
smod = renameHoleModule' pkg_map closure hsubst
new_insts = map (\(k,v) -> (k,smod v)) (unitInstantiations conf)
in conf {
unitInstantiations = new_insts,
unitExposedModules = map (\(mod_name, mb_mod) -> (mod_name, fmap smod mb_mod))
(unitExposedModules conf)
}
matchingStr :: String -> UnitInfo -> Bool
matchingStr str p
= str == unitPackageIdString p
|| str == unitPackageNameString p
matchingId :: UnitId -> UnitInfo -> Bool
matchingId uid p = uid == unitId p
matching :: PackageArg -> UnitInfo -> Bool
matching (PackageArg str) = matchingStr str
matching (UnitIdArg (RealUnit (Definite uid))) = matchingId uid
matching (UnitIdArg _) = \_ -> False
sortByPreference :: UnitPrecedenceMap -> [UnitInfo] -> [UnitInfo]
sortByPreference prec_map = sortBy (flip (compareByPreference prec_map))
compareByPreference
:: UnitPrecedenceMap
-> UnitInfo
-> UnitInfo
-> Ordering
compareByPreference prec_map pkg pkg'
= case comparing unitPackageVersion pkg pkg' of
GT -> GT
EQ | Just prec <- Map.lookup (unitId pkg) prec_map
, Just prec' <- Map.lookup (unitId pkg') prec_map
-> compare prec prec'
| otherwise
-> EQ
LT -> LT
comparing :: Ord a => (t -> a) -> t -> t -> Ordering
comparing f a b = f a `compare` f b
pprFlag :: PackageFlag -> SDoc
pprFlag flag = case flag of
HidePackage p -> text "-hide-package " <> text p
ExposePackage doc _ _ -> text doc
pprTrustFlag :: TrustFlag -> SDoc
pprTrustFlag flag = case flag of
TrustPackage p -> text "-trust " <> text p
DistrustPackage p -> text "-distrust " <> text p
type WiringMap = Map UnitId UnitId
findWiredInUnits
:: (SDoc -> IO ())
-> UnitPrecedenceMap
-> [UnitInfo]
-> VisibilityMap
-> IO ([UnitInfo],
WiringMap)
findWiredInUnits printer prec_map pkgs vis_map = do
let
matches :: UnitInfo -> UnitId -> Bool
pc `matches` pid = unitPackageName pc == PackageName (unitIdFS pid)
findWiredInUnit :: [UnitInfo] -> UnitId -> IO (Maybe (UnitId, UnitInfo))
findWiredInUnit pkgs wired_pkg =
let all_ps = [ p | p <- pkgs, p `matches` wired_pkg ]
all_exposed_ps =
[ p | p <- all_ps
, Map.member (mkUnit p) vis_map ] in
case all_exposed_ps of
[] -> case all_ps of
[] -> notfound
many -> pick (head (sortByPreference prec_map many))
many -> pick (head (sortByPreference prec_map many))
where
notfound = do
printer $
text "wired-in package "
<> ftext (unitIdFS wired_pkg)
<> text " not found."
return Nothing
pick :: UnitInfo -> IO (Maybe (UnitId, UnitInfo))
pick pkg = do
printer $
text "wired-in package "
<> ftext (unitIdFS wired_pkg)
<> text " mapped to "
<> ppr (unitId pkg)
return (Just (wired_pkg, pkg))
mb_wired_in_pkgs <- mapM (findWiredInUnit pkgs) wiredInUnitIds
let
wired_in_pkgs = catMaybes mb_wired_in_pkgs
wiredInMap :: Map UnitId UnitId
wiredInMap = Map.fromList
[ (unitId realUnitInfo, wiredInUnitId)
| (wiredInUnitId, realUnitInfo) <- wired_in_pkgs
, not (unitIsIndefinite realUnitInfo)
]
updateWiredInDependencies pkgs = map (upd_deps . upd_pkg) pkgs
where upd_pkg pkg
| Just wiredInUnitId <- Map.lookup (unitId pkg) wiredInMap
= pkg { unitId = wiredInUnitId
, unitInstanceOf = fmap (const wiredInUnitId) (unitInstanceOf pkg)
}
| otherwise
= pkg
upd_deps pkg = pkg {
unitDepends = map (upd_wired_in wiredInMap) (unitDepends pkg),
unitExposedModules
= map (\(k,v) -> (k, fmap (upd_wired_in_mod wiredInMap) v))
(unitExposedModules pkg)
}
return (updateWiredInDependencies pkgs, wiredInMap)
upd_wired_in_mod :: WiringMap -> Module -> Module
upd_wired_in_mod wiredInMap (Module uid m) = Module (upd_wired_in_uid wiredInMap uid) m
upd_wired_in_uid :: WiringMap -> Unit -> Unit
upd_wired_in_uid wiredInMap u = case u of
HoleUnit -> HoleUnit
RealUnit (Definite uid) -> RealUnit (Definite (upd_wired_in wiredInMap uid))
VirtUnit indef_uid ->
VirtUnit $ mkInstantiatedUnit
(instUnitInstanceOf indef_uid)
(map (\(x,y) -> (x,upd_wired_in_mod wiredInMap y)) (instUnitInsts indef_uid))
upd_wired_in :: WiringMap -> UnitId -> UnitId
upd_wired_in wiredInMap key
| Just key' <- Map.lookup key wiredInMap = key'
| otherwise = key
updateVisibilityMap :: WiringMap -> VisibilityMap -> VisibilityMap
updateVisibilityMap wiredInMap vis_map = foldl' f vis_map (Map.toList wiredInMap)
where f vm (from, to) = case Map.lookup (RealUnit (Definite from)) vis_map of
Nothing -> vm
Just r -> Map.insert (RealUnit (Definite to)) r
(Map.delete (RealUnit (Definite from)) vm)
data UnusableUnitReason
=
IgnoredWithFlag
| BrokenDependencies [UnitId]
| CyclicDependencies [UnitId]
| IgnoredDependencies [UnitId]
| ShadowedDependencies [UnitId]
instance Outputable UnusableUnitReason where
ppr IgnoredWithFlag = text "[ignored with flag]"
ppr (BrokenDependencies uids) = brackets (text "broken" <+> ppr uids)
ppr (CyclicDependencies uids) = brackets (text "cyclic" <+> ppr uids)
ppr (IgnoredDependencies uids) = brackets (text "ignored" <+> ppr uids)
ppr (ShadowedDependencies uids) = brackets (text "shadowed" <+> ppr uids)
type UnusableUnits = Map UnitId (UnitInfo, UnusableUnitReason)
pprReason :: SDoc -> UnusableUnitReason -> SDoc
pprReason pref reason = case reason of
IgnoredWithFlag ->
pref <+> text "ignored due to an -ignore-package flag"
BrokenDependencies deps ->
pref <+> text "unusable due to missing dependencies:" $$
nest 2 (hsep (map ppr deps))
CyclicDependencies deps ->
pref <+> text "unusable due to cyclic dependencies:" $$
nest 2 (hsep (map ppr deps))
IgnoredDependencies deps ->
pref <+> text ("unusable because the -ignore-package flag was used to " ++
"ignore at least one of its dependencies:") $$
nest 2 (hsep (map ppr deps))
ShadowedDependencies deps ->
pref <+> text "unusable due to shadowed dependencies:" $$
nest 2 (hsep (map ppr deps))
reportCycles :: (SDoc -> IO ()) -> [SCC UnitInfo] -> IO ()
reportCycles printer sccs = mapM_ report sccs
where
report (AcyclicSCC _) = return ()
report (CyclicSCC vs) =
printer $
text "these packages are involved in a cycle:" $$
nest 2 (hsep (map (ppr . unitId) vs))
reportUnusable :: (SDoc -> IO ()) -> UnusableUnits -> IO ()
reportUnusable printer pkgs = mapM_ report (Map.toList pkgs)
where
report (ipid, (_, reason)) =
printer $
pprReason
(text "package" <+> ppr ipid <+> text "is") reason
type RevIndex = Map UnitId [UnitId]
reverseDeps :: UnitInfoMap -> RevIndex
reverseDeps db = Map.foldl' go Map.empty db
where
go r pkg = foldl' (go' (unitId pkg)) r (unitDepends pkg)
go' from r to = Map.insertWith (++) to [from] r
removeUnits :: [UnitId] -> RevIndex
-> UnitInfoMap
-> (UnitInfoMap, [UnitInfo])
removeUnits uids index m = go uids (m,[])
where
go [] (m,pkgs) = (m,pkgs)
go (uid:uids) (m,pkgs)
| Just pkg <- Map.lookup uid m
= case Map.lookup uid index of
Nothing -> go uids (Map.delete uid m, pkg:pkgs)
Just rdeps -> go (rdeps ++ uids) (Map.delete uid m, pkg:pkgs)
| otherwise
= go uids (m,pkgs)
depsNotAvailable :: UnitInfoMap
-> UnitInfo
-> [UnitId]
depsNotAvailable pkg_map pkg = filter (not . (`Map.member` pkg_map)) (unitDepends pkg)
depsAbiMismatch :: UnitInfoMap
-> UnitInfo
-> [UnitId]
depsAbiMismatch pkg_map pkg = map fst . filter (not . abiMatch) $ unitAbiDepends pkg
where
abiMatch (dep_uid, abi)
| Just dep_pkg <- Map.lookup dep_uid pkg_map
= unitAbiHash dep_pkg == abi
| otherwise
= False
ignoreUnits :: [IgnorePackageFlag] -> [UnitInfo] -> UnusableUnits
ignoreUnits flags pkgs = Map.fromList (concatMap doit flags)
where
doit (IgnorePackage str) =
case partition (matchingStr str) pkgs of
(ps, _) -> [ (unitId p, (p, IgnoredWithFlag))
| p <- ps ]
type UnitPrecedenceMap = Map UnitId Int
mergeDatabases :: (SDoc -> IO ()) -> [UnitDatabase UnitId]
-> IO (UnitInfoMap, UnitPrecedenceMap)
mergeDatabases printer = foldM merge (Map.empty, Map.empty) . zip [1..]
where
merge (pkg_map, prec_map) (i, UnitDatabase db_path db) = do
printer $
text "loading package database" <+> text db_path
forM_ (Set.toList override_set) $ \pkg ->
printer $
text "package" <+> ppr pkg <+>
text "overrides a previously defined package"
return (pkg_map', prec_map')
where
db_map = mk_pkg_map db
mk_pkg_map = Map.fromList . map (\p -> (unitId p, p))
override_set :: Set UnitId
override_set = Set.intersection (Map.keysSet db_map)
(Map.keysSet pkg_map)
pkg_map' :: UnitInfoMap
pkg_map' = Map.union db_map pkg_map
prec_map' :: UnitPrecedenceMap
prec_map' = Map.union (Map.map (const i) db_map) prec_map
validateDatabase :: UnitConfig -> UnitInfoMap
-> (UnitInfoMap, UnusableUnits, [SCC UnitInfo])
validateDatabase cfg pkg_map1 =
(pkg_map5, unusable, sccs)
where
ignore_flags = reverse (unitConfigFlagsIgnored cfg)
index = reverseDeps pkg_map1
mk_unusable mk_err dep_matcher m uids =
Map.fromList [ (unitId pkg, (pkg, mk_err (dep_matcher m pkg)))
| pkg <- uids ]
directly_broken = filter (not . null . depsNotAvailable pkg_map1)
(Map.elems pkg_map1)
(pkg_map2, broken) = removeUnits (map unitId directly_broken) index pkg_map1
unusable_broken = mk_unusable BrokenDependencies depsNotAvailable pkg_map2 broken
sccs = stronglyConnComp [ (pkg, unitId pkg, unitDepends pkg)
| pkg <- Map.elems pkg_map2 ]
getCyclicSCC (CyclicSCC vs) = map unitId vs
getCyclicSCC (AcyclicSCC _) = []
(pkg_map3, cyclic) = removeUnits (concatMap getCyclicSCC sccs) index pkg_map2
unusable_cyclic = mk_unusable CyclicDependencies depsNotAvailable pkg_map3 cyclic
directly_ignored = ignoreUnits ignore_flags (Map.elems pkg_map3)
(pkg_map4, ignored) = removeUnits (Map.keys directly_ignored) index pkg_map3
unusable_ignored = mk_unusable IgnoredDependencies depsNotAvailable pkg_map4 ignored
directly_shadowed = filter (not . null . depsAbiMismatch pkg_map4)
(Map.elems pkg_map4)
(pkg_map5, shadowed) = removeUnits (map unitId directly_shadowed) index pkg_map4
unusable_shadowed = mk_unusable ShadowedDependencies depsAbiMismatch pkg_map5 shadowed
unusable = directly_ignored `Map.union` unusable_ignored
`Map.union` unusable_broken
`Map.union` unusable_cyclic
`Map.union` unusable_shadowed
mkUnitState
:: SDocContext
-> (Int -> SDoc -> IO ())
-> UnitConfig
-> IO (UnitState,[UnitDatabase UnitId])
mkUnitState ctx printer cfg = do
raw_dbs <- case unitConfigDBCache cfg of
Nothing -> readUnitDatabases printer cfg
Just dbs -> return dbs
let distrust_all db = db { unitDatabaseUnits = distrustAllUnits (unitDatabaseUnits db) }
dbs | unitConfigDistrustAll cfg = map distrust_all raw_dbs
| otherwise = raw_dbs
let other_flags = reverse (unitConfigFlagsExposed cfg)
printer 2 $
text "package flags" <+> ppr other_flags
(pkg_map1, prec_map) <- mergeDatabases (printer 2) dbs
let (pkg_map2, unusable, sccs) = validateDatabase cfg pkg_map1
reportCycles (printer 2) sccs
reportUnusable (printer 2) unusable
pkgs1 <- mayThrowUnitErr
$ foldM (applyTrustFlag prec_map unusable)
(Map.elems pkg_map2) (reverse (unitConfigFlagsTrusted cfg))
let prelim_pkg_db = mkUnitInfoMap pkgs1
let preferLater unit unit' =
case compareByPreference prec_map unit unit' of
GT -> unit
_ -> unit'
addIfMorePreferable m unit = addToUDFM_C preferLater m (fsPackageName unit) unit
mostPreferablePackageReps = if unitConfigHideAll cfg
then emptyUDFM
else foldl' addIfMorePreferable emptyUDFM pkgs1
mostPreferable u =
case lookupUDFM mostPreferablePackageReps (fsPackageName u) of
Nothing -> False
Just u' -> compareByPreference prec_map u u' == EQ
vis_map1 = foldl' (\vm p ->
if unitIsExposed p && unitIsDefinite (mkUnit p) && mostPreferable p
then Map.insert (mkUnit p)
UnitVisibility {
uv_expose_all = True,
uv_renamings = [],
uv_package_name = First (Just (fsPackageName p)),
uv_requirements = Map.empty,
uv_explicit = False
}
vm
else vm)
Map.empty pkgs1
vis_map2 <- mayThrowUnitErr
$ foldM (applyPackageFlag prec_map prelim_pkg_db emptyUniqSet unusable
(unitConfigHideAll cfg) pkgs1)
vis_map1 other_flags
(pkgs2, wired_map) <- findWiredInUnits (printer 2) prec_map pkgs1 vis_map2
let pkg_db = mkUnitInfoMap pkgs2
let vis_map = updateVisibilityMap wired_map vis_map2
let hide_plugin_pkgs = unitConfigHideAllPlugins cfg
plugin_vis_map <-
case unitConfigFlagsPlugins cfg of
[] | not hide_plugin_pkgs -> return vis_map
| otherwise -> return Map.empty
_ -> do let plugin_vis_map1
| hide_plugin_pkgs = Map.empty
| otherwise = vis_map2
plugin_vis_map2
<- mayThrowUnitErr
$ foldM (applyPackageFlag prec_map prelim_pkg_db emptyUniqSet unusable
hide_plugin_pkgs pkgs1)
plugin_vis_map1
(reverse (unitConfigFlagsPlugins cfg))
return (updateVisibilityMap wired_map plugin_vis_map2)
let pkgname_map = listToUFM [ (unitPackageName p, unitInstanceOf p)
| p <- pkgs2
]
let explicit_pkgs = Map.keys vis_map
req_ctx = Map.map (Set.toList)
$ Map.unionsWith Set.union (map uv_requirements (Map.elems vis_map))
let preload1 = Map.keys (Map.filter uv_explicit vis_map)
basicLinkedUnits = fmap (RealUnit . Definite)
$ filter (flip Map.member pkg_db)
$ unitConfigAutoLink cfg
preload3 = ordNub $ (basicLinkedUnits ++ preload1)
dep_preload <- mayThrowUnitErr
$ closeUnitDeps pkg_db
$ zip (map toUnitId preload3) (repeat Nothing)
let mod_map1 = mkModuleNameProvidersMap ctx cfg pkg_db emptyUniqSet vis_map
mod_map2 = mkUnusableModuleNameProvidersMap unusable
mod_map = Map.union mod_map1 mod_map2
let !state = UnitState
{ preloadUnits = dep_preload
, explicitUnits = explicit_pkgs
, unitInfoMap = pkg_db
, preloadClosure = emptyUniqSet
, moduleNameProvidersMap = mod_map
, pluginModuleNameProvidersMap = mkModuleNameProvidersMap ctx cfg pkg_db emptyUniqSet plugin_vis_map
, packageNameMap = pkgname_map
, wireMap = wired_map
, unwireMap = Map.fromList [ (v,k) | (k,v) <- Map.toList wired_map ]
, requirementContext = req_ctx
, allowVirtualUnits = unitConfigAllowVirtual cfg
}
return (state, raw_dbs)
unwireUnit :: UnitState -> Unit -> Unit
unwireUnit state uid@(RealUnit (Definite def_uid)) =
maybe uid (RealUnit . Definite) (Map.lookup def_uid (unwireMap state))
unwireUnit _ uid = uid
mkModuleNameProvidersMap
:: SDocContext
-> UnitConfig
-> UnitInfoMap
-> PreloadUnitClosure
-> VisibilityMap
-> ModuleNameProvidersMap
mkModuleNameProvidersMap ctx cfg pkg_map closure vis_map =
Map.foldlWithKey extend_modmap emptyMap vis_map_extended
where
vis_map_extended = Map.union vis_map default_vis
default_vis = Map.fromList
[ (mkUnit pkg, mempty)
| pkg <- Map.elems pkg_map
, unitIsIndefinite pkg || null (unitInstantiations pkg)
]
emptyMap = Map.empty
setOrigins m os = fmap (const os) m
extend_modmap modmap uid
UnitVisibility { uv_expose_all = b, uv_renamings = rns }
= addListTo modmap theBindings
where
pkg = unit_lookup uid
theBindings :: [(ModuleName, Map Module ModuleOrigin)]
theBindings = newBindings b rns
newBindings :: Bool
-> [(ModuleName, ModuleName)]
-> [(ModuleName, Map Module ModuleOrigin)]
newBindings e rns = es e ++ hiddens ++ map rnBinding rns
rnBinding :: (ModuleName, ModuleName)
-> (ModuleName, Map Module ModuleOrigin)
rnBinding (orig, new) = (new, setOrigins origEntry fromFlag)
where origEntry = case lookupUFM esmap orig of
Just r -> r
Nothing -> throwGhcException (CmdLineError (renderWithContext ctx
(text "package flag: could not find module name" <+>
ppr orig <+> text "in package" <+> ppr pk)))
es :: Bool -> [(ModuleName, Map Module ModuleOrigin)]
es e = do
(m, exposedReexport) <- exposed_mods
let (pk', m', origin') =
case exposedReexport of
Nothing -> (pk, m, fromExposedModules e)
Just (Module pk' m') ->
let pkg' = unit_lookup pk'
in (pk', m', fromReexportedModules e pkg')
return (m, mkModMap pk' m' origin')
esmap :: UniqFM ModuleName (Map Module ModuleOrigin)
esmap = listToUFM (es False)
hiddens = [(m, mkModMap pk m ModHidden) | m <- hidden_mods]
pk = mkUnit pkg
unit_lookup uid = lookupUnit' (unitConfigAllowVirtual cfg) pkg_map closure uid
`orElse` pprPanic "unit_lookup" (ppr uid)
exposed_mods = unitExposedModules pkg
hidden_mods = unitHiddenModules pkg
mkUnusableModuleNameProvidersMap :: UnusableUnits -> ModuleNameProvidersMap
mkUnusableModuleNameProvidersMap unusables =
Map.foldl' extend_modmap Map.empty unusables
where
extend_modmap modmap (pkg, reason) = addListTo modmap bindings
where bindings :: [(ModuleName, Map Module ModuleOrigin)]
bindings = exposed ++ hidden
origin = ModUnusable reason
pkg_id = mkUnit pkg
exposed = map get_exposed exposed_mods
hidden = [(m, mkModMap pkg_id m origin) | m <- hidden_mods]
get_exposed (mod, Just mod') = (mod, Map.singleton mod' origin)
get_exposed (mod, _) = (mod, mkModMap pkg_id mod origin)
exposed_mods = unitExposedModules pkg
hidden_mods = unitHiddenModules pkg
addListTo :: (Monoid a, Ord k1, Ord k2)
=> Map k1 (Map k2 a)
-> [(k1, Map k2 a)]
-> Map k1 (Map k2 a)
addListTo = foldl' merge
where merge m (k, v) = MapStrict.insertWith (Map.unionWith mappend) k v m
mkModMap :: Unit -> ModuleName -> ModuleOrigin -> Map Module ModuleOrigin
mkModMap pkg mod = Map.singleton (mkModule pkg mod)
lookupModuleInAllUnits :: UnitState
-> ModuleName
-> [(Module, UnitInfo)]
lookupModuleInAllUnits pkgs m
= case lookupModuleWithSuggestions pkgs m Nothing of
LookupFound a b -> [(a,b)]
LookupMultiple rs -> map f rs
where f (m,_) = (m, expectJust "lookupModule" (lookupUnit pkgs
(moduleUnit m)))
_ -> []
data LookupResult =
LookupFound Module UnitInfo
| LookupMultiple [(Module, ModuleOrigin)]
| LookupHidden [(Module, ModuleOrigin)] [(Module, ModuleOrigin)]
| LookupUnusable [(Module, ModuleOrigin)]
| LookupNotFound [ModuleSuggestion]
data ModuleSuggestion = SuggestVisible ModuleName Module ModuleOrigin
| SuggestHidden ModuleName Module ModuleOrigin
lookupModuleWithSuggestions :: UnitState
-> ModuleName
-> Maybe FastString
-> LookupResult
lookupModuleWithSuggestions pkgs
= lookupModuleWithSuggestions' pkgs (moduleNameProvidersMap pkgs)
lookupPluginModuleWithSuggestions :: UnitState
-> ModuleName
-> Maybe FastString
-> LookupResult
lookupPluginModuleWithSuggestions pkgs
= lookupModuleWithSuggestions' pkgs (pluginModuleNameProvidersMap pkgs)
lookupModuleWithSuggestions' :: UnitState
-> ModuleNameProvidersMap
-> ModuleName
-> Maybe FastString
-> LookupResult
lookupModuleWithSuggestions' pkgs mod_map m mb_pn
= case Map.lookup m mod_map of
Nothing -> LookupNotFound suggestions
Just xs ->
case foldl' classify ([],[],[], []) (Map.toList xs) of
([], [], [], []) -> LookupNotFound suggestions
(_, _, _, [(m, _)]) -> LookupFound m (mod_unit m)
(_, _, _, exposed@(_:_)) -> LookupMultiple exposed
([], [], unusable@(_:_), []) -> LookupUnusable unusable
(hidden_pkg, hidden_mod, _, []) ->
LookupHidden hidden_pkg hidden_mod
where
classify (hidden_pkg, hidden_mod, unusable, exposed) (m, origin0) =
let origin = filterOrigin mb_pn (mod_unit m) origin0
x = (m, origin)
in case origin of
ModHidden
-> (hidden_pkg, x:hidden_mod, unusable, exposed)
ModUnusable _
-> (hidden_pkg, hidden_mod, x:unusable, exposed)
_ | originEmpty origin
-> (hidden_pkg, hidden_mod, unusable, exposed)
| originVisible origin
-> (hidden_pkg, hidden_mod, unusable, x:exposed)
| otherwise
-> (x:hidden_pkg, hidden_mod, unusable, exposed)
unit_lookup p = lookupUnit pkgs p `orElse` pprPanic "lookupModuleWithSuggestions" (ppr p <+> ppr m)
mod_unit = unit_lookup . moduleUnit
filterOrigin :: Maybe FastString
-> UnitInfo
-> ModuleOrigin
-> ModuleOrigin
filterOrigin Nothing _ o = o
filterOrigin (Just pn) pkg o =
case o of
ModHidden -> if go pkg then ModHidden else mempty
(ModUnusable _) -> if go pkg then o else mempty
ModOrigin { fromOrigUnit = e, fromExposedReexport = res,
fromHiddenReexport = rhs }
-> ModOrigin {
fromOrigUnit = if go pkg then e else Nothing
, fromExposedReexport = filter go res
, fromHiddenReexport = filter go rhs
, fromPackageFlag = False
}
where go pkg = pn == fsPackageName pkg
suggestions = fuzzyLookup (moduleNameString m) all_mods
all_mods :: [(String, ModuleSuggestion)]
all_mods = sortBy (comparing fst) $
[ (moduleNameString m, suggestion)
| (m, e) <- Map.toList (moduleNameProvidersMap pkgs)
, suggestion <- map (getSuggestion m) (Map.toList e)
]
getSuggestion name (mod, origin) =
(if originVisible origin then SuggestVisible else SuggestHidden)
name mod origin
listVisibleModuleNames :: UnitState -> [ModuleName]
listVisibleModuleNames state =
map fst (filter visible (Map.toList (moduleNameProvidersMap state)))
where visible (_, ms) = any originVisible (Map.elems ms)
closeUnitDeps :: UnitInfoMap -> [(UnitId,Maybe UnitId)] -> MaybeErr UnitErr [UnitId]
closeUnitDeps pkg_map ps = closeUnitDeps' pkg_map [] ps
closeUnitDeps' :: UnitInfoMap -> [UnitId] -> [(UnitId,Maybe UnitId)] -> MaybeErr UnitErr [UnitId]
closeUnitDeps' pkg_map current_ids ps = foldM (add_unit pkg_map) current_ids ps
add_unit :: UnitInfoMap
-> [UnitId]
-> (UnitId,Maybe UnitId)
-> MaybeErr UnitErr [UnitId]
add_unit pkg_map ps (p, mb_parent)
| p `elem` ps = return ps
| otherwise = case lookupUnitId' pkg_map p of
Nothing -> Failed (CloseUnitErr p mb_parent)
Just info -> do
ps' <- foldM add_unit_key ps (unitDepends info)
return (p : ps')
where
add_unit_key ps key
= add_unit pkg_map ps (key, Just p)
data UnitErr
= CloseUnitErr !UnitId !(Maybe UnitId)
| PackageFlagErr !PackageFlag ![(UnitInfo,UnusableUnitReason)]
| TrustFlagErr !TrustFlag ![(UnitInfo,UnusableUnitReason)]
mayThrowUnitErr :: MaybeErr UnitErr a -> IO a
mayThrowUnitErr = \case
Failed e -> throwGhcExceptionIO
$ CmdLineError
$ renderWithContext defaultSDocContext
$ withPprStyle defaultUserStyle
$ ppr e
Succeeded a -> return a
instance Outputable UnitErr where
ppr = \case
CloseUnitErr p mb_parent
-> (ftext (fsLit "unknown unit:") <+> ppr p)
<> case mb_parent of
Nothing -> Outputable.empty
Just parent -> space <> parens (text "dependency of"
<+> ftext (unitIdFS parent))
PackageFlagErr flag reasons
-> flag_err (pprFlag flag) reasons
TrustFlagErr flag reasons
-> flag_err (pprTrustFlag flag) reasons
where
flag_err flag_doc reasons =
text "cannot satisfy "
<> flag_doc
<> (if null reasons then Outputable.empty else text ": ")
$$ nest 4 (vcat (map ppr_reason reasons) $$
text "(use -v for more information)")
ppr_reason (p, reason) =
pprReason (ppr (unitId p) <+> text "is") reason
requirementMerges :: UnitState -> ModuleName -> [InstantiatedModule]
requirementMerges pkgstate mod_name =
fmap fixupModule $ fromMaybe [] (Map.lookup mod_name (requirementContext pkgstate))
where
fixupModule (Module iud name) = Module iud' name
where
iud' = iud { instUnitInstanceOf = cid' }
cid' = instUnitInstanceOf iud
pprUnitIdForUser :: UnitState -> UnitId -> SDoc
pprUnitIdForUser state uid@(UnitId fs) =
case lookupUnitPprInfo state uid of
Nothing -> ftext fs
Just i -> ppr i
pprUnitInfoForUser :: UnitInfo -> SDoc
pprUnitInfoForUser info = ppr (mkUnitPprInfo unitIdFS info)
lookupUnitPprInfo :: UnitState -> UnitId -> Maybe UnitPprInfo
lookupUnitPprInfo state uid = fmap (mkUnitPprInfo unitIdFS) (lookupUnitId state uid)
pprUnits :: UnitState -> SDoc
pprUnits = pprUnitsWith pprUnitInfo
pprUnitsWith :: (UnitInfo -> SDoc) -> UnitState -> SDoc
pprUnitsWith pprIPI pkgstate =
vcat (intersperse (text "---") (map pprIPI (listUnitInfo pkgstate)))
pprUnitsSimple :: UnitState -> SDoc
pprUnitsSimple = pprUnitsWith pprIPI
where pprIPI ipi = let i = unitIdFS (unitId ipi)
e = if unitIsExposed ipi then text "E" else text " "
t = if unitIsTrusted ipi then text "T" else text " "
in e <> t <> text " " <> ftext i
pprModuleMap :: ModuleNameProvidersMap -> SDoc
pprModuleMap mod_map =
vcat (map pprLine (Map.toList mod_map))
where
pprLine (m,e) = ppr m $$ nest 50 (vcat (map (pprEntry m) (Map.toList e)))
pprEntry :: Outputable a => ModuleName -> (Module, a) -> SDoc
pprEntry m (m',o)
| m == moduleName m' = ppr (moduleUnit m') <+> parens (ppr o)
| otherwise = ppr m' <+> parens (ppr o)
fsPackageName :: UnitInfo -> FastString
fsPackageName info = fs
where
PackageName fs = unitPackageName info
improveUnit :: UnitState -> Unit -> Unit
improveUnit state u = improveUnit' (unitInfoMap state) (preloadClosure state) u
improveUnit' :: UnitInfoMap -> PreloadUnitClosure -> Unit -> Unit
improveUnit' _ _ uid@(RealUnit _) = uid
improveUnit' pkg_map closure uid =
case lookupUnit' False pkg_map closure uid of
Nothing -> uid
Just pkg ->
if unitId pkg `elementOfUniqSet` closure
then mkUnit pkg
else uid
instUnitToUnit :: UnitState -> InstantiatedUnit -> Unit
instUnitToUnit state iuid =
improveUnit state $ VirtUnit iuid
type ShHoleSubst = ModuleNameEnv Module
renameHoleModule :: UnitState -> ShHoleSubst -> Module -> Module
renameHoleModule state = renameHoleModule' (unitInfoMap state) (preloadClosure state)
renameHoleUnit :: UnitState -> ShHoleSubst -> Unit -> Unit
renameHoleUnit state = renameHoleUnit' (unitInfoMap state) (preloadClosure state)
renameHoleModule' :: UnitInfoMap -> PreloadUnitClosure -> ShHoleSubst -> Module -> Module
renameHoleModule' pkg_map closure env m
| not (isHoleModule m) =
let uid = renameHoleUnit' pkg_map closure env (moduleUnit m)
in mkModule uid (moduleName m)
| Just m' <- lookupUFM env (moduleName m) = m'
| otherwise = m
renameHoleUnit' :: UnitInfoMap -> PreloadUnitClosure -> ShHoleSubst -> Unit -> Unit
renameHoleUnit' pkg_map closure env uid =
case uid of
(VirtUnit
InstantiatedUnit{ instUnitInstanceOf = cid
, instUnitInsts = insts
, instUnitHoles = fh })
-> if isNullUFM (intersectUFM_C const (udfmToUfm (getUniqDSet fh)) env)
then uid
else improveUnit' pkg_map closure $
mkVirtUnit cid
(map (\(k,v) -> (k, renameHoleModule' pkg_map closure env v)) insts)
_ -> uid
instModuleToModule :: UnitState -> InstantiatedModule -> Module
instModuleToModule pkgstate (Module iuid mod_name) =
mkModule (instUnitToUnit pkgstate iuid) mod_name
pprWithUnitState :: UnitState -> SDoc -> SDoc
pprWithUnitState state = updSDocContext (\ctx -> ctx
{ sdocUnitIdForUser = \fs -> pprUnitIdForUser state (UnitId fs)
})