{-# LANGUAGE CPP, ScopedTypeVariables, BangPatterns, FlexibleContexts #-}
module Packages (
module PackageConfig,
PackageState(preloadPackages, explicitPackages, moduleToPkgConfAll, requirementContext),
PackageConfigMap,
emptyPackageState,
initPackages,
readPackageConfigs,
getPackageConfRefs,
resolvePackageConfig,
readPackageConfig,
listPackageConfigMap,
lookupPackage,
lookupPackage',
lookupInstalledPackage,
lookupPackageName,
improveUnitId,
searchPackageId,
getPackageDetails,
getInstalledPackageDetails,
componentIdString,
displayInstalledUnitId,
listVisibleModuleNames,
lookupModuleInAllPackages,
lookupModuleWithSuggestions,
lookupPluginModuleWithSuggestions,
LookupResult(..),
ModuleSuggestion(..),
ModuleOrigin(..),
UnusablePackageReason(..),
pprReason,
getPackageIncludePath,
getPackageLibraryPath,
getPackageLinkOpts,
getPackageExtraCcOpts,
getPackageFrameworkPath,
getPackageFrameworks,
getPackageConfigMap,
getPreloadPackagesAnd,
collectArchives,
collectIncludeDirs, collectLibraryPaths, collectLinkOpts,
packageHsLibs, getLibs,
unwireUnitId,
pprFlag,
pprPackages,
pprPackagesSimple,
pprModuleMap,
isIndefinite,
isDllName
)
where
#include "GhclibHsVersions.h"
import GhcPrelude
import GHC.PackageDb
import PackageConfig
import DynFlags
import Name ( Name, nameModule_maybe )
import UniqFM
import UniqDFM
import UniqSet
import Module
import Util
import Panic
import GHC.Platform
import Outputable
import Maybes
import CmdLineParser
import System.Environment ( getEnv )
import FastString
import ErrUtils ( debugTraceMsg, MsgDoc, dumpIfSet_dyn, compilationProgressMsg,
withTiming )
import Exception
import System.Directory
import System.FilePath as FilePath
import qualified System.FilePath.Posix as FilePath.Posix
import System.IO.Error ( isDoesNotExistError )
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
import Data.Version
data ModuleOrigin =
ModHidden
| ModUnusable UnusablePackageReason
| ModOrigin {
fromOrigPackage :: Maybe Bool
, fromExposedReexport :: [PackageConfig]
, fromHiddenReexport :: [PackageConfig]
, 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 . packageConfigId) res)]) ++
(if null rhs
then []
else [text "hidden reexport by" <+>
sep (map (ppr . packageConfigId) res)]) ++
(if f then [text "package flag"] else [])
))
fromExposedModules :: Bool -> ModuleOrigin
fromExposedModules e = ModOrigin (Just e) [] [] False
fromReexportedModules :: Bool -> PackageConfig -> 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 InstalledUnitIdMap = UniqDFM
data PackageConfigMap = PackageConfigMap {
unPackageConfigMap :: InstalledUnitIdMap PackageConfig,
preloadClosure :: UniqSet InstalledUnitId
}
type VisibilityMap = Map UnitId UnitVisibility
data UnitVisibility = UnitVisibility
{ uv_expose_all :: Bool
, uv_renamings :: [(ModuleName, ModuleName)]
, uv_package_name :: First FastString
, uv_requirements :: Map ModuleName (Set IndefModule)
, 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.<>)
type WiredUnitId = DefUnitId
type PreloadUnitId = InstalledUnitId
type ModuleToPkgConfAll =
Map ModuleName (Map Module ModuleOrigin)
data PackageState = PackageState {
pkgIdMap :: PackageConfigMap,
packageNameMap :: Map PackageName ComponentId,
unwireMap :: Map WiredUnitId WiredUnitId,
preloadPackages :: [PreloadUnitId],
explicitPackages :: [UnitId],
moduleToPkgConfAll :: !ModuleToPkgConfAll,
pluginModuleToPkgConfAll :: !ModuleToPkgConfAll,
requirementContext :: Map ModuleName [IndefModule]
}
emptyPackageState :: PackageState
emptyPackageState = PackageState {
pkgIdMap = emptyPackageConfigMap,
packageNameMap = Map.empty,
unwireMap = Map.empty,
preloadPackages = [],
explicitPackages = [],
moduleToPkgConfAll = Map.empty,
pluginModuleToPkgConfAll = Map.empty,
requirementContext = Map.empty
}
type InstalledPackageIndex = Map InstalledUnitId PackageConfig
emptyPackageConfigMap :: PackageConfigMap
emptyPackageConfigMap = PackageConfigMap emptyUDFM emptyUniqSet
lookupPackage :: DynFlags -> UnitId -> Maybe PackageConfig
lookupPackage dflags = lookupPackage' (isIndefinite dflags) (pkgIdMap (pkgState dflags))
lookupPackage' :: Bool -> PackageConfigMap -> UnitId -> Maybe PackageConfig
lookupPackage' False (PackageConfigMap pkg_map _) uid = lookupUDFM pkg_map uid
lookupPackage' True m@(PackageConfigMap pkg_map _) uid =
case splitUnitIdInsts uid of
(iuid, Just indef) ->
fmap (renamePackage m (indefUnitIdInsts indef))
(lookupUDFM pkg_map iuid)
(_, Nothing) -> lookupUDFM pkg_map uid
lookupPackageName :: DynFlags -> PackageName -> Maybe ComponentId
lookupPackageName dflags n = Map.lookup n (packageNameMap (pkgState dflags))
searchPackageId :: DynFlags -> SourcePackageId -> [PackageConfig]
searchPackageId dflags pid = filter ((pid ==) . sourcePackageId)
(listPackageConfigMap dflags)
extendPackageConfigMap
:: PackageConfigMap -> [PackageConfig] -> PackageConfigMap
extendPackageConfigMap (PackageConfigMap pkg_map closure) new_pkgs
= PackageConfigMap (foldl' add pkg_map new_pkgs) closure
where add pkg_map p = addToUDFM (addToUDFM pkg_map (expandedPackageConfigId p) p)
(installedPackageConfigId p) p
getPackageDetails :: DynFlags -> UnitId -> PackageConfig
getPackageDetails dflags pid =
expectJust "getPackageDetails" (lookupPackage dflags pid)
lookupInstalledPackage :: DynFlags -> InstalledUnitId -> Maybe PackageConfig
lookupInstalledPackage dflags uid = lookupInstalledPackage' (pkgIdMap (pkgState dflags)) uid
lookupInstalledPackage' :: PackageConfigMap -> InstalledUnitId -> Maybe PackageConfig
lookupInstalledPackage' (PackageConfigMap db _) uid = lookupUDFM db uid
getInstalledPackageDetails :: DynFlags -> InstalledUnitId -> PackageConfig
getInstalledPackageDetails dflags uid =
expectJust "getInstalledPackageDetails" $
lookupInstalledPackage dflags uid
listPackageConfigMap :: DynFlags -> [PackageConfig]
listPackageConfigMap dflags = eltsUDFM pkg_map
where
PackageConfigMap pkg_map _ = pkgIdMap (pkgState dflags)
initPackages :: DynFlags -> IO (DynFlags, [PreloadUnitId])
initPackages dflags0 = withTiming dflags0
(text "initializing package database")
forcePkgDb $ do
dflags <- interpretPackageEnv dflags0
pkg_db <-
case pkgDatabase dflags of
Nothing -> readPackageConfigs dflags
Just db -> return $ map (\(p, pkgs)
-> (p, setBatchPackageFlags dflags pkgs)) db
(pkg_state, preload, insts)
<- mkPackageState dflags pkg_db []
return (dflags{ pkgDatabase = Just pkg_db,
pkgState = pkg_state,
thisUnitIdInsts_ = insts },
preload)
where
forcePkgDb (dflags, _) = pkgIdMap (pkgState dflags) `seq` ()
readPackageConfigs :: DynFlags -> IO [(FilePath, [PackageConfig])]
readPackageConfigs dflags = do
conf_refs <- getPackageConfRefs dflags
confs <- liftM catMaybes $ mapM (resolvePackageConfig dflags) conf_refs
mapM (readPackageConfig dflags) confs
getPackageConfRefs :: DynFlags -> IO [PkgConfRef]
getPackageConfRefs dflags = do
let system_conf_refs = [UserPkgConf, GlobalPkgConf]
e_pkg_path <- tryIO (getEnv $ map toUpper (programName dflags) ++ "_PACKAGE_PATH")
let base_conf_refs = case e_pkg_path of
Left _ -> system_conf_refs
Right path
| not (null path) && isSearchPathSeparator (last path)
-> map PkgConfFile (splitSearchPath (init path)) ++ system_conf_refs
| otherwise
-> map PkgConfFile (splitSearchPath path)
return $ reverse (foldr doFlag base_conf_refs (packageDBFlags dflags))
where
doFlag (PackageDB p) dbs = p : dbs
doFlag NoUserPackageDB dbs = filter isNotUser dbs
doFlag NoGlobalPackageDB dbs = filter isNotGlobal dbs
doFlag ClearPackageDBs _ = []
isNotUser UserPkgConf = False
isNotUser _ = True
isNotGlobal GlobalPkgConf = False
isNotGlobal _ = True
resolvePackageConfig :: DynFlags -> PkgConfRef -> IO (Maybe FilePath)
resolvePackageConfig dflags GlobalPkgConf = return $ Just (systemPackageConfig dflags)
resolvePackageConfig dflags UserPkgConf = runMaybeT $ do
dir <- versionedAppDir dflags
let pkgconf = dir </> "package.conf.d"
exist <- tryMaybeT $ doesDirectoryExist pkgconf
if exist then return pkgconf else mzero
resolvePackageConfig _ (PkgConfFile name) = return $ Just name
readPackageConfig :: DynFlags -> FilePath -> IO (FilePath, [PackageConfig])
readPackageConfig dflags conf_file = do
isdir <- doesDirectoryExist conf_file
proto_pkg_configs <-
if isdir
then readDirStylePackageConfig conf_file
else do
isfile <- doesFileExist conf_file
if isfile
then do
mpkgs <- tryReadOldFileStylePackageConfig
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 = topDir dflags
pkgroot = takeDirectory conf_file'
pkg_configs1 = map (mungePackageConfig top_dir pkgroot)
proto_pkg_configs
pkg_configs2 = setBatchPackageFlags dflags pkg_configs1
return (conf_file', pkg_configs2)
where
readDirStylePackageConfig conf_dir = do
let filename = conf_dir </> "package.cache"
cache_exists <- doesFileExist filename
if cache_exists
then do
debugTraceMsg dflags 2 $ text "Using binary package database:"
<+> text filename
readPackageDbForGhc filename
else do
debugTraceMsg dflags 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
debugTraceMsg dflags 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"
tryReadOldFileStylePackageConfig = 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 debugTraceMsg dflags 2 (text "Ignoring old file-style db and trying:" <+> text conf_dir)
liftM Just (readDirStylePackageConfig conf_dir)
else return (Just [])
else return Nothing
setBatchPackageFlags :: DynFlags -> [PackageConfig] -> [PackageConfig]
setBatchPackageFlags dflags pkgs = maybeDistrustAll pkgs
where
maybeDistrustAll pkgs'
| gopt Opt_DistrustAllPackages dflags = map distrust pkgs'
| otherwise = pkgs'
distrust pkg = pkg{ trusted = False }
mungePackageConfig :: FilePath -> FilePath
-> PackageConfig -> PackageConfig
mungePackageConfig top_dir pkgroot =
mungeDynLibFields
. mungePackagePaths top_dir pkgroot
mungeDynLibFields :: PackageConfig -> PackageConfig
mungeDynLibFields pkg =
pkg {
libraryDynDirs = libraryDynDirs pkg
`orIfNull` libraryDirs pkg
}
where
orIfNull [] flags = flags
orIfNull flags _ = flags
mungePackagePaths :: FilePath -> FilePath -> PackageConfig -> PackageConfig
mungePackagePaths top_dir pkgroot pkg =
pkg {
importDirs = munge_paths (importDirs pkg),
includeDirs = munge_paths (includeDirs pkg),
libraryDirs = munge_paths (libraryDirs pkg),
libraryDynDirs = munge_paths (libraryDynDirs pkg),
frameworkDirs = munge_paths (frameworkDirs pkg),
haddockInterfaces = munge_paths (haddockInterfaces pkg),
haddockHTMLs = munge_urls (haddockHTMLs pkg)
}
where
munge_paths = map munge_path
munge_urls = map munge_url
munge_path p
| Just p' <- stripVarPrefix "${pkgroot}" p = pkgroot ++ p'
| Just p' <- stripVarPrefix "$topdir" p = top_dir ++ p'
| otherwise = p
munge_url p
| Just p' <- stripVarPrefix "${pkgrooturl}" p = toUrlPath pkgroot p'
| Just p' <- stripVarPrefix "$httptopdir" p = toUrlPath top_dir p'
| otherwise = p
toUrlPath r p = "file:///"
++ FilePath.Posix.joinPath
(r :
dropWhile (all isPathSeparator)
(FilePath.splitDirectories p))
stripVarPrefix var path = case stripPrefix var path of
Just [] -> Just []
Just cs@(c : _) | isPathSeparator c -> Just cs
_ -> Nothing
applyTrustFlag
:: DynFlags
-> PackagePrecedenceIndex
-> UnusablePackages
-> [PackageConfig]
-> TrustFlag
-> IO [PackageConfig]
applyTrustFlag dflags prec_map unusable pkgs flag =
case flag of
TrustPackage str ->
case selectPackages prec_map (PackageArg str) pkgs unusable of
Left ps -> trustFlagErr dflags flag ps
Right (ps,qs) -> return (map trust ps ++ qs)
where trust p = p {trusted=True}
DistrustPackage str ->
case selectPackages prec_map (PackageArg str) pkgs unusable of
Left ps -> trustFlagErr dflags flag ps
Right (ps,qs) -> return (map distrust ps ++ qs)
where distrust p = p {trusted=False}
isIndefinite :: DynFlags -> Bool
isIndefinite dflags = not (unitIdIsDefinite (thisPackage dflags))
applyPackageFlag
:: DynFlags
-> PackagePrecedenceIndex
-> PackageConfigMap
-> UnusablePackages
-> Bool
-> [PackageConfig]
-> VisibilityMap
-> PackageFlag
-> IO VisibilityMap
applyPackageFlag dflags prec_map pkg_db unusable no_hide_others pkgs vm flag =
case flag of
ExposePackage _ arg (ModRenaming b rns) ->
case findPackages prec_map pkg_db arg pkgs unusable of
Left ps -> packageFlagErr dflags flag ps
Right (p:_) -> return vm'
where
n = fsPackageName p
reqs | UnitIdArg orig_uid <- arg = collectHoles orig_uid
| otherwise = Map.empty
collectHoles uid = case splitUnitIdInsts uid of
(_, Just indef) ->
let local = [ Map.singleton
(moduleName mod)
(Set.singleton $ IndefModule indef mod_name)
| (mod_name, mod) <- indefUnitIdInsts indef
, isHoleModule mod ]
recurse = [ collectHoles (moduleUnitId mod)
| (_, mod) <- indefUnitIdInsts indef ]
in Map.unionsWith Set.union $ local ++ recurse
(_, Nothing) -> Map.empty
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 (packageConfigId p) uv vm_cleared
vm_cleared | no_hide_others = vm
| (_:_) <- rns = vm
| otherwise = Map.filterWithKey
(\k uv -> k == packageConfigId p
|| First (Just n) /= uv_package_name uv) vm
_ -> panic "applyPackageFlag"
HidePackage str ->
case findPackages prec_map pkg_db (PackageArg str) pkgs unusable of
Left ps -> packageFlagErr dflags flag ps
Right ps -> return vm'
where vm' = foldl' (flip Map.delete) vm (map packageConfigId ps)
findPackages :: PackagePrecedenceIndex
-> PackageConfigMap -> PackageArg -> [PackageConfig]
-> UnusablePackages
-> Either [(PackageConfig, UnusablePackageReason)]
[PackageConfig]
findPackages prec_map pkg_db 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 == sourcePackageIdString p || str == packageNameString p
then Just p
else Nothing
finder (UnitIdArg uid) p
= let (iuid, mb_indef) = splitUnitIdInsts uid
in if iuid == installedPackageConfigId p
then Just (case mb_indef of
Nothing -> p
Just indef -> renamePackage pkg_db (indefUnitIdInsts indef) p)
else Nothing
selectPackages :: PackagePrecedenceIndex -> PackageArg -> [PackageConfig]
-> UnusablePackages
-> Either [(PackageConfig, UnusablePackageReason)]
([PackageConfig], [PackageConfig])
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)
renamePackage :: PackageConfigMap -> [(ModuleName, Module)]
-> PackageConfig -> PackageConfig
renamePackage pkg_map insts conf =
let hsubst = listToUFM insts
smod = renameHoleModule' pkg_map hsubst
new_insts = map (\(k,v) -> (k,smod v)) (instantiatedWith conf)
in conf {
instantiatedWith = new_insts,
exposedModules = map (\(mod_name, mb_mod) -> (mod_name, fmap smod mb_mod))
(exposedModules conf)
}
matchingStr :: String -> PackageConfig -> Bool
matchingStr str p
= str == sourcePackageIdString p
|| str == packageNameString p
matchingId :: InstalledUnitId -> PackageConfig -> Bool
matchingId uid p = uid == installedPackageConfigId p
matching :: PackageArg -> PackageConfig -> Bool
matching (PackageArg str) = matchingStr str
matching (UnitIdArg (DefiniteUnitId (DefUnitId uid))) = matchingId uid
matching (UnitIdArg _) = \_ -> False
sortByPreference :: PackagePrecedenceIndex -> [PackageConfig] -> [PackageConfig]
sortByPreference prec_map = sortBy (flip (compareByPreference prec_map))
compareByPreference
:: PackagePrecedenceIndex
-> PackageConfig
-> PackageConfig
-> Ordering
compareByPreference prec_map pkg pkg'
| Just prec <- Map.lookup (unitId pkg) prec_map
, Just prec' <- Map.lookup (unitId pkg') prec_map
, differentIntegerPkgs pkg pkg'
= compare prec prec'
| otherwise
= case comparing packageVersion 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
where isIntegerPkg p = packageNameString p `elem`
["integer-simple", "integer-gmp"]
differentIntegerPkgs p p' =
isIntegerPkg p && isIntegerPkg p' &&
(packageName p /= packageName p')
comparing :: Ord a => (t -> a) -> t -> t -> Ordering
comparing f a b = f a `compare` f b
packageFlagErr :: DynFlags
-> PackageFlag
-> [(PackageConfig, UnusablePackageReason)]
-> IO a
packageFlagErr dflags flag reasons
= packageFlagErr' dflags (pprFlag flag) reasons
trustFlagErr :: DynFlags
-> TrustFlag
-> [(PackageConfig, UnusablePackageReason)]
-> IO a
trustFlagErr dflags flag reasons
= packageFlagErr' dflags (pprTrustFlag flag) reasons
packageFlagErr' :: DynFlags
-> SDoc
-> [(PackageConfig, UnusablePackageReason)]
-> IO a
packageFlagErr' dflags flag_doc reasons
= throwGhcExceptionIO (CmdLineError (showSDoc dflags $ 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 WiredInUnitId = String
type WiredPackagesMap = Map WiredUnitId WiredUnitId
wired_in_pkgids :: [WiredInUnitId]
wired_in_pkgids = map unitIdString wiredInUnitIds
findWiredInPackages
:: DynFlags
-> PackagePrecedenceIndex
-> [PackageConfig]
-> VisibilityMap
-> IO ([PackageConfig],
WiredPackagesMap)
findWiredInPackages dflags prec_map pkgs vis_map = do
let
matches :: PackageConfig -> WiredInUnitId -> Bool
pc `matches` pid
| pid == unitIdString integerUnitId
= packageNameString pc `elem` ["integer-gmp", "integer-simple"]
pc `matches` pid = packageNameString pc == pid
findWiredInPackage :: [PackageConfig] -> WiredInUnitId
-> IO (Maybe (WiredInUnitId, PackageConfig))
findWiredInPackage pkgs wired_pkg =
let all_ps = [ p | p <- pkgs, p `matches` wired_pkg ]
all_exposed_ps =
[ p | p <- all_ps
, Map.member (packageConfigId 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
debugTraceMsg dflags 2 $
text "wired-in package "
<> text wired_pkg
<> text " not found."
return Nothing
pick :: PackageConfig
-> IO (Maybe (WiredInUnitId, PackageConfig))
pick pkg = do
debugTraceMsg dflags 2 $
text "wired-in package "
<> text wired_pkg
<> text " mapped to "
<> ppr (unitId pkg)
return (Just (wired_pkg, pkg))
mb_wired_in_pkgs <- mapM (findWiredInPackage pkgs) wired_in_pkgids
let
wired_in_pkgs = catMaybes mb_wired_in_pkgs
wiredInMap :: Map WiredUnitId WiredUnitId
wiredInMap = Map.fromList
[ (key, DefUnitId (stringToInstalledUnitId wiredInUnitId))
| (wiredInUnitId, pkg) <- wired_in_pkgs
, Just key <- pure $ definitePackageConfigId pkg
]
updateWiredInDependencies pkgs = map (upd_deps . upd_pkg) pkgs
where upd_pkg pkg
| Just def_uid <- definitePackageConfigId pkg
, Just wiredInUnitId <- Map.lookup def_uid wiredInMap
= let fs = installedUnitIdFS (unDefUnitId wiredInUnitId)
in pkg {
unitId = fsToInstalledUnitId fs,
componentId = ComponentId fs
}
| otherwise
= pkg
upd_deps pkg = pkg {
depends = map (unDefUnitId . upd_wired_in wiredInMap . DefUnitId) (depends pkg),
exposedModules
= map (\(k,v) -> (k, fmap (upd_wired_in_mod wiredInMap) v))
(exposedModules pkg)
}
return (updateWiredInDependencies pkgs, wiredInMap)
upd_wired_in_mod :: WiredPackagesMap -> Module -> Module
upd_wired_in_mod wiredInMap (Module uid m) = Module (upd_wired_in_uid wiredInMap uid) m
upd_wired_in_uid :: WiredPackagesMap -> UnitId -> UnitId
upd_wired_in_uid wiredInMap (DefiniteUnitId def_uid) =
DefiniteUnitId (upd_wired_in wiredInMap def_uid)
upd_wired_in_uid wiredInMap (IndefiniteUnitId indef_uid) =
IndefiniteUnitId $ newIndefUnitId
(indefUnitIdComponentId indef_uid)
(map (\(x,y) -> (x,upd_wired_in_mod wiredInMap y)) (indefUnitIdInsts indef_uid))
upd_wired_in :: WiredPackagesMap -> DefUnitId -> DefUnitId
upd_wired_in wiredInMap key
| Just key' <- Map.lookup key wiredInMap = key'
| otherwise = key
updateVisibilityMap :: WiredPackagesMap -> VisibilityMap -> VisibilityMap
updateVisibilityMap wiredInMap vis_map = foldl' f vis_map (Map.toList wiredInMap)
where f vm (from, to) = case Map.lookup (DefiniteUnitId from) vis_map of
Nothing -> vm
Just r -> Map.insert (DefiniteUnitId to) r
(Map.delete (DefiniteUnitId from) vm)
data UnusablePackageReason
=
IgnoredWithFlag
| BrokenDependencies [InstalledUnitId]
| CyclicDependencies [InstalledUnitId]
| IgnoredDependencies [InstalledUnitId]
| ShadowedDependencies [InstalledUnitId]
instance Outputable UnusablePackageReason 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 UnusablePackages = Map InstalledUnitId
(PackageConfig, UnusablePackageReason)
pprReason :: SDoc -> UnusablePackageReason -> 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 :: DynFlags -> [SCC PackageConfig] -> IO ()
reportCycles dflags sccs = mapM_ report sccs
where
report (AcyclicSCC _) = return ()
report (CyclicSCC vs) =
debugTraceMsg dflags 2 $
text "these packages are involved in a cycle:" $$
nest 2 (hsep (map (ppr . unitId) vs))
reportUnusable :: DynFlags -> UnusablePackages -> IO ()
reportUnusable dflags pkgs = mapM_ report (Map.toList pkgs)
where
report (ipid, (_, reason)) =
debugTraceMsg dflags 2 $
pprReason
(text "package" <+> ppr ipid <+> text "is") reason
type RevIndex = Map InstalledUnitId [InstalledUnitId]
reverseDeps :: InstalledPackageIndex -> RevIndex
reverseDeps db = Map.foldl' go Map.empty db
where
go r pkg = foldl' (go' (unitId pkg)) r (depends pkg)
go' from r to = Map.insertWith (++) to [from] r
removePackages :: [InstalledUnitId] -> RevIndex
-> InstalledPackageIndex
-> (InstalledPackageIndex, [PackageConfig])
removePackages 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 :: InstalledPackageIndex
-> PackageConfig
-> [InstalledUnitId]
depsNotAvailable pkg_map pkg = filter (not . (`Map.member` pkg_map)) (depends pkg)
depsAbiMismatch :: InstalledPackageIndex
-> PackageConfig
-> [InstalledUnitId]
depsAbiMismatch pkg_map pkg = map fst . filter (not . abiMatch) $ abiDepends pkg
where
abiMatch (dep_uid, abi)
| Just dep_pkg <- Map.lookup dep_uid pkg_map
= abiHash dep_pkg == abi
| otherwise
= False
ignorePackages :: [IgnorePackageFlag] -> [PackageConfig] -> UnusablePackages
ignorePackages 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 PackagePrecedenceIndex = Map InstalledUnitId Int
mergeDatabases :: DynFlags -> [(FilePath, [PackageConfig])]
-> IO (InstalledPackageIndex, PackagePrecedenceIndex)
mergeDatabases dflags = foldM merge (Map.empty, Map.empty) . zip [1..]
where
merge (pkg_map, prec_map) (i, (db_path, db)) = do
debugTraceMsg dflags 2 $
text "loading package database" <+> text db_path
forM_ (Set.toList override_set) $ \pkg ->
debugTraceMsg dflags 2 $
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 InstalledUnitId
override_set = Set.intersection (Map.keysSet db_map)
(Map.keysSet pkg_map)
pkg_map' :: InstalledPackageIndex
pkg_map' = Map.union db_map pkg_map
prec_map' :: PackagePrecedenceIndex
prec_map' = Map.union (Map.map (const i) db_map) prec_map
validateDatabase :: DynFlags -> InstalledPackageIndex
-> (InstalledPackageIndex, UnusablePackages, [SCC PackageConfig])
validateDatabase dflags pkg_map1 =
(pkg_map5, unusable, sccs)
where
ignore_flags = reverse (ignorePackageFlags dflags)
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) = removePackages (map unitId directly_broken) index pkg_map1
unusable_broken = mk_unusable BrokenDependencies depsNotAvailable pkg_map2 broken
sccs = stronglyConnComp [ (pkg, unitId pkg, depends pkg)
| pkg <- Map.elems pkg_map2 ]
getCyclicSCC (CyclicSCC vs) = map unitId vs
getCyclicSCC (AcyclicSCC _) = []
(pkg_map3, cyclic) = removePackages (concatMap getCyclicSCC sccs) index pkg_map2
unusable_cyclic = mk_unusable CyclicDependencies depsNotAvailable pkg_map3 cyclic
directly_ignored = ignorePackages ignore_flags (Map.elems pkg_map3)
(pkg_map4, ignored) = removePackages (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) = removePackages (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
mkPackageState
:: DynFlags
-> [(FilePath, [PackageConfig])]
-> [PreloadUnitId]
-> IO (PackageState,
[PreloadUnitId],
Maybe [(ModuleName, Module)])
mkPackageState dflags dbs preload0 = do
let other_flags = reverse (packageFlags dflags)
debugTraceMsg dflags 2 $
text "package flags" <+> ppr other_flags
(pkg_map1, prec_map) <- mergeDatabases dflags dbs
let (pkg_map2, unusable, sccs) = validateDatabase dflags pkg_map1
reportCycles dflags sccs
reportUnusable dflags unusable
pkgs1 <- foldM (applyTrustFlag dflags prec_map unusable)
(Map.elems pkg_map2) (reverse (trustFlags dflags))
let prelim_pkg_db = extendPackageConfigMap emptyPackageConfigMap 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 gopt Opt_HideAllPackages dflags
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 exposed p && unitIdIsDefinite (packageConfigId p) && mostPreferable p
then Map.insert (packageConfigId 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 dflags prec_map prelim_pkg_db unusable
(gopt Opt_HideAllPackages dflags) pkgs1)
vis_map1 other_flags
(pkgs2, wired_map) <- findWiredInPackages dflags prec_map pkgs1 vis_map2
let pkg_db = extendPackageConfigMap emptyPackageConfigMap pkgs2
let vis_map = updateVisibilityMap wired_map vis_map2
let hide_plugin_pkgs = gopt Opt_HideAllPluginPackages dflags
plugin_vis_map <-
case pluginPackageFlags dflags 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 dflags prec_map prelim_pkg_db unusable
(gopt Opt_HideAllPluginPackages dflags) pkgs1)
plugin_vis_map1
(reverse (pluginPackageFlags dflags))
return (updateVisibilityMap wired_map plugin_vis_map2)
let preload1 = Map.keys (Map.filter uv_explicit vis_map)
let pkgname_map = foldl' add Map.empty pkgs2
where add pn_map p
= Map.insert (packageName p) (componentId 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 preload2 = preload1
let
basicLinkedPackages
| gopt Opt_AutoLinkPackages dflags
= filter (flip elemUDFM (unPackageConfigMap pkg_db))
[baseUnitId, rtsUnitId]
| otherwise = []
preload3 = ordNub $ filter (/= thisPackage dflags)
$ (basicLinkedPackages ++ preload2)
dep_preload <- closeDeps dflags pkg_db (zip (map toInstalledUnitId preload3) (repeat Nothing))
let new_dep_preload = filter (`notElem` preload0) dep_preload
let mod_map1 = mkModuleToPkgConfAll dflags pkg_db vis_map
mod_map2 = mkUnusableModuleToPkgConfAll unusable
mod_map = Map.union mod_map1 mod_map2
dumpIfSet_dyn (dflags { pprCols = 200 }) Opt_D_dump_mod_map "Mod Map"
(pprModuleMap mod_map)
let !pstate = PackageState{
preloadPackages = dep_preload,
explicitPackages = explicit_pkgs,
pkgIdMap = pkg_db,
moduleToPkgConfAll = mod_map,
pluginModuleToPkgConfAll = mkModuleToPkgConfAll dflags pkg_db plugin_vis_map,
packageNameMap = pkgname_map,
unwireMap = Map.fromList [ (v,k) | (k,v) <- Map.toList wired_map ],
requirementContext = req_ctx
}
let new_insts = fmap (map (fmap (upd_wired_in_mod wired_map))) (thisUnitIdInsts_ dflags)
return (pstate, new_dep_preload, new_insts)
unwireUnitId :: DynFlags -> UnitId -> UnitId
unwireUnitId dflags uid@(DefiniteUnitId def_uid) =
maybe uid DefiniteUnitId (Map.lookup def_uid (unwireMap (pkgState dflags)))
unwireUnitId _ uid = uid
mkModuleToPkgConfAll
:: DynFlags
-> PackageConfigMap
-> VisibilityMap
-> ModuleToPkgConfAll
mkModuleToPkgConfAll dflags pkg_db vis_map =
Map.foldlWithKey extend_modmap emptyMap vis_map_extended
where
vis_map_extended = Map.union vis_map default_vis
default_vis = Map.fromList
[ (packageConfigId pkg, mempty)
| pkg <- eltsUDFM (unPackageConfigMap pkg_db)
, indefinite pkg || null (instantiatedWith 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 = pkg_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 (showSDoc dflags
(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' = pkg_lookup pk'
in (pk', m', fromReexportedModules e pkg')
return (m, mkModMap pk' m' origin')
esmap :: UniqFM (Map Module ModuleOrigin)
esmap = listToUFM (es False)
hiddens = [(m, mkModMap pk m ModHidden) | m <- hidden_mods]
pk = packageConfigId pkg
pkg_lookup uid = lookupPackage' (isIndefinite dflags) pkg_db uid
`orElse` pprPanic "pkg_lookup" (ppr uid)
exposed_mods = exposedModules pkg
hidden_mods = hiddenModules pkg
mkUnusableModuleToPkgConfAll :: UnusablePackages -> ModuleToPkgConfAll
mkUnusableModuleToPkgConfAll 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 = packageConfigId 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 = exposedModules pkg
hidden_mods = hiddenModules 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 :: UnitId -> ModuleName -> ModuleOrigin -> Map Module ModuleOrigin
mkModMap pkg mod = Map.singleton (mkModule pkg mod)
getPackageIncludePath :: DynFlags -> [PreloadUnitId] -> IO [String]
getPackageIncludePath dflags pkgs =
collectIncludeDirs `fmap` getPreloadPackagesAnd dflags pkgs
collectIncludeDirs :: [PackageConfig] -> [FilePath]
collectIncludeDirs ps = ordNub (filter notNull (concatMap includeDirs ps))
getPackageLibraryPath :: DynFlags -> [PreloadUnitId] -> IO [String]
getPackageLibraryPath dflags pkgs =
collectLibraryPaths dflags `fmap` getPreloadPackagesAnd dflags pkgs
collectLibraryPaths :: DynFlags -> [PackageConfig] -> [FilePath]
collectLibraryPaths dflags = ordNub . filter notNull
. concatMap (libraryDirsForWay dflags)
getPackageLinkOpts :: DynFlags -> [PreloadUnitId] -> IO ([String], [String], [String])
getPackageLinkOpts dflags pkgs =
collectLinkOpts dflags `fmap` getPreloadPackagesAnd dflags pkgs
collectLinkOpts :: DynFlags -> [PackageConfig] -> ([String], [String], [String])
collectLinkOpts dflags ps =
(
concatMap (map ("-l" ++) . packageHsLibs dflags) ps,
concatMap (map ("-l" ++) . extraLibraries) ps,
concatMap ldOptions ps
)
collectArchives :: DynFlags -> PackageConfig -> 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 ++ extraLibraries pc
getLibs :: DynFlags -> [PreloadUnitId] -> IO [(String,String)]
getLibs dflags pkgs = do
ps <- getPreloadPackagesAnd 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 -> PackageConfig -> [String]
packageHsLibs dflags p = map (mkDynName . addSuffix) (hsLibraries p)
where
ways0 = ways dflags
ways1 = filter (/= WayDyn) ways0
ways2 | WayDebug `elem` ways1 || WayProf `elem` ways1
= filter (/= WayEventLog) ways1
| otherwise
= ways1
tag = mkBuildTag (filter (not . wayRTSOnly) ways2)
rts_tag = mkBuildTag ways2
mkDynName x
| WayDyn `notElem` 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 -> PackageConfig -> [String]
libraryDirsForWay dflags
| WayDyn `elem` ways dflags = libraryDynDirs
| otherwise = libraryDirs
getPackageExtraCcOpts :: DynFlags -> [PreloadUnitId] -> IO [String]
getPackageExtraCcOpts dflags pkgs = do
ps <- getPreloadPackagesAnd dflags pkgs
return (concatMap ccOptions ps)
getPackageFrameworkPath :: DynFlags -> [PreloadUnitId] -> IO [String]
getPackageFrameworkPath dflags pkgs = do
ps <- getPreloadPackagesAnd dflags pkgs
return (ordNub (filter notNull (concatMap frameworkDirs ps)))
getPackageFrameworks :: DynFlags -> [PreloadUnitId] -> IO [String]
getPackageFrameworks dflags pkgs = do
ps <- getPreloadPackagesAnd dflags pkgs
return (concatMap frameworks ps)
lookupModuleInAllPackages :: DynFlags
-> ModuleName
-> [(Module, PackageConfig)]
lookupModuleInAllPackages dflags m
= case lookupModuleWithSuggestions dflags m Nothing of
LookupFound a b -> [(a,b)]
LookupMultiple rs -> map f rs
where f (m,_) = (m, expectJust "lookupModule" (lookupPackage dflags
(moduleUnitId m)))
_ -> []
data LookupResult =
LookupFound Module PackageConfig
| LookupMultiple [(Module, ModuleOrigin)]
| LookupHidden [(Module, ModuleOrigin)] [(Module, ModuleOrigin)]
| LookupUnusable [(Module, ModuleOrigin)]
| LookupNotFound [ModuleSuggestion]
data ModuleSuggestion = SuggestVisible ModuleName Module ModuleOrigin
| SuggestHidden ModuleName Module ModuleOrigin
lookupModuleWithSuggestions :: DynFlags
-> ModuleName
-> Maybe FastString
-> LookupResult
lookupModuleWithSuggestions dflags
= lookupModuleWithSuggestions' dflags
(moduleToPkgConfAll (pkgState dflags))
lookupPluginModuleWithSuggestions :: DynFlags
-> ModuleName
-> Maybe FastString
-> LookupResult
lookupPluginModuleWithSuggestions dflags
= lookupModuleWithSuggestions' dflags
(pluginModuleToPkgConfAll (pkgState dflags))
lookupModuleWithSuggestions' :: DynFlags
-> ModuleToPkgConfAll
-> ModuleName
-> Maybe FastString
-> LookupResult
lookupModuleWithSuggestions' dflags 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_pkg 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_pkg 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)
pkg_lookup p = lookupPackage dflags p `orElse` pprPanic "lookupModuleWithSuggestions" (ppr p <+> ppr m)
mod_pkg = pkg_lookup . moduleUnitId
filterOrigin :: Maybe FastString
-> PackageConfig
-> 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 { fromOrigPackage = e, fromExposedReexport = res,
fromHiddenReexport = rhs }
-> ModOrigin {
fromOrigPackage = if go pkg then e else Nothing
, fromExposedReexport = filter go res
, fromHiddenReexport = filter go rhs
, fromPackageFlag = False
}
where go pkg = pn == fsPackageName pkg
suggestions
| gopt Opt_HelpfulErrors dflags =
fuzzyLookup (moduleNameString m) all_mods
| otherwise = []
all_mods :: [(String, ModuleSuggestion)]
all_mods = sortBy (comparing fst) $
[ (moduleNameString m, suggestion)
| (m, e) <- Map.toList (moduleToPkgConfAll (pkgState dflags))
, suggestion <- map (getSuggestion m) (Map.toList e)
]
getSuggestion name (mod, origin) =
(if originVisible origin then SuggestVisible else SuggestHidden)
name mod origin
listVisibleModuleNames :: DynFlags -> [ModuleName]
listVisibleModuleNames dflags =
map fst (filter visible (Map.toList (moduleToPkgConfAll (pkgState dflags))))
where visible (_, ms) = any originVisible (Map.elems ms)
getPreloadPackagesAnd :: DynFlags -> [PreloadUnitId] -> IO [PackageConfig]
getPreloadPackagesAnd dflags pkgids0 =
let
pkgids = pkgids0 ++
if isIndefinite dflags
then []
else map (toInstalledUnitId . moduleUnitId . snd)
(thisUnitIdInsts dflags)
state = pkgState dflags
pkg_map = pkgIdMap state
preload = preloadPackages state
pairs = zip pkgids (repeat Nothing)
in do
all_pkgs <- throwErr dflags (foldM (add_package dflags pkg_map) preload pairs)
return (map (getInstalledPackageDetails dflags) all_pkgs)
closeDeps :: DynFlags
-> PackageConfigMap
-> [(InstalledUnitId, Maybe InstalledUnitId)]
-> IO [InstalledUnitId]
closeDeps dflags pkg_map ps
= throwErr dflags (closeDepsErr dflags pkg_map ps)
throwErr :: DynFlags -> MaybeErr MsgDoc a -> IO a
throwErr dflags m
= case m of
Failed e -> throwGhcExceptionIO (CmdLineError (showSDoc dflags e))
Succeeded r -> return r
closeDepsErr :: DynFlags
-> PackageConfigMap
-> [(InstalledUnitId,Maybe InstalledUnitId)]
-> MaybeErr MsgDoc [InstalledUnitId]
closeDepsErr dflags pkg_map ps = foldM (add_package dflags pkg_map) [] ps
add_package :: DynFlags
-> PackageConfigMap
-> [PreloadUnitId]
-> (PreloadUnitId,Maybe PreloadUnitId)
-> MaybeErr MsgDoc [PreloadUnitId]
add_package dflags pkg_db ps (p, mb_parent)
| p `elem` ps = return ps
| otherwise =
case lookupInstalledPackage' pkg_db p of
Nothing -> Failed (missingPackageMsg p <>
missingDependencyMsg mb_parent)
Just pkg -> do
ps' <- foldM add_unit_key ps (depends pkg)
return (p : ps')
where
add_unit_key ps key
= add_package dflags pkg_db ps (key, Just p)
missingPackageMsg :: Outputable pkgid => pkgid -> SDoc
missingPackageMsg p = text "unknown package:" <+> ppr p
missingDependencyMsg :: Maybe InstalledUnitId -> SDoc
missingDependencyMsg Nothing = Outputable.empty
missingDependencyMsg (Just parent)
= space <> parens (text "dependency of" <+> ftext (installedUnitIdFS parent))
componentIdString :: DynFlags -> ComponentId -> Maybe String
componentIdString dflags cid = do
conf <- lookupInstalledPackage dflags (componentIdToInstalledUnitId cid)
return $
case sourceLibName conf of
Nothing -> sourcePackageIdString conf
Just (PackageName libname) ->
packageNameString conf
++ "-" ++ showVersion (packageVersion conf)
++ ":" ++ unpackFS libname
displayInstalledUnitId :: DynFlags -> InstalledUnitId -> Maybe String
displayInstalledUnitId dflags uid =
fmap sourcePackageIdString (lookupInstalledPackage dflags uid)
isDllName :: DynFlags -> Module -> Name -> Bool
isDllName dflags this_mod name
| not (gopt Opt_ExternalDynamicRefs dflags) = False
| Just mod <- nameModule_maybe name
= case platformOS $ targetPlatform dflags of
OSMinGW32 -> moduleUnitId mod /= moduleUnitId this_mod
_ -> mod /= this_mod
| otherwise = False
pprPackages :: DynFlags -> SDoc
pprPackages = pprPackagesWith pprPackageConfig
pprPackagesWith :: (PackageConfig -> SDoc) -> DynFlags -> SDoc
pprPackagesWith pprIPI dflags =
vcat (intersperse (text "---") (map pprIPI (listPackageConfigMap dflags)))
pprPackagesSimple :: DynFlags -> SDoc
pprPackagesSimple = pprPackagesWith pprIPI
where pprIPI ipi = let i = installedUnitIdFS (unitId ipi)
e = if exposed ipi then text "E" else text " "
t = if trusted ipi then text "T" else text " "
in e <> t <> text " " <> ftext i
pprModuleMap :: ModuleToPkgConfAll -> 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 (moduleUnitId m') <+> parens (ppr o)
| otherwise = ppr m' <+> parens (ppr o)
fsPackageName :: PackageConfig -> FastString
fsPackageName = mkFastString . packageNameString
improveUnitId :: PackageConfigMap -> UnitId -> UnitId
improveUnitId _ uid@(DefiniteUnitId _) = uid
improveUnitId pkg_map uid =
case lookupPackage' False pkg_map uid of
Nothing -> uid
Just pkg ->
if installedPackageConfigId pkg `elementOfUniqSet` preloadClosure pkg_map
then packageConfigId pkg
else uid
getPackageConfigMap :: DynFlags -> PackageConfigMap
getPackageConfigMap = pkgIdMap . pkgState
interpretPackageEnv :: DynFlags -> IO DynFlags
interpretPackageEnv dflags = do
mPkgEnv <- runMaybeT $ msum $ [
getCmdLineArg >>= \env -> msum [
probeNullEnv env
, probeEnvFile env
, probeEnvName env
, cmdLineError env
]
, getEnvVar >>= \env -> msum [
probeNullEnv env
, probeEnvFile env
, probeEnvName env
, envError env
]
, notIfHideAllPackages >> msum [
findLocalEnvFile >>= probeEnvFile
, probeEnvName defaultEnvName
]
]
case mPkgEnv of
Nothing ->
return dflags
Just "-" -> do
return dflags
Just envfile -> do
content <- readFile envfile
compilationProgressMsg dflags ("Loaded package environment from " ++ envfile)
let (_, dflags') = runCmdLine (runEwM (setFlagsFromEnvFile envfile content)) dflags
return dflags'
where
namedEnvPath :: String -> MaybeT IO FilePath
namedEnvPath name = do
appdir <- versionedAppDir dflags
return $ appdir </> "environments" </> name
probeEnvName :: String -> MaybeT IO FilePath
probeEnvName name = probeEnvFile =<< namedEnvPath name
probeEnvFile :: FilePath -> MaybeT IO FilePath
probeEnvFile path = do
guard =<< liftMaybeT (doesFileExist path)
return path
probeNullEnv :: FilePath -> MaybeT IO FilePath
probeNullEnv "-" = return "-"
probeNullEnv _ = mzero
getCmdLineArg :: MaybeT IO String
getCmdLineArg = MaybeT $ return $ packageEnv dflags
getEnvVar :: MaybeT IO String
getEnvVar = do
mvar <- liftMaybeT $ try $ getEnv "GHC_ENVIRONMENT"
case mvar of
Right var -> return var
Left err -> if isDoesNotExistError err then mzero
else liftMaybeT $ throwIO err
notIfHideAllPackages :: MaybeT IO ()
notIfHideAllPackages =
guard (not (gopt Opt_HideAllPackages dflags))
defaultEnvName :: String
defaultEnvName = "default"
localEnvFileName :: FilePath
localEnvFileName = ".ghc.environment" <.> versionedFilePath dflags
findLocalEnvFile :: MaybeT IO FilePath
findLocalEnvFile = do
curdir <- liftMaybeT getCurrentDirectory
homedir <- tryMaybeT getHomeDirectory
let probe dir | isDrive dir || dir == homedir
= mzero
probe dir = do
let file = dir </> localEnvFileName
exists <- liftMaybeT (doesFileExist file)
if exists
then return file
else probe (takeDirectory dir)
probe curdir
cmdLineError :: String -> MaybeT IO a
cmdLineError env = liftMaybeT . throwGhcExceptionIO . CmdLineError $
"Package environment " ++ show env ++ " not found"
envError :: String -> MaybeT IO a
envError env = liftMaybeT . throwGhcExceptionIO . CmdLineError $
"Package environment "
++ show env
++ " (specified in GHC_ENVIRONMENT) not found"