module GHC.Unit.State (
module GHC.Unit.Info,
UnitState(..),
UnitDatabase (..),
emptyUnitState,
initUnits,
readUnitDatabases,
readUnitDatabase,
getUnitDbRefs,
resolveUnitDatabase,
listUnitInfo,
lookupUnit,
lookupUnit',
unsafeLookupUnit,
lookupUnitId,
lookupUnitId',
unsafeLookupUnitId,
lookupPackageName,
improveUnit,
searchPackageId,
displayUnitId,
listVisibleModuleNames,
lookupModuleInAllUnits,
lookupModuleWithSuggestions,
lookupPluginModuleWithSuggestions,
LookupResult(..),
ModuleSuggestion(..),
ModuleOrigin(..),
UnusableUnitReason(..),
pprReason,
getUnitIncludePath,
getUnitLibraryPath,
getUnitLinkOpts,
getUnitExtraCcOpts,
getUnitFrameworkPath,
getUnitFrameworks,
getPreloadUnitsAnd,
collectArchives,
collectIncludeDirs, collectLibraryPaths, collectLinkOpts,
packageHsLibs, getLibs,
ShHoleSubst,
renameHoleUnit,
renameHoleModule,
renameHoleUnit',
renameHoleModule',
instUnitToUnit,
instModuleToModule,
mkIndefUnitId,
updateIndefUnitId,
unwireUnit,
pprFlag,
pprUnits,
pprUnitsSimple,
pprModuleMap,
homeUnitIsIndefinite,
homeUnitIsDefinite,
)
where
#include "HsVersions.h"
import GHC.Prelude
import GHC.Platform
import GHC.Unit.Database
import GHC.Unit.Info
import GHC.Unit.Types
import GHC.Unit.Module
import GHC.Driver.Session
import GHC.Driver.Ways
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 GHC.Utils.Error ( debugTraceMsg, MsgDoc, dumpIfSet_dyn,
withTiming, DumpFormat (..) )
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 as List
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
ModOrigin e res rhs f <> 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 = panic "ModOrigin: package both exposed/hidden"
g Nothing x = x
g x Nothing = x
_x <> _y = panic "ModOrigin: hidden module redefined"
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 :: !PlatformMini
, unitConfigWays :: !(Set Way)
, unitConfigProgramName :: !String
, unitConfigGlobalDB :: !FilePath
, unitConfigGHCDir :: !FilePath
, unitConfigDBName :: !String
, unitConfigAutoLink :: ![UnitId]
, unitConfigDistrustAll :: !Bool
, unitConfigHideAll :: !Bool
, unitConfigHideAllPlugins :: !Bool
, unitConfigAllowVirtualUnits :: !Bool
, unitConfigDBCache :: Maybe [UnitDatabase UnitId]
, unitConfigFlagsDB :: [PackageDBFlag]
, unitConfigFlagsExposed :: [PackageFlag]
, unitConfigFlagsIgnored :: [IgnorePackageFlag]
, unitConfigFlagsTrusted :: [TrustFlag]
, unitConfigFlagsPlugins :: [PackageFlag]
}
initUnitConfig :: DynFlags -> UnitConfig
initUnitConfig dflags =
let autoLink
| not (gopt Opt_AutoLinkPackages dflags) = []
| otherwise = filter (/= homeUnitId dflags) [baseUnitId, rtsUnitId]
in UnitConfig
{ unitConfigPlatformArchOs = platformMini (targetPlatform dflags)
, unitConfigProgramName = programName dflags
, unitConfigWays = ways dflags
, 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
, unitConfigAllowVirtualUnits = homeUnitIsIndefinite dflags
, unitConfigDBCache = unitDatabases dflags
, 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 :: Map 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 = Map.empty,
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 = Map.lookup n (packageNameMap pkgstate)
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 :: DynFlags -> IO DynFlags
initUnits dflags = do
let forceUnitInfoMap (state, _) = unitInfoMap state `seq` ()
let ctx = initSDocContext dflags defaultUserStyle
let printer = debugTraceMsg dflags
(state,dbs) <- withTiming dflags (text "initializing unit database")
forceUnitInfoMap
(mkUnitState ctx printer (initUnitConfig dflags))
dumpIfSet_dyn (dflags { pprCols = 200 }) Opt_D_dump_mod_map "Module Map"
FormatText (pprModuleMap (moduleNameProvidersMap state))
let dflags' = dflags
{ unitDatabases = Just dbs
, unitState = state
}
dflags'' = upd_wired_in_home_instantiations dflags'
return dflags''
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) unitIdFS . 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 do
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 top_dir pkgroot
mungeDynLibFields :: UnitInfo -> UnitInfo
mungeDynLibFields pkg =
pkg {
unitLibraryDynDirs = case unitLibraryDynDirs pkg of
[] -> unitLibraryDirs pkg
ds -> ds
}
applyTrustFlag
:: SDocContext
-> UnitPrecedenceMap
-> UnusableUnits
-> [UnitInfo]
-> TrustFlag
-> IO [UnitInfo]
applyTrustFlag ctx prec_map unusable pkgs flag =
case flag of
TrustPackage str ->
case selectPackages prec_map (PackageArg str) pkgs unusable of
Left ps -> trustFlagErr ctx flag ps
Right (ps,qs) -> return (map trust ps ++ qs)
where trust p = p {unitIsTrusted=True}
DistrustPackage str ->
case selectPackages prec_map (PackageArg str) pkgs unusable of
Left ps -> trustFlagErr ctx flag ps
Right (ps,qs) -> return (distrustAllUnits ps ++ qs)
homeUnitIsIndefinite :: DynFlags -> Bool
homeUnitIsIndefinite dflags = not (homeUnitIsDefinite dflags)
homeUnitIsDefinite :: DynFlags -> Bool
homeUnitIsDefinite dflags = unitIsDefinite (homeUnit dflags)
applyPackageFlag
:: SDocContext
-> UnitPrecedenceMap
-> UnitInfoMap
-> PreloadUnitClosure
-> UnusableUnits
-> Bool
-> [UnitInfo]
-> VisibilityMap
-> PackageFlag
-> IO VisibilityMap
applyPackageFlag ctx 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 -> packageFlagErr ctx flag ps
Right (p:_) -> return 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 -> packageFlagErr ctx flag ps
Right ps -> return vm'
where vm' = 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 str == unitPackageIdString p || str == unitPackageNameString 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
packageFlagErr :: SDocContext
-> PackageFlag
-> [(UnitInfo, UnusableUnitReason)]
-> IO a
packageFlagErr ctx flag reasons
= packageFlagErr' ctx (pprFlag flag) reasons
trustFlagErr :: SDocContext
-> TrustFlag
-> [(UnitInfo, UnusableUnitReason)]
-> IO a
trustFlagErr ctx flag reasons
= packageFlagErr' ctx (pprTrustFlag flag) reasons
packageFlagErr' :: SDocContext
-> SDoc
-> [(UnitInfo, UnusableUnitReason)]
-> IO a
packageFlagErr' ctx flag_doc reasons
= throwGhcExceptionIO (CmdLineError (renderWithStyle ctx $ err))
where err = text "cannot satisfy " <> flag_doc <>
(if null reasons then Outputable.empty else text ": ") $$
nest 4 (ppr_reasons $$
text "(use -v for more information)")
ppr_reasons = vcat (map ppr_reason reasons)
ppr_reason (p, reason) =
pprReason (ppr (unitId p) <+> text "is") reason
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_home_instantiations :: DynFlags -> DynFlags
upd_wired_in_home_instantiations dflags = dflags { homeUnitInstantiations = wiredInsts }
where
state = unitState dflags
wiringMap = wireMap state
unwiredInsts = homeUnitInstantiations dflags
wiredInsts = map (fmap (upd_wired_in_mod wiringMap)) unwiredInsts
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 <- foldM (applyTrustFlag ctx 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 <- foldM (applyPackageFlag ctx 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
<- foldM (applyPackageFlag ctx 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 = foldl' add Map.empty pkgs2
where add pn_map p
= Map.insert (unitPackageName p) (unitInstanceOf p) pn_map
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)
let dep_preload_err = closeUnitDeps pkg_db (zip (map toUnitId preload3) (repeat Nothing))
dep_preload <- throwErr ctx dep_preload_err
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 = unitConfigAllowVirtualUnits 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 (renderWithStyle 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' (unitConfigAllowVirtualUnits 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)
getUnitIncludePath :: DynFlags -> [UnitId] -> IO [String]
getUnitIncludePath dflags pkgs =
collectIncludeDirs `fmap` getPreloadUnitsAnd dflags pkgs
collectIncludeDirs :: [UnitInfo] -> [FilePath]
collectIncludeDirs ps = ordNub (filter notNull (concatMap unitIncludeDirs ps))
getUnitLibraryPath :: DynFlags -> [UnitId] -> IO [String]
getUnitLibraryPath dflags pkgs =
collectLibraryPaths dflags `fmap` getPreloadUnitsAnd dflags pkgs
collectLibraryPaths :: DynFlags -> [UnitInfo] -> [FilePath]
collectLibraryPaths dflags = ordNub . filter notNull
. concatMap (libraryDirsForWay dflags)
getUnitLinkOpts :: DynFlags -> [UnitId] -> IO ([String], [String], [String])
getUnitLinkOpts dflags pkgs =
collectLinkOpts dflags `fmap` getPreloadUnitsAnd dflags pkgs
collectLinkOpts :: DynFlags -> [UnitInfo] -> ([String], [String], [String])
collectLinkOpts dflags ps =
(
concatMap (map ("-l" ++) . packageHsLibs dflags) ps,
concatMap (map ("-l" ++) . unitExtDepLibsSys) ps,
concatMap unitLinkerOptions ps
)
collectArchives :: DynFlags -> UnitInfo -> IO [FilePath]
collectArchives dflags pc =
filterM doesFileExist [ searchPath </> ("lib" ++ lib ++ ".a")
| searchPath <- searchPaths
, lib <- libs ]
where searchPaths = ordNub . filter notNull . libraryDirsForWay dflags $ pc
libs = packageHsLibs dflags pc ++ unitExtDepLibsSys pc
getLibs :: DynFlags -> [UnitId] -> IO [(String,String)]
getLibs dflags pkgs = do
ps <- getPreloadUnitsAnd dflags pkgs
fmap concat . forM ps $ \p -> do
let candidates = [ (l </> f, f) | l <- collectLibraryPaths dflags [p]
, f <- (\n -> "lib" ++ n ++ ".a") <$> packageHsLibs dflags p ]
filterM (doesFileExist . fst) candidates
packageHsLibs :: DynFlags -> UnitInfo -> [String]
packageHsLibs dflags p = map (mkDynName . addSuffix) (unitLibraries p)
where
ways0 = ways dflags
ways1 = Set.filter (/= WayDyn) ways0
ways2 | WayDebug `Set.member` ways1 || WayProf `Set.member` ways1
= Set.filter (/= WayEventLog) ways1
| otherwise
= ways1
tag = waysTag (Set.filter (not . wayRTSOnly) ways2)
rts_tag = waysTag ways2
mkDynName x
| WayDyn `Set.notMember` ways dflags = x
| "HS" `isPrefixOf` x =
x ++ '-':programName dflags ++ projectVersion dflags
| Just x' <- stripPrefix "C" x = x'
| otherwise
= panic ("Don't understand library name " ++ x)
addSuffix rts@"HSrts" = rts ++ (expandTag rts_tag)
addSuffix rts@"HSrts-1.0"= rts ++ (expandTag rts_tag)
addSuffix other_lib = other_lib ++ (expandTag tag)
expandTag t | null t = ""
| otherwise = '_':t
libraryDirsForWay :: DynFlags -> UnitInfo -> [String]
libraryDirsForWay dflags
| WayDyn `elem` ways dflags = unitLibraryDynDirs
| otherwise = unitLibraryDirs
getUnitExtraCcOpts :: DynFlags -> [UnitId] -> IO [String]
getUnitExtraCcOpts dflags pkgs = do
ps <- getPreloadUnitsAnd dflags pkgs
return (concatMap unitCcOptions ps)
getUnitFrameworkPath :: DynFlags -> [UnitId] -> IO [String]
getUnitFrameworkPath dflags pkgs = do
ps <- getPreloadUnitsAnd dflags pkgs
return (ordNub (filter notNull (concatMap unitExtDepFrameworkDirs ps)))
getUnitFrameworks :: DynFlags -> [UnitId] -> IO [String]
getUnitFrameworks dflags pkgs = do
ps <- getPreloadUnitsAnd dflags pkgs
return (concatMap unitExtDepFrameworks ps)
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)
getPreloadUnitsAnd :: DynFlags -> [UnitId] -> IO [UnitInfo]
getPreloadUnitsAnd dflags ids0 =
let
ids = ids0 ++
if homeUnitIsIndefinite dflags
then []
else map (toUnitId . moduleUnit . snd)
(homeUnitInstantiations dflags)
state = unitState dflags
pkg_map = unitInfoMap state
preload = preloadUnits state
ctx = initSDocContext dflags defaultUserStyle
in do
all_pkgs <- throwErr ctx (closeUnitDeps' pkg_map preload (ids `zip` repeat Nothing))
return (map (unsafeLookupUnitId state) all_pkgs)
throwErr :: SDocContext -> MaybeErr MsgDoc a -> IO a
throwErr ctx m = case m of
Failed e -> throwGhcExceptionIO (CmdLineError (renderWithStyle ctx e))
Succeeded r -> return r
closeUnitDeps :: UnitInfoMap -> [(UnitId,Maybe UnitId)] -> MaybeErr MsgDoc [UnitId]
closeUnitDeps pkg_map ps = closeUnitDeps' pkg_map [] ps
closeUnitDeps' :: UnitInfoMap -> [UnitId] -> [(UnitId,Maybe UnitId)] -> MaybeErr MsgDoc [UnitId]
closeUnitDeps' pkg_map current_ids ps = foldM (add_unit pkg_map) current_ids ps
add_unit :: UnitInfoMap
-> [UnitId]
-> (UnitId,Maybe UnitId)
-> MaybeErr MsgDoc [UnitId]
add_unit pkg_map ps (p, mb_parent)
| p `elem` ps = return ps
| otherwise = case lookupUnitId' pkg_map p of
Nothing -> Failed $
(ftext (fsLit "unknown package:") <+> ppr p)
<> case mb_parent of
Nothing -> Outputable.empty
Just parent -> space <> parens (text "dependency of"
<+> ftext (unitIdFS 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)
mkIndefUnitId :: UnitState -> FastString -> IndefUnitId
mkIndefUnitId pkgstate raw =
let uid = UnitId raw
in case lookupUnitId pkgstate uid of
Nothing -> Indefinite uid Nothing
Just c -> Indefinite uid $ Just $ mkUnitPprInfo c
updateIndefUnitId :: UnitState -> IndefUnitId -> IndefUnitId
updateIndefUnitId pkgstate uid = mkIndefUnitId pkgstate (unitIdFS (indefUnit uid))
displayUnitId :: UnitState -> UnitId -> Maybe String
displayUnitId pkgstate uid =
fmap unitPackageIdString (lookupUnitId pkgstate 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