module GHC.Driver.Make (
depanal, depanalE, depanalPartial,
load, load', LoadHowMuch(..),
instantiationNodes,
downsweep,
topSortModuleGraph,
ms_home_srcimps, ms_home_imps,
summariseModule,
hscSourceToIsBoot,
findExtraSigImports,
implicitRequirementsShallow,
noModError, cyclicModuleErr,
moduleGraphNodes, SummaryNode,
IsBootInterface(..),
ModNodeMap(..), emptyModNodeMap, modNodeMapElems, modNodeMapLookup, modNodeMapInsert
) where
#include "HsVersions.h"
import GHC.Prelude
import GHC.Tc.Utils.Backpack
import GHC.Tc.Utils.Monad ( initIfaceCheck )
import GHC.Runtime.Interpreter
import qualified GHC.Linker.Loader as Linker
import GHC.Linker.Types
import GHC.Runtime.Context
import GHC.Driver.Config
import GHC.Driver.Phases
import GHC.Driver.Pipeline
import GHC.Driver.Session
import GHC.Driver.Backend
import GHC.Driver.Monad
import GHC.Driver.Env
import GHC.Driver.Errors
import GHC.Driver.Main
import GHC.Parser.Header
import GHC.Parser.Errors.Ppr
import GHC.Iface.Load ( cannotFindModule )
import GHC.IfaceToCore ( typecheckIface )
import GHC.Iface.Recomp ( RecompileRequired ( MustCompile ) )
import GHC.Data.Bag ( unitBag, listToBag, unionManyBags, isEmptyBag )
import GHC.Data.Graph.Directed
import GHC.Data.FastString
import GHC.Data.Maybe ( expectJust )
import GHC.Data.StringBuffer
import qualified GHC.LanguageExtensions as LangExt
import GHC.Utils.Exception ( tryIO )
import GHC.Utils.Monad ( allM )
import GHC.Utils.Outputable
import GHC.Utils.Panic
import GHC.Utils.Misc
import GHC.Utils.Error
import GHC.Utils.Logger
import GHC.Utils.TmpFs
import GHC.Types.Basic
import GHC.Types.Target
import GHC.Types.SourceFile
import GHC.Types.SourceError
import GHC.Types.SrcLoc
import GHC.Types.Unique.FM
import GHC.Types.Unique.DSet
import GHC.Types.Unique.Set
import GHC.Types.Name
import GHC.Types.Name.Env
import GHC.Unit
import GHC.Unit.External
import GHC.Unit.State
import GHC.Unit.Finder
import GHC.Unit.Module.ModSummary
import GHC.Unit.Module.ModIface
import GHC.Unit.Module.ModDetails
import GHC.Unit.Module.Graph
import GHC.Unit.Home.ModInfo
import Data.Either ( rights, partitionEithers )
import qualified Data.Map as Map
import Data.Map (Map)
import qualified Data.Set as Set
import qualified GHC.Data.FiniteMap as Map ( insertListWith )
import Control.Concurrent ( forkIOWithUnmask, killThread )
import qualified GHC.Conc as CC
import Control.Concurrent.MVar
import Control.Concurrent.QSem
import Control.Exception
import Control.Monad
import Control.Monad.Trans.Except ( ExceptT(..), runExceptT, throwE )
import qualified Control.Monad.Catch as MC
import Data.IORef
import Data.List (nub, sort, sortBy, partition)
import qualified Data.List as List
import Data.Foldable (toList)
import Data.Maybe
import Data.Ord ( comparing )
import Data.Time
import Data.Bifunctor (first)
import System.Directory
import System.FilePath
import System.IO ( fixIO )
import System.IO.Error ( isDoesNotExistError )
import GHC.Conc ( getNumProcessors, getNumCapabilities, setNumCapabilities )
label_self :: String -> IO ()
label_self thread_name = do
self_tid <- CC.myThreadId
CC.labelThread self_tid thread_name
depanal :: GhcMonad m =>
[ModuleName]
-> Bool
-> m ModuleGraph
depanal excluded_mods allow_dup_roots = do
(errs, mod_graph) <- depanalE excluded_mods allow_dup_roots
if isEmptyBag errs
then pure mod_graph
else throwErrors errs
depanalE :: GhcMonad m =>
[ModuleName]
-> Bool
-> m (ErrorMessages, ModuleGraph)
depanalE excluded_mods allow_dup_roots = do
hsc_env <- getSession
(errs, mod_graph) <- depanalPartial excluded_mods allow_dup_roots
if isEmptyBag errs
then do
warnMissingHomeModules hsc_env mod_graph
setSession hsc_env { hsc_mod_graph = mod_graph }
pure (errs, mod_graph)
else do
setSession hsc_env { hsc_mod_graph = emptyMG }
pure (errs, emptyMG)
depanalPartial
:: GhcMonad m
=> [ModuleName]
-> Bool
-> m (ErrorMessages, ModuleGraph)
depanalPartial excluded_mods allow_dup_roots = do
hsc_env <- getSession
let
dflags = hsc_dflags hsc_env
targets = hsc_targets hsc_env
old_graph = hsc_mod_graph hsc_env
logger = hsc_logger hsc_env
withTiming logger dflags (text "Chasing dependencies") (const ()) $ do
liftIO $ debugTraceMsg logger dflags 2 (hcat [
text "Chasing modules from: ",
hcat (punctuate comma (map pprTarget targets))])
liftIO $ flushFinderCaches hsc_env
mod_summariesE <- liftIO $ downsweep
hsc_env (mgExtendedModSummaries old_graph)
excluded_mods allow_dup_roots
let
(errs, mod_summaries) = partitionEithers mod_summariesE
mod_graph = mkModuleGraph' $
fmap ModuleNode mod_summaries ++ instantiationNodes (hsc_units hsc_env)
return (unionManyBags errs, mod_graph)
instantiationNodes :: UnitState -> [ModuleGraphNode]
instantiationNodes unit_state = InstantiationNode <$> iuids_to_check
where
iuids_to_check :: [InstantiatedUnit]
iuids_to_check =
nubSort $ concatMap goUnitId (explicitUnits unit_state)
where
goUnitId uid =
[ recur
| VirtUnit indef <- [uid]
, inst <- instUnitInsts indef
, recur <- (indef :) $ goUnitId $ moduleUnit $ snd inst
]
warnMissingHomeModules :: GhcMonad m => HscEnv -> ModuleGraph -> m ()
warnMissingHomeModules hsc_env mod_graph =
when (wopt Opt_WarnMissingHomeModules dflags && not (null missing)) $
logWarnings (listToBag [warn])
where
dflags = hsc_dflags hsc_env
targets = map targetId (hsc_targets hsc_env)
is_known_module mod = any (is_my_target mod) targets
is_my_target mod (TargetModule name)
= moduleName (ms_mod mod) == name
is_my_target mod (TargetFile target_file _)
| Just mod_file <- ml_hs_file (ms_location mod)
= target_file == mod_file ||
addBootSuffix target_file == mod_file ||
mkModuleName (fst $ splitExtension target_file)
== moduleName (ms_mod mod)
is_my_target _ _ = False
missing = map (moduleName . ms_mod) $
filter (not . is_known_module) (mgModSummaries mod_graph)
msg
| gopt Opt_BuildingCabalPackage dflags
= hang
(text "These modules are needed for compilation but not listed in your .cabal file's other-modules: ")
4
(sep (map ppr missing))
| otherwise
=
hang
(text "Modules are not listed in command line but needed for compilation: ")
4
(sep (map ppr missing))
warn = makeIntoWarning
(Reason Opt_WarnMissingHomeModules)
(mkPlainMsgEnvelope noSrcSpan msg)
data LoadHowMuch
= LoadAllTargets
| LoadUpTo ModuleName
| LoadDependenciesOf ModuleName
load :: GhcMonad m => LoadHowMuch -> m SuccessFlag
load how_much = do
(errs, mod_graph) <- depanalE [] False
success <- load' how_much (Just batchMsg) mod_graph
warnUnusedPackages
if isEmptyBag errs
then pure success
else throwErrors errs
warnUnusedPackages :: GhcMonad m => m ()
warnUnusedPackages = do
hsc_env <- getSession
eps <- liftIO $ hscEPS hsc_env
let dflags = hsc_dflags hsc_env
state = hsc_units hsc_env
pit = eps_PIT eps
let loadedPackages
= map (unsafeLookupUnit state)
. nub . sort
. map moduleUnit
. moduleEnvKeys
$ pit
requestedArgs = mapMaybe packageArg (packageFlags dflags)
unusedArgs
= filter (\arg -> not $ any (matching state arg) loadedPackages)
requestedArgs
let warn = makeIntoWarning
(Reason Opt_WarnUnusedPackages)
(mkPlainMsgEnvelope noSrcSpan msg)
msg = vcat [ text "The following packages were specified" <+>
text "via -package or -package-id flags,"
, text "but were not needed for compilation:"
, nest 2 (vcat (map (withDash . pprUnusedArg) unusedArgs)) ]
when (wopt Opt_WarnUnusedPackages dflags && not (null unusedArgs)) $
logWarnings (listToBag [warn])
where
packageArg (ExposePackage _ arg _) = Just arg
packageArg _ = Nothing
pprUnusedArg (PackageArg str) = text str
pprUnusedArg (UnitIdArg uid) = ppr uid
withDash = (<+>) (text "-")
matchingStr :: String -> UnitInfo -> Bool
matchingStr str p
= str == unitPackageIdString p
|| str == unitPackageNameString p
matching :: UnitState -> PackageArg -> UnitInfo -> Bool
matching _ (PackageArg str) p = matchingStr str p
matching state (UnitIdArg uid) p = uid == realUnit state p
realUnit :: UnitState -> UnitInfo -> Unit
realUnit state
= unwireUnit state
. RealUnit
. Definite
. unitId
load' :: GhcMonad m => LoadHowMuch -> Maybe Messager -> ModuleGraph -> m SuccessFlag
load' how_much mHscMessage mod_graph = do
modifySession $ \hsc_env -> hsc_env { hsc_mod_graph = mod_graph }
guessOutputFile
hsc_env <- getSession
let hpt1 = hsc_HPT hsc_env
let dflags = hsc_dflags hsc_env
let logger = hsc_logger hsc_env
let interp = hscInterp hsc_env
let all_home_mods =
mkUniqSet [ ms_mod_name s
| s <- mgModSummaries mod_graph, isBootSummary s == NotBoot]
let checkHowMuch (LoadUpTo m) = checkMod m
checkHowMuch (LoadDependenciesOf m) = checkMod m
checkHowMuch _ = id
checkMod m and_then
| m `elementOfUniqSet` all_home_mods = and_then
| otherwise = do
liftIO $ errorMsg logger dflags
(text "no such module:" <+> quotes (ppr m))
return Failed
checkHowMuch how_much $ do
let mg2_with_srcimps :: [SCC ModSummary]
mg2_with_srcimps = filterToposortToModules $
topSortModuleGraph True mod_graph Nothing
warnUnnecessarySourceImports mg2_with_srcimps
let
stable_mods@(stable_obj,stable_bco)
= checkStability hpt1 mg2_with_srcimps all_home_mods
pruned_hpt = pruneHomePackageTable hpt1
(flattenSCCs mg2_with_srcimps)
stable_mods
_ <- liftIO $ evaluate pruned_hpt
setSession $ discardIC $ hsc_env { hsc_HPT = pruned_hpt }
liftIO $ debugTraceMsg logger dflags 2 (text "Stable obj:" <+> ppr stable_obj $$
text "Stable BCO:" <+> ppr stable_bco)
let stable_linkables = [ linkable
| m <- nonDetEltsUniqSet stable_obj ++
nonDetEltsUniqSet stable_bco,
Just hmi <- [lookupHpt pruned_hpt m],
Just linkable <- [hm_linkable hmi] ]
liftIO $ unload interp hsc_env stable_linkables
let full_mg, partial_mg0, partial_mg, unstable_mg :: [SCC ModuleGraphNode]
stable_mg :: [SCC ExtendedModSummary]
full_mg = topSortModuleGraph False mod_graph Nothing
maybe_top_mod = case how_much of
LoadUpTo m -> Just m
LoadDependenciesOf m -> Just m
_ -> Nothing
partial_mg0 = topSortModuleGraph False mod_graph maybe_top_mod
partial_mg
| LoadDependenciesOf _mod <- how_much
= ASSERT( case last partial_mg0 of
AcyclicSCC (ModuleNode (ExtendedModSummary ms _)) -> ms_mod_name ms == _mod; _ -> False )
List.init partial_mg0
| otherwise
= partial_mg0
stable_mg =
[ AcyclicSCC ems
| AcyclicSCC (ModuleNode ems@(ExtendedModSummary ms _)) <- full_mg
, stable_mod_summary ms
]
stable_mod_summary ms =
ms_mod_name ms `elementOfUniqSet` stable_obj ||
ms_mod_name ms `elementOfUniqSet` stable_bco
unstable_mg = filter not_stable partial_mg
where not_stable (CyclicSCC _) = True
not_stable (AcyclicSCC (InstantiationNode _)) = True
not_stable (AcyclicSCC (ModuleNode (ExtendedModSummary ms _)))
= not $ stable_mod_summary ms
mg = fmap (fmap ModuleNode) stable_mg ++ unstable_mg
liftIO $ debugTraceMsg logger dflags 2 (hang (text "Ready for upsweep")
2 (ppr mg))
n_jobs <- case parMakeCount dflags of
Nothing -> liftIO getNumProcessors
Just n -> return n
let upsweep_fn | n_jobs > 1 = parUpsweep n_jobs
| otherwise = upsweep
setSession hsc_env{ hsc_HPT = emptyHomePackageTable }
(upsweep_ok, modsUpswept) <- withDeferredDiagnostics $
upsweep_fn mHscMessage pruned_hpt stable_mods mg
let nodesDone = reverse modsUpswept
(_, modsDone) = partitionNodes nodesDone
if succeeded upsweep_ok
then
do liftIO $ debugTraceMsg logger dflags 2 (text "Upsweep completely successful.")
hsc_env1 <- getSession
liftIO $ cleanCurrentModuleTempFiles logger (hsc_tmpfs hsc_env1) dflags
let ofile = outputFile dflags
let no_hs_main = gopt Opt_NoHsMain dflags
let
main_mod = mainModIs hsc_env
a_root_is_Main = mgElemModule mod_graph main_mod
do_linking = a_root_is_Main || no_hs_main || ghcLink dflags == LinkDynLib || ghcLink dflags == LinkStaticLib
hsc_env <- getSession
linkresult <- liftIO $ link (ghcLink dflags)
logger
(hsc_tmpfs hsc_env)
(hsc_hooks hsc_env)
dflags
(hsc_unit_env hsc_env)
do_linking
(hsc_HPT hsc_env1)
if ghcLink dflags == LinkBinary && isJust ofile && not do_linking
then do
liftIO $ errorMsg logger dflags $ text
("output was redirected with -o, " ++
"but no output will be generated\n" ++
"because there is no " ++
moduleNameString (moduleName main_mod) ++ " module.")
loadFinish Failed linkresult
else
loadFinish Succeeded linkresult
else
do liftIO $ debugTraceMsg logger dflags 2 (text "Upsweep partially successful.")
let modsDone_names
= map (ms_mod . emsModSummary) modsDone
let mods_to_zap_names
= findPartiallyCompletedCycles modsDone_names
mg2_with_srcimps
let (mods_to_clean, mods_to_keep) =
partition ((`Set.member` mods_to_zap_names).ms_mod) $
emsModSummary <$> modsDone
hsc_env1 <- getSession
let hpt4 = hsc_HPT hsc_env1
unneeded_temps = concat
[ms_hspp_file : object_files
| ModSummary{ms_mod, ms_hspp_file} <- mods_to_clean
, let object_files = maybe [] linkableObjs $
lookupHpt hpt4 (moduleName ms_mod)
>>= hm_linkable
]
tmpfs <- hsc_tmpfs <$> getSession
liftIO $ changeTempFilesLifetime tmpfs TFL_CurrentModule unneeded_temps
liftIO $ cleanCurrentModuleTempFiles logger tmpfs dflags
let hpt5 = retainInTopLevelEnvs (map ms_mod_name mods_to_keep)
hpt4
let just_linkables =
isNoLink (ghcLink dflags)
|| allHpt (isJust.hm_linkable)
(filterHpt ((== HsSrcFile).mi_hsc_src.hm_iface)
hpt5)
ASSERT( just_linkables ) do
hsc_env <- getSession
linkresult <- liftIO $ link (ghcLink dflags)
logger
(hsc_tmpfs hsc_env)
(hsc_hooks hsc_env)
dflags
(hsc_unit_env hsc_env)
False
hpt5
modifySession $ \hsc_env -> hsc_env{ hsc_HPT = hpt5 }
loadFinish Failed linkresult
partitionNodes
:: [ModuleGraphNode]
-> ( [InstantiatedUnit]
, [ExtendedModSummary]
)
partitionNodes ns = partitionEithers $ flip fmap ns $ \case
InstantiationNode x -> Left x
ModuleNode x -> Right x
loadFinish :: GhcMonad m => SuccessFlag -> SuccessFlag -> m SuccessFlag
loadFinish _all_ok Failed
= do hsc_env <- getSession
let interp = hscInterp hsc_env
liftIO $ unload interp hsc_env []
modifySession discardProg
return Failed
loadFinish all_ok Succeeded
= do modifySession discardIC
return all_ok
discardProg :: HscEnv -> HscEnv
discardProg hsc_env
= discardIC $ hsc_env { hsc_mod_graph = emptyMG
, hsc_HPT = emptyHomePackageTable }
discardIC :: HscEnv -> HscEnv
discardIC hsc_env
= hsc_env { hsc_IC = empty_ic { ic_int_print = new_ic_int_print
, ic_monad = new_ic_monad } }
where
!new_ic_int_print = keep_external_name ic_int_print
!new_ic_monad = keep_external_name ic_monad
dflags = ic_dflags old_ic
old_ic = hsc_IC hsc_env
empty_ic = emptyInteractiveContext dflags
keep_external_name ic_name
| nameIsFromExternalPackage home_unit old_name = old_name
| otherwise = ic_name empty_ic
where
home_unit = hsc_home_unit hsc_env
old_name = ic_name old_ic
guessOutputFile :: GhcMonad m => m ()
guessOutputFile = modifySession $ \env ->
let dflags = hsc_dflags env
!mod_graph = hsc_mod_graph env
mainModuleSrcPath :: Maybe String
mainModuleSrcPath = do
ms <- mgLookupModule mod_graph (mainModIs env)
ml_hs_file (ms_location ms)
name = fmap dropExtension mainModuleSrcPath
name_exe = do
#if defined(mingw32_HOST_OS)
name' <- fmap (<.> "exe") name
#else
name' <- name
#endif
mainModuleSrcPath' <- mainModuleSrcPath
if name' == mainModuleSrcPath'
then throwGhcException . UsageError $
"default output name would overwrite the input file; " ++
"must specify -o explicitly"
else Just name'
in
case outputFile_ dflags of
Just _ -> env
Nothing -> env { hsc_dflags = dflags { outputFile_ = name_exe } }
pruneHomePackageTable :: HomePackageTable
-> [ModSummary]
-> StableModules
-> HomePackageTable
pruneHomePackageTable hpt summ (stable_obj, stable_bco)
= mapHpt prune hpt
where prune hmi
| is_stable modl = hmi'
| otherwise = hmi'{ hm_details = emptyModDetails }
where
modl = moduleName (mi_module (hm_iface hmi))
hmi' | Just l <- hm_linkable hmi, linkableTime l < ms_hs_date ms
= hmi{ hm_linkable = Nothing }
| otherwise
= hmi
where ms = expectJust "prune" (lookupUFM ms_map modl)
ms_map = listToUFM [(ms_mod_name ms, ms) | ms <- summ]
is_stable m =
m `elementOfUniqSet` stable_obj ||
m `elementOfUniqSet` stable_bco
findPartiallyCompletedCycles :: [Module] -> [SCC ModSummary] -> Set.Set Module
findPartiallyCompletedCycles modsDone theGraph
= Set.unions
[mods_in_this_cycle
| CyclicSCC vs <- theGraph
, let names_in_this_cycle = Set.fromList (map ms_mod vs)
mods_in_this_cycle =
Set.intersection (Set.fromList modsDone) names_in_this_cycle
, Set.size mods_in_this_cycle < Set.size names_in_this_cycle]
unload :: Interp -> HscEnv -> [Linkable] -> IO ()
unload interp hsc_env stable_linkables
= case ghcLink (hsc_dflags hsc_env) of
LinkInMemory -> Linker.unload interp hsc_env stable_linkables
_other -> return ()
type StableModules =
( UniqSet ModuleName
, UniqSet ModuleName
)
checkStability
:: HomePackageTable
-> [SCC ModSummary]
-> UniqSet ModuleName
-> StableModules
checkStability hpt sccs all_home_mods =
foldl' checkSCC (emptyUniqSet, emptyUniqSet) sccs
where
checkSCC :: StableModules -> SCC ModSummary -> StableModules
checkSCC (stable_obj, stable_bco) scc0
| stableObjects = (addListToUniqSet stable_obj scc_mods, stable_bco)
| stableBCOs = (stable_obj, addListToUniqSet stable_bco scc_mods)
| otherwise = (stable_obj, stable_bco)
where
scc = flattenSCC scc0
scc_mods = map ms_mod_name scc
home_module m =
m `elementOfUniqSet` all_home_mods && m `notElem` scc_mods
scc_allimps = nub (filter home_module (concatMap ms_home_allimps scc))
stable_obj_imps = map (`elementOfUniqSet` stable_obj) scc_allimps
stable_bco_imps = map (`elementOfUniqSet` stable_bco) scc_allimps
stableObjects =
and stable_obj_imps
&& all object_ok scc
stableBCOs =
and (zipWith (||) stable_obj_imps stable_bco_imps)
&& all bco_ok scc
object_ok ms
| gopt Opt_ForceRecomp (ms_hspp_opts ms) = False
| Just t <- ms_obj_date ms = t >= ms_hs_date ms
&& same_as_prev t
| otherwise = False
where
same_as_prev t = case lookupHpt hpt (ms_mod_name ms) of
Just hmi | Just l <- hm_linkable hmi
-> isObjectLinkable l && t == linkableTime l
_other -> True
bco_ok ms
| gopt Opt_ForceRecomp (ms_hspp_opts ms) = False
| otherwise = case lookupHpt hpt (ms_mod_name ms) of
Just hmi | Just l <- hm_linkable hmi ->
not (isObjectLinkable l) &&
linkableTime l >= ms_hs_date ms
_other -> False
data LogQueue = LogQueue !(IORef [Maybe (WarnReason, Severity, SrcSpan, SDoc)])
!(MVar ())
type CompilationGraph = [(ModuleGraphNode, MVar SuccessFlag, LogQueue)]
buildCompGraph :: [SCC ModuleGraphNode] -> IO (CompilationGraph, Maybe [ModuleGraphNode])
buildCompGraph [] = return ([], Nothing)
buildCompGraph (scc:sccs) = case scc of
AcyclicSCC ms -> do
mvar <- newEmptyMVar
log_queue <- do
ref <- newIORef []
sem <- newEmptyMVar
return (LogQueue ref sem)
(rest,cycle) <- buildCompGraph sccs
return ((ms,mvar,log_queue):rest, cycle)
CyclicSCC mss -> return ([], Just mss)
data BuildModule = BuildModule_Unit !InstantiatedUnit | BuildModule_Module !ModuleWithIsBoot
deriving (Eq, Ord)
hscSourceToIsBoot :: HscSource -> IsBootInterface
hscSourceToIsBoot HsBootFile = IsBoot
hscSourceToIsBoot _ = NotBoot
mkBuildModule :: ModuleGraphNode -> BuildModule
mkBuildModule = \case
InstantiationNode x -> BuildModule_Unit x
ModuleNode ems -> BuildModule_Module $ mkBuildModule0 (emsModSummary ems)
mkHomeBuildModule :: ModuleGraphNode -> NodeKey
mkHomeBuildModule = \case
InstantiationNode x -> NodeKey_Unit x
ModuleNode ems -> NodeKey_Module $ mkHomeBuildModule0 (emsModSummary ems)
mkBuildModule0 :: ModSummary -> ModuleWithIsBoot
mkBuildModule0 ms = GWIB
{ gwib_mod = ms_mod ms
, gwib_isBoot = isBootSummary ms
}
mkHomeBuildModule0 :: ModSummary -> ModuleNameWithIsBoot
mkHomeBuildModule0 ms = GWIB
{ gwib_mod = moduleName $ ms_mod ms
, gwib_isBoot = isBootSummary ms
}
parUpsweep
:: GhcMonad m
=> Int
-> Maybe Messager
-> HomePackageTable
-> StableModules
-> [SCC ModuleGraphNode]
-> m (SuccessFlag,
[ModuleGraphNode])
parUpsweep n_jobs mHscMessage old_hpt stable_mods sccs = do
hsc_env <- getSession
let dflags = hsc_dflags hsc_env
let logger = hsc_logger hsc_env
let tmpfs = hsc_tmpfs hsc_env
hsc_env_var <- liftIO $ newMVar hsc_env
old_hpt_var <- liftIO $ newIORef old_hpt
par_sem <- liftIO $ newQSem n_jobs
let updNumCapabilities = liftIO $ do
n_capabilities <- getNumCapabilities
n_cpus <- getNumProcessors
let n_caps = min n_jobs n_cpus
unless (n_capabilities /= 1) $ setNumCapabilities n_caps
return n_capabilities
let resetNumCapabilities orig_n = liftIO $ setNumCapabilities orig_n
MC.bracket updNumCapabilities resetNumCapabilities $ \_ -> do
let finallySyncSession io = io `MC.finally` do
hsc_env <- liftIO $ readMVar hsc_env_var
setSession hsc_env
finallySyncSession $ do
(comp_graph,cycle) <- liftIO $ buildCompGraph sccs
let comp_graph_w_idx = zip comp_graph [1..]
let graph = map fstOf3 (reverse comp_graph)
boot_modules = mkModuleSet
[ms_mod ms | ModuleNode (ExtendedModSummary ms _) <- graph, isBootSummary ms == IsBoot]
comp_graph_loops = go graph boot_modules
where
remove ms bm = case isBootSummary ms of
IsBoot -> delModuleSet bm (ms_mod ms)
NotBoot -> bm
go [] _ = []
go (InstantiationNode _ : mss) boot_modules
= go mss boot_modules
go mg@(mnode@(ModuleNode (ExtendedModSummary ms _)) : mss) boot_modules
| Just loop <- getModLoop ms mg (`elemModuleSet` boot_modules)
= map mkBuildModule (mnode : loop) : go mss (remove ms boot_modules)
| otherwise
= go mss (remove ms boot_modules)
let home_mod_map :: Map BuildModule (MVar SuccessFlag, Int)
home_mod_map =
Map.fromList [ (mkBuildModule ms, (mvar, idx))
| ((ms,mvar,_),idx) <- comp_graph_w_idx ]
liftIO $ label_self "main --make thread"
thread_safe_logger <- liftIO $ makeThreadSafe logger
let { spawnWorkers = forM comp_graph_w_idx $ \((mod,!mvar,!log_queue),!mod_idx) ->
forkIOWithUnmask $ \unmask -> do
liftIO $ label_self $ unwords $ concat
[ [ "worker --make thread" ]
, case mod of
InstantiationNode iuid ->
[ "for instantiation of unit"
, show $ VirtUnit iuid
]
ModuleNode ems ->
[ "for module"
, show (moduleNameString (ms_mod_name (emsModSummary ems)))
]
, ["number"
, show mod_idx
]
]
let lcl_logger = pushLogHook (const (parLogAction log_queue)) thread_safe_logger
lcl_tmpfs <- forkTmpFsFrom tmpfs
m_res <- MC.try $ unmask $ prettyPrintGhcErrors dflags $
case mod of
InstantiationNode iuid -> do
hsc_env <- readMVar hsc_env_var
liftIO $ upsweep_inst hsc_env mHscMessage mod_idx (length sccs) iuid
pure Succeeded
ModuleNode ems ->
parUpsweep_one (emsModSummary ems) home_mod_map comp_graph_loops
lcl_logger lcl_tmpfs dflags (hsc_home_unit hsc_env)
mHscMessage
par_sem hsc_env_var old_hpt_var
stable_mods mod_idx (length sccs)
res <- case m_res of
Right flag -> return flag
Left exc -> do
when (fromException exc /= Just ThreadKilled)
(errorMsg lcl_logger dflags (text (show exc)))
return Failed
putMVar mvar res
writeLogQueue log_queue Nothing
mergeTmpFsInto lcl_tmpfs tmpfs
; killWorkers = MC.uninterruptibleMask_ . mapM_ killThread }
results <- liftIO $ MC.bracket spawnWorkers killWorkers $ \_ ->
forM comp_graph $ \(mod,mvar,log_queue) -> do
printLogs logger dflags log_queue
result <- readMVar mvar
if succeeded result then return (Just mod) else return Nothing
let ok_results = reverse (catMaybes results)
case cycle of
Just mss -> do
liftIO $ fatalErrorMsg logger dflags (cyclicModuleErr mss)
return (Failed,ok_results)
Nothing -> do
let success_flag = successIf (all isJust results)
return (success_flag,ok_results)
where
writeLogQueue :: LogQueue -> Maybe (WarnReason,Severity,SrcSpan,SDoc) -> IO ()
writeLogQueue (LogQueue ref sem) msg = do
atomicModifyIORef' ref $ \msgs -> (msg:msgs,())
_ <- tryPutMVar sem ()
return ()
parLogAction :: LogQueue -> LogAction
parLogAction log_queue _dflags !reason !severity !srcSpan !msg =
writeLogQueue log_queue (Just (reason,severity,srcSpan,msg))
printLogs :: Logger -> DynFlags -> LogQueue -> IO ()
printLogs !logger !dflags (LogQueue ref sem) = read_msgs
where read_msgs = do
takeMVar sem
msgs <- atomicModifyIORef' ref $ \xs -> ([], reverse xs)
print_loop msgs
print_loop [] = read_msgs
print_loop (x:xs) = case x of
Just (reason,severity,srcSpan,msg) -> do
putLogMsg logger dflags reason severity srcSpan msg
print_loop xs
Nothing -> return ()
parUpsweep_one
:: ModSummary
-> Map BuildModule (MVar SuccessFlag, Int)
-> [[BuildModule]]
-> Logger
-> TmpFs
-> DynFlags
-> HomeUnit
-> Maybe Messager
-> QSem
-> MVar HscEnv
-> IORef HomePackageTable
-> StableModules
-> Int
-> Int
-> IO SuccessFlag
parUpsweep_one mod home_mod_map comp_graph_loops lcl_logger lcl_tmpfs lcl_dflags home_unit mHscMessage par_sem
hsc_env_var old_hpt_var stable_mods mod_index num_mods = do
let this_build_mod = mkBuildModule0 mod
let home_imps = map unLoc $ ms_home_imps mod
let home_src_imps = map unLoc $ ms_home_srcimps mod
let textual_deps = Set.fromList $
zipWith f home_imps (repeat NotBoot) ++
zipWith f home_src_imps (repeat IsBoot)
where f mn isBoot = BuildModule_Module $ GWIB
{ gwib_mod = mkHomeModule home_unit mn
, gwib_isBoot = isBoot
}
let finish_loop :: Maybe [ModuleWithIsBoot]
finish_loop = listToMaybe
[ flip mapMaybe (tail loop) $ \case
BuildModule_Unit _ -> Nothing
BuildModule_Module ms -> Just ms
| loop <- comp_graph_loops
, head loop == BuildModule_Module this_build_mod
]
let int_loop_deps :: Set.Set BuildModule
int_loop_deps = Set.fromList $
case finish_loop of
Nothing -> []
Just loop -> BuildModule_Module <$> filter (/= this_build_mod) loop
let ext_loop_deps :: Set.Set BuildModule
ext_loop_deps = Set.fromList
[ head loop | loop <- comp_graph_loops
, any (`Set.member` textual_deps) loop
, BuildModule_Module this_build_mod `notElem` loop ]
let all_deps = foldl1 Set.union [textual_deps, int_loop_deps, ext_loop_deps]
let home_deps_with_idx =
[ home_dep | dep <- Set.toList all_deps
, Just home_dep <- [Map.lookup dep home_mod_map]
]
let home_deps = map fst $ sortBy (flip (comparing snd)) home_deps_with_idx
deps_ok <- allM (fmap succeeded . readMVar) home_deps
if not deps_ok
then return Failed
else do
hsc_env <- readMVar hsc_env_var
old_hpt <- readIORef old_hpt_var
let logg err = printBagOfErrors lcl_logger lcl_dflags (srcErrorMessages err)
let withSem sem = MC.bracket_ (waitQSem sem) (signalQSem sem)
mb_mod_info <- withSem par_sem $
handleSourceError (\err -> do logg err; return Nothing) $ do
let lcl_hsc_env = localize_hsc_env hsc_env
type_env_var <- liftIO $ newIORef emptyNameEnv
let lcl_hsc_env' = lcl_hsc_env { hsc_type_env_var =
Just (ms_mod mod, type_env_var) }
lcl_hsc_env'' <- case finish_loop of
Nothing -> return lcl_hsc_env'
Just loop -> typecheckLoop lcl_dflags lcl_hsc_env' $
filter (/= moduleName (gwib_mod this_build_mod)) $
map (moduleName . gwib_mod) loop
mod_info <- upsweep_mod lcl_hsc_env'' mHscMessage old_hpt stable_mods
mod mod_index num_mods
return (Just mod_info)
case mb_mod_info of
Nothing -> return Failed
Just mod_info -> do
let this_mod = ms_mod_name mod
unless (isBootSummary mod == IsBoot) $
atomicModifyIORef' old_hpt_var $ \old_hpt ->
(delFromHpt old_hpt this_mod, ())
lcl_hsc_env' <- modifyMVar hsc_env_var $ \hsc_env -> do
let hsc_env' = hsc_env
{ hsc_HPT = addToHpt (hsc_HPT hsc_env)
this_mod mod_info }
hsc_env'' <- case finish_loop of
Nothing -> return hsc_env'
Just loop -> typecheckLoop lcl_dflags hsc_env' $
map (moduleName . gwib_mod) loop
return (hsc_env'', localize_hsc_env hsc_env'')
cleanCurrentModuleTempFiles (hsc_logger lcl_hsc_env')
(hsc_tmpfs lcl_hsc_env')
(hsc_dflags lcl_hsc_env')
return Succeeded
where
localize_hsc_env hsc_env
= hsc_env { hsc_logger = lcl_logger
, hsc_tmpfs = lcl_tmpfs
}
upsweep
:: forall m
. GhcMonad m
=> Maybe Messager
-> HomePackageTable
-> StableModules
-> [SCC ModuleGraphNode]
-> m (SuccessFlag,
[ModuleGraphNode])
upsweep mHscMessage old_hpt stable_mods sccs = do
(res, done) <- upsweep' old_hpt emptyMG sccs 1 (length sccs)
return (res, reverse $ mgModSummaries' done)
where
keep_going
:: [NodeKey]
-> HomePackageTable
-> ModuleGraph
-> [SCC ModuleGraphNode]
-> Int
-> Int
-> m (SuccessFlag, ModuleGraph)
keep_going this_mods old_hpt done mods mod_index nmods = do
let sum_deps ms (AcyclicSCC iuidOrMod) =
if any (flip elem $ unfilteredEdges False iuidOrMod) $ ms
then mkHomeBuildModule iuidOrMod : ms
else ms
sum_deps ms _ = ms
dep_closure = foldl' sum_deps this_mods mods
dropped_ms = drop (length this_mods) (reverse dep_closure)
prunable (AcyclicSCC node) = elem (mkHomeBuildModule node) dep_closure
prunable _ = False
mods' = filter (not . prunable) mods
nmods' = nmods length dropped_ms
when (not $ null dropped_ms) $ do
dflags <- getSessionDynFlags
logger <- getLogger
liftIO $ fatalErrorMsg logger dflags (keepGoingPruneErr $ dropped_ms)
(_, done') <- upsweep' old_hpt done mods' (mod_index+1) nmods'
return (Failed, done')
upsweep'
:: HomePackageTable
-> ModuleGraph
-> [SCC ModuleGraphNode]
-> Int
-> Int
-> m (SuccessFlag, ModuleGraph)
upsweep' _old_hpt done
[] _ _
= return (Succeeded, done)
upsweep' _old_hpt done
(CyclicSCC ms : mods) mod_index nmods
= do dflags <- getSessionDynFlags
logger <- getLogger
liftIO $ fatalErrorMsg logger dflags (cyclicModuleErr ms)
if gopt Opt_KeepGoing dflags
then keep_going (mkHomeBuildModule <$> ms) old_hpt done mods mod_index nmods
else return (Failed, done)
upsweep' old_hpt done
(AcyclicSCC (InstantiationNode iuid) : mods) mod_index nmods
= do hsc_env <- getSession
liftIO $ upsweep_inst hsc_env mHscMessage mod_index nmods iuid
upsweep' old_hpt done mods (mod_index+1) nmods
upsweep' old_hpt done
(AcyclicSCC (ModuleNode ems@(ExtendedModSummary mod _)) : mods) mod_index nmods
= do
let logg _mod = defaultWarnErrLogger
hsc_env <- getSession
liftIO $ cleanCurrentModuleTempFiles (hsc_logger hsc_env)
(hsc_tmpfs hsc_env)
(hsc_dflags hsc_env)
type_env_var <- liftIO $ newIORef emptyNameEnv
let hsc_env1 = hsc_env { hsc_type_env_var =
Just (ms_mod mod, type_env_var) }
setSession hsc_env1
hsc_env2 <- liftIO $ reTypecheckLoop hsc_env1 mod done
setSession hsc_env2
mb_mod_info
<- handleSourceError
(\err -> do logg mod (Just err); return Nothing) $ do
mod_info <- liftIO $ upsweep_mod hsc_env2 mHscMessage old_hpt stable_mods
mod mod_index nmods
logg mod Nothing
return (Just mod_info)
case mb_mod_info of
Nothing -> do
dflags <- getSessionDynFlags
if gopt Opt_KeepGoing dflags
then keep_going [NodeKey_Module $ mkHomeBuildModule0 mod] old_hpt done mods mod_index nmods
else return (Failed, done)
Just mod_info -> do
let this_mod = ms_mod_name mod
hpt1 = addToHpt (hsc_HPT hsc_env2) this_mod mod_info
hsc_env3 = hsc_env2 { hsc_HPT = hpt1, hsc_type_env_var = Nothing }
old_hpt1 = case isBootSummary mod of
IsBoot -> old_hpt
NotBoot -> delFromHpt old_hpt this_mod
done' = extendMG done ems
hsc_env4 <- liftIO $ reTypecheckLoop hsc_env3 mod done'
setSession hsc_env4
when (backend (hsc_dflags hsc_env4) == Interpreter) $
liftIO $ hscAddSptEntries hsc_env4
[ spt
| Just linkable <- pure $ hm_linkable mod_info
, unlinked <- linkableUnlinked linkable
, BCOs _ spts <- pure unlinked
, spt <- spts
]
upsweep' old_hpt1 done' mods (mod_index+1) nmods
maybeGetIfaceDate :: DynFlags -> ModLocation -> IO (Maybe UTCTime)
maybeGetIfaceDate dflags location
| writeInterfaceOnlyMode dflags
= modificationTimeIfExists (ml_hi_file location)
| otherwise
= return Nothing
upsweep_inst :: HscEnv
-> Maybe Messager
-> Int
-> Int
-> InstantiatedUnit
-> IO ()
upsweep_inst hsc_env mHscMessage mod_index nmods iuid = do
case mHscMessage of
Just hscMessage -> hscMessage hsc_env (mod_index, nmods) MustCompile (InstantiationNode iuid)
Nothing -> return ()
runHsc hsc_env $ ioMsgMaybe $ tcRnCheckUnit hsc_env $ VirtUnit iuid
pure ()
upsweep_mod :: HscEnv
-> Maybe Messager
-> HomePackageTable
-> StableModules
-> ModSummary
-> Int
-> Int
-> IO HomeModInfo
upsweep_mod hsc_env mHscMessage old_hpt (stable_obj, stable_bco) summary mod_index nmods
= let
this_mod_name = ms_mod_name summary
this_mod = ms_mod summary
mb_obj_date = ms_obj_date summary
mb_if_date = ms_iface_date summary
obj_fn = ml_obj_file (ms_location summary)
hs_date = ms_hs_date summary
is_stable_obj = this_mod_name `elementOfUniqSet` stable_obj
is_stable_bco = this_mod_name `elementOfUniqSet` stable_bco
old_hmi = lookupHpt old_hpt this_mod_name
lcl_dflags = ms_hspp_opts summary
prevailing_backend = backend (hsc_dflags hsc_env)
local_backend = backend lcl_dflags
bcknd = case (prevailing_backend,local_backend) of
(LLVM,NCG) -> NCG
(NCG,LLVM) -> LLVM
(NoBackend,b)
| backendProducesObject b -> b
(Interpreter,b)
| backendProducesObject b -> b
_ -> prevailing_backend
summary' = summary{ ms_hspp_opts = lcl_dflags { backend = bcknd } }
mb_old_iface
= case old_hmi of
Nothing -> Nothing
Just hm_info | isBootSummary summary == IsBoot -> Just iface
| mi_boot iface == NotBoot -> Just iface
| otherwise -> Nothing
where
iface = hm_iface hm_info
compile_it :: Maybe Linkable -> SourceModified -> IO HomeModInfo
compile_it mb_linkable src_modified =
compileOne' Nothing mHscMessage hsc_env summary' mod_index nmods
mb_old_iface mb_linkable src_modified
compile_it_discard_iface :: Maybe Linkable -> SourceModified
-> IO HomeModInfo
compile_it_discard_iface mb_linkable src_modified =
compileOne' Nothing mHscMessage hsc_env summary' mod_index nmods
Nothing mb_linkable src_modified
is_fake_linkable
| Just hmi <- old_hmi, Just l <- hm_linkable hmi =
null (linkableUnlinked l)
| otherwise =
False
implies False _ = True
implies True x = x
debug_trace n t = liftIO $ debugTraceMsg (hsc_logger hsc_env) (hsc_dflags hsc_env) n t
in
case () of
_
| is_stable_obj, Just hmi <- old_hmi -> do
debug_trace 5 (text "skipping stable obj mod:" <+> ppr this_mod_name)
return hmi
| is_stable_obj, isNothing old_hmi -> do
debug_trace 5 (text "compiling stable on-disk mod:" <+> ppr this_mod_name)
linkable <- liftIO $ findObjectLinkable this_mod obj_fn
(expectJust "upsweep1" mb_obj_date)
compile_it (Just linkable) SourceUnmodifiedAndStable
| not (backendProducesObject bcknd), is_stable_bco,
(bcknd /= NoBackend) `implies` not is_fake_linkable ->
ASSERT(isJust old_hmi)
let Just hmi = old_hmi in do
debug_trace 5 (text "skipping stable BCO mod:" <+> ppr this_mod_name)
return hmi
| not (backendProducesObject bcknd),
Just hmi <- old_hmi,
Just l <- hm_linkable hmi,
not (isObjectLinkable l),
(bcknd /= NoBackend) `implies` not is_fake_linkable,
linkableTime l >= ms_hs_date summary -> do
debug_trace 5 (text "compiling non-stable BCO mod:" <+> ppr this_mod_name)
compile_it (Just l) SourceUnmodified
| backendProducesObject bcknd,
Just obj_date <- mb_obj_date,
obj_date >= hs_date -> do
case old_hmi of
Just hmi
| Just l <- hm_linkable hmi,
isObjectLinkable l && linkableTime l == obj_date -> do
debug_trace 5 (text "compiling mod with new on-disk obj:" <+> ppr this_mod_name)
compile_it (Just l) SourceUnmodified
_otherwise -> do
debug_trace 5 (text "compiling mod with new on-disk obj2:" <+> ppr this_mod_name)
linkable <- liftIO $ findObjectLinkable this_mod obj_fn obj_date
compile_it_discard_iface (Just linkable) SourceUnmodified
| writeInterfaceOnlyMode lcl_dflags,
Just if_date <- mb_if_date,
if_date >= hs_date -> do
debug_trace 5 (text "skipping tc'd mod:" <+> ppr this_mod_name)
compile_it Nothing SourceUnmodified
_otherwise -> do
debug_trace 5 (text "compiling mod:" <+> ppr this_mod_name)
compile_it Nothing SourceModified
retainInTopLevelEnvs :: [ModuleName] -> HomePackageTable -> HomePackageTable
retainInTopLevelEnvs keep_these hpt
= listToHpt [ (mod, expectJust "retain" mb_mod_info)
| mod <- keep_these
, let mb_mod_info = lookupHpt hpt mod
, isJust mb_mod_info ]
reTypecheckLoop :: HscEnv -> ModSummary -> ModuleGraph -> IO HscEnv
reTypecheckLoop hsc_env ms graph
| Just loop <- getModLoop ms mss appearsAsBoot
, let non_boot = flip mapMaybe loop $ \case
InstantiationNode _ -> Nothing
ModuleNode ems -> do
let l = emsModSummary ems
guard $ not $ isBootSummary l == IsBoot && ms_mod l == ms_mod ms
pure l
= typecheckLoop (hsc_dflags hsc_env) hsc_env (map ms_mod_name non_boot)
| otherwise
= return hsc_env
where
mss = mgModSummaries' graph
appearsAsBoot = (`elemModuleSet` mgBootModules graph)
getModLoop
:: ModSummary
-> [ModuleGraphNode]
-> (Module -> Bool)
-> Maybe [ModuleGraphNode]
getModLoop ms graph appearsAsBoot
| isBootSummary ms == NotBoot
, appearsAsBoot this_mod
, let mss = reachableBackwards (ms_mod_name ms) graph
= Just mss
| otherwise
= Nothing
where
this_mod = ms_mod ms
typecheckLoop :: DynFlags -> HscEnv -> [ModuleName] -> IO HscEnv
typecheckLoop dflags hsc_env mods = do
debugTraceMsg logger dflags 2 $
text "Re-typechecking loop: " <> ppr mods
new_hpt <-
fixIO $ \new_hpt -> do
let new_hsc_env = hsc_env{ hsc_HPT = new_hpt }
mds <- initIfaceCheck (text "typecheckLoop") new_hsc_env $
mapM (typecheckIface . hm_iface) hmis
let new_hpt = addListToHpt old_hpt
(zip mods [ hmi{ hm_details = details }
| (hmi,details) <- zip hmis mds ])
return new_hpt
return hsc_env{ hsc_HPT = new_hpt }
where
logger = hsc_logger hsc_env
old_hpt = hsc_HPT hsc_env
hmis = map (expectJust "typecheckLoop" . lookupHpt old_hpt) mods
reachableBackwards :: ModuleName -> [ModuleGraphNode] -> [ModuleGraphNode]
reachableBackwards mod summaries
= [ node_payload node | node <- reachableG (transposeG graph) root ]
where
(graph, lookup_node) = moduleGraphNodes False summaries
root = expectJust "reachableBackwards" (lookup_node $ NodeKey_Module $ GWIB mod IsBoot)
topSortModuleGraph
:: Bool
-> ModuleGraph
-> Maybe ModuleName
-> [SCC ModuleGraphNode]
topSortModuleGraph drop_hs_boot_nodes module_graph mb_root_mod
= map (fmap summaryNodeSummary) $ stronglyConnCompG initial_graph
where
summaries = mgModSummaries' module_graph
(graph, lookup_node) =
moduleGraphNodes drop_hs_boot_nodes (reverse summaries)
initial_graph = case mb_root_mod of
Nothing -> graph
Just root_mod ->
let root | Just node <- lookup_node $ NodeKey_Module $ GWIB root_mod NotBoot
, graph `hasVertexG` node
= node
| otherwise
= throwGhcException (ProgramError "module does not exist")
in graphFromEdgedVerticesUniq (seq root (reachableG graph root))
type SummaryNode = Node Int ModuleGraphNode
summaryNodeKey :: SummaryNode -> Int
summaryNodeKey = node_key
summaryNodeSummary :: SummaryNode -> ModuleGraphNode
summaryNodeSummary = node_payload
unfilteredEdges :: Bool -> ModuleGraphNode -> [NodeKey]
unfilteredEdges drop_hs_boot_nodes = \case
InstantiationNode iuid ->
NodeKey_Module . flip GWIB NotBoot <$> uniqDSetToList (instUnitHoles iuid)
ModuleNode (ExtendedModSummary ms bds) ->
(NodeKey_Module . flip GWIB hs_boot_key . unLoc <$> ms_home_srcimps ms) ++
(NodeKey_Module . flip GWIB NotBoot . unLoc <$> ms_home_imps ms) ++
[ NodeKey_Module $ GWIB (ms_mod_name ms) IsBoot
| not $ drop_hs_boot_nodes || ms_hsc_src ms == HsBootFile
] ++
[ NodeKey_Unit inst_unit
| inst_unit <- bds
]
where
hs_boot_key | drop_hs_boot_nodes = NotBoot
| otherwise = IsBoot
moduleGraphNodes :: Bool -> [ModuleGraphNode]
-> (Graph SummaryNode, NodeKey -> Maybe SummaryNode)
moduleGraphNodes drop_hs_boot_nodes summaries =
(graphFromEdgedVerticesUniq nodes, lookup_node)
where
numbered_summaries = zip summaries [1..]
lookup_node :: NodeKey -> Maybe SummaryNode
lookup_node key = Map.lookup key (unNodeMap node_map)
lookup_key :: NodeKey -> Maybe Int
lookup_key = fmap summaryNodeKey . lookup_node
node_map :: NodeMap SummaryNode
node_map = NodeMap $
Map.fromList [ (mkHomeBuildModule s, node)
| node <- nodes
, let s = summaryNodeSummary node
]
nodes :: [SummaryNode]
nodes = [ DigraphNode s key $ out_edge_keys $ unfilteredEdges drop_hs_boot_nodes s
| (s, key) <- numbered_summaries
, case s of
InstantiationNode _ -> True
ModuleNode ems -> not $ isBootSummary (emsModSummary ems) == IsBoot && drop_hs_boot_nodes
]
out_edge_keys :: [NodeKey] -> [Int]
out_edge_keys = mapMaybe lookup_key
type ModNodeKey = ModuleNameWithIsBoot
newtype ModNodeMap a = ModNodeMap { unModNodeMap :: Map.Map ModNodeKey a }
deriving (Functor, Traversable, Foldable)
emptyModNodeMap :: ModNodeMap a
emptyModNodeMap = ModNodeMap Map.empty
modNodeMapInsert :: ModNodeKey -> a -> ModNodeMap a -> ModNodeMap a
modNodeMapInsert k v (ModNodeMap m) = ModNodeMap (Map.insert k v m)
modNodeMapElems :: ModNodeMap a -> [a]
modNodeMapElems (ModNodeMap m) = Map.elems m
modNodeMapLookup :: ModNodeKey -> ModNodeMap a -> Maybe a
modNodeMapLookup k (ModNodeMap m) = Map.lookup k m
data NodeKey = NodeKey_Unit !InstantiatedUnit | NodeKey_Module !ModNodeKey
deriving (Eq, Ord)
newtype NodeMap a = NodeMap { unNodeMap :: Map.Map NodeKey a }
deriving (Functor, Traversable, Foldable)
msKey :: ModSummary -> ModNodeKey
msKey = mkHomeBuildModule0
mkNodeKey :: ModuleGraphNode -> NodeKey
mkNodeKey = \case
InstantiationNode x -> NodeKey_Unit x
ModuleNode x -> NodeKey_Module $ mkHomeBuildModule0 (emsModSummary x)
pprNodeKey :: NodeKey -> SDoc
pprNodeKey (NodeKey_Unit iu) = ppr iu
pprNodeKey (NodeKey_Module mk) = ppr mk
mkNodeMap :: [ExtendedModSummary] -> ModNodeMap ExtendedModSummary
mkNodeMap summaries = ModNodeMap $ Map.fromList
[ (msKey $ emsModSummary s, s) | s <- summaries]
warnUnnecessarySourceImports :: GhcMonad m => [SCC ModSummary] -> m ()
warnUnnecessarySourceImports sccs = do
dflags <- getDynFlags
when (wopt Opt_WarnUnusedImports dflags)
(logWarnings (listToBag (concatMap (check . flattenSCC) sccs)))
where check ms =
let mods_in_this_cycle = map ms_mod_name ms in
[ warn i | m <- ms, i <- ms_home_srcimps m,
unLoc i `notElem` mods_in_this_cycle ]
warn :: Located ModuleName -> WarnMsg
warn (L loc mod) =
mkPlainMsgEnvelope loc
(text "Warning: {-# SOURCE #-} unnecessary in import of "
<+> quotes (ppr mod))
downsweep :: HscEnv
-> [ExtendedModSummary]
-> [ModuleName]
-> Bool
-> IO [Either ErrorMessages ExtendedModSummary]
downsweep hsc_env old_summaries excl_mods allow_dup_roots
= do
rootSummaries <- mapM getRootSummary roots
let (errs, rootSummariesOk) = partitionEithers rootSummaries
root_map = mkRootMap rootSummariesOk
checkDuplicates root_map
map0 <- loop (concatMap calcDeps rootSummariesOk) root_map
let default_backend = platformDefaultBackend (targetPlatform dflags)
let home_unit = hsc_home_unit hsc_env
let tmpfs = hsc_tmpfs hsc_env
map1 <- case backend dflags of
NoBackend -> enableCodeGenForTH logger tmpfs home_unit default_backend map0
_ -> return map0
if null errs
then pure $ concat $ modNodeMapElems map1
else pure $ map Left errs
where
calcDeps (ExtendedModSummary ms _bkp_deps) = msDeps ms
dflags = hsc_dflags hsc_env
logger = hsc_logger hsc_env
roots = hsc_targets hsc_env
old_summary_map :: ModNodeMap ExtendedModSummary
old_summary_map = mkNodeMap old_summaries
getRootSummary :: Target -> IO (Either ErrorMessages ExtendedModSummary)
getRootSummary (Target (TargetFile file mb_phase) obj_allowed maybe_buf)
= do exists <- liftIO $ doesFileExist file
if exists || isJust maybe_buf
then summariseFile hsc_env old_summaries file mb_phase
obj_allowed maybe_buf
else return $ Left $ unitBag $ mkPlainMsgEnvelope noSrcSpan $
text "can't find file:" <+> text file
getRootSummary (Target (TargetModule modl) obj_allowed maybe_buf)
= do maybe_summary <- summariseModule hsc_env old_summary_map NotBoot
(L rootLoc modl) obj_allowed
maybe_buf excl_mods
case maybe_summary of
Nothing -> return $ Left $ moduleNotFoundErr modl
Just s -> return s
rootLoc = mkGeneralSrcSpan (fsLit "<command line>")
checkDuplicates
:: ModNodeMap
[Either ErrorMessages
ExtendedModSummary]
-> IO ()
checkDuplicates root_map
| allow_dup_roots = return ()
| null dup_roots = return ()
| otherwise = liftIO $ multiRootsErr (emsModSummary <$> head dup_roots)
where
dup_roots :: [[ExtendedModSummary]]
dup_roots = filterOut isSingleton $ map rights $ modNodeMapElems root_map
loop :: [GenWithIsBoot (Located ModuleName)]
-> ModNodeMap [Either ErrorMessages ExtendedModSummary]
-> IO (ModNodeMap [Either ErrorMessages ExtendedModSummary])
loop [] done = return done
loop (s : ss) done
| Just summs <- modNodeMapLookup key done
= if isSingleton summs then
loop ss done
else
do { multiRootsErr (emsModSummary <$> rights summs)
; return (ModNodeMap Map.empty)
}
| otherwise
= do mb_s <- summariseModule hsc_env old_summary_map
is_boot wanted_mod True
Nothing excl_mods
case mb_s of
Nothing -> loop ss done
Just (Left e) -> loop ss (modNodeMapInsert key [Left e] done)
Just (Right s)-> do
new_map <-
loop (calcDeps s) (modNodeMapInsert key [Right s] done)
loop ss new_map
where
GWIB { gwib_mod = L loc mod, gwib_isBoot = is_boot } = s
wanted_mod = L loc mod
key = GWIB
{ gwib_mod = unLoc wanted_mod
, gwib_isBoot = is_boot
}
enableCodeGenForTH
:: Logger
-> TmpFs
-> HomeUnit
-> Backend
-> ModNodeMap [Either ErrorMessages ExtendedModSummary]
-> IO (ModNodeMap [Either ErrorMessages ExtendedModSummary])
enableCodeGenForTH logger tmpfs home_unit =
enableCodeGenWhen logger tmpfs condition should_modify TFL_CurrentModule TFL_GhcSession
where
condition = isTemplateHaskellOrQQNonBoot
should_modify (ModSummary { ms_hspp_opts = dflags }) =
backend dflags == NoBackend &&
isHomeUnitDefinite home_unit
enableCodeGenWhen
:: Logger
-> TmpFs
-> (ModSummary -> Bool)
-> (ModSummary -> Bool)
-> TempFileLifetime
-> TempFileLifetime
-> Backend
-> ModNodeMap [Either ErrorMessages ExtendedModSummary]
-> IO (ModNodeMap [Either ErrorMessages ExtendedModSummary])
enableCodeGenWhen logger tmpfs condition should_modify staticLife dynLife bcknd nodemap =
traverse (traverse (traverse enable_code_gen)) nodemap
where
enable_code_gen :: ExtendedModSummary -> IO ExtendedModSummary
enable_code_gen (ExtendedModSummary ms bkp_deps)
| ModSummary
{ ms_mod = ms_mod
, ms_location = ms_location
, ms_hsc_src = HsSrcFile
, ms_hspp_opts = dflags
} <- ms
, should_modify ms
, ms_mod `Set.member` needs_codegen_set
= do
let new_temp_file suf dynsuf = do
tn <- newTempName logger tmpfs dflags staticLife suf
let dyn_tn = tn -<.> dynsuf
addFilesToClean tmpfs dynLife [dyn_tn]
return tn
(hi_file, o_file) <-
if gopt Opt_WriteInterface dflags
then return (ml_hi_file ms_location, ml_obj_file ms_location)
else (,) <$> (new_temp_file (hiSuf_ dflags) (dynHiSuf_ dflags))
<*> (new_temp_file (objectSuf_ dflags) (dynObjectSuf_ dflags))
let ms' = ms
{ ms_location =
ms_location {ml_hi_file = hi_file, ml_obj_file = o_file}
, ms_hspp_opts = updOptLevel 0 $ dflags {backend = bcknd}
}
pure (ExtendedModSummary ms' bkp_deps)
| otherwise = return (ExtendedModSummary ms bkp_deps)
needs_codegen_set = transitive_deps_set
[ ms
| mss <- modNodeMapElems nodemap
, Right (ExtendedModSummary { emsModSummary = ms }) <- mss
, condition ms
]
transitive_deps_set :: [ModSummary] -> Set.Set Module
transitive_deps_set modSums = foldl' go Set.empty modSums
where
go marked_mods ms@ModSummary{ms_mod}
| ms_mod `Set.member` marked_mods = marked_mods
| otherwise =
let deps =
[ dep_ms
| dep <- msDeps ms
, NotBoot == gwib_isBoot dep
, dep_ms_0 <- toList $ modNodeMapLookup (unLoc <$> dep) nodemap
, dep_ms_1 <- toList $ dep_ms_0
, (ExtendedModSummary { emsModSummary = dep_ms }) <- toList $ dep_ms_1
]
new_marked_mods = Set.insert ms_mod marked_mods
in foldl' go new_marked_mods deps
mkRootMap
:: [ExtendedModSummary]
-> ModNodeMap [Either ErrorMessages ExtendedModSummary]
mkRootMap summaries = ModNodeMap $ Map.insertListWith
(flip (++))
[ (msKey $ emsModSummary s, [Right s]) | s <- summaries ]
Map.empty
msDeps :: ModSummary -> [GenWithIsBoot (Located ModuleName)]
msDeps s = [ d
| m <- ms_home_srcimps s
, d <- [ GWIB { gwib_mod = m, gwib_isBoot = IsBoot }
, GWIB { gwib_mod = m, gwib_isBoot = NotBoot }
]
]
++ [ GWIB { gwib_mod = m, gwib_isBoot = NotBoot }
| m <- ms_home_imps s
]
summariseFile
:: HscEnv
-> [ExtendedModSummary]
-> FilePath
-> Maybe Phase
-> Bool
-> Maybe (StringBuffer,UTCTime)
-> IO (Either ErrorMessages ExtendedModSummary)
summariseFile hsc_env old_summaries src_fn mb_phase obj_allowed maybe_buf
| Just old_summary <- findSummaryBySourceFile old_summaries src_fn
= do
let location = ms_location $ emsModSummary old_summary
dflags = hsc_dflags hsc_env
src_timestamp <- get_src_timestamp
checkSummaryTimestamp
hsc_env dflags obj_allowed NotBoot (new_summary src_fn)
old_summary location src_timestamp
| otherwise
= do src_timestamp <- get_src_timestamp
new_summary src_fn src_timestamp
where
get_src_timestamp = case maybe_buf of
Just (_,t) -> return t
Nothing -> liftIO $ getModificationUTCTime src_fn
new_summary src_fn src_timestamp = runExceptT $ do
preimps@PreprocessedImports {..}
<- getPreprocessedImports hsc_env src_fn mb_phase maybe_buf
location <- liftIO $ mkHomeModLocation (hsc_dflags hsc_env) pi_mod_name src_fn
mod <- liftIO $ addHomeModuleToFinder hsc_env pi_mod_name location
liftIO $ makeNewModSummary hsc_env $ MakeNewModSummary
{ nms_src_fn = src_fn
, nms_src_timestamp = src_timestamp
, nms_is_boot = NotBoot
, nms_hsc_src =
if isHaskellSigFilename src_fn
then HsigFile
else HsSrcFile
, nms_location = location
, nms_mod = mod
, nms_obj_allowed = obj_allowed
, nms_preimps = preimps
}
findSummaryBySourceFile :: [ExtendedModSummary] -> FilePath -> Maybe ExtendedModSummary
findSummaryBySourceFile summaries file = case
[ ms
| ms <- summaries
, HsSrcFile <- [ms_hsc_src $ emsModSummary ms]
, let derived_file = ml_hs_file $ ms_location $ emsModSummary ms
, expectJust "findSummaryBySourceFile" derived_file == file
]
of
[] -> Nothing
(x:_) -> Just x
checkSummaryTimestamp
:: HscEnv -> DynFlags -> Bool -> IsBootInterface
-> (UTCTime -> IO (Either e ExtendedModSummary))
-> ExtendedModSummary -> ModLocation -> UTCTime
-> IO (Either e ExtendedModSummary)
checkSummaryTimestamp
hsc_env dflags obj_allowed is_boot new_summary
(ExtendedModSummary { emsModSummary = old_summary, emsInstantiatedUnits = bkp_deps})
location src_timestamp
| ms_hs_date old_summary == src_timestamp &&
not (gopt Opt_ForceRecomp (hsc_dflags hsc_env)) = do
obj_timestamp <-
if backendProducesObject (backend (hsc_dflags hsc_env))
|| obj_allowed
then liftIO $ getObjTimestamp location is_boot
else return Nothing
_ <- addHomeModuleToFinder hsc_env
(moduleName (ms_mod old_summary)) location
hi_timestamp <- maybeGetIfaceDate dflags location
hie_timestamp <- modificationTimeIfExists (ml_hie_file location)
return $ Right
( ExtendedModSummary { emsModSummary = old_summary
{ ms_obj_date = obj_timestamp
, ms_iface_date = hi_timestamp
, ms_hie_date = hie_timestamp
}
, emsInstantiatedUnits = bkp_deps
}
)
| otherwise =
new_summary src_timestamp
summariseModule
:: HscEnv
-> ModNodeMap ExtendedModSummary
-> IsBootInterface
-> Located ModuleName
-> Bool
-> Maybe (StringBuffer, UTCTime)
-> [ModuleName]
-> IO (Maybe (Either ErrorMessages ExtendedModSummary))
summariseModule hsc_env old_summary_map is_boot (L loc wanted_mod)
obj_allowed maybe_buf excl_mods
| wanted_mod `elem` excl_mods
= return Nothing
| Just old_summary <- modNodeMapLookup
(GWIB { gwib_mod = wanted_mod, gwib_isBoot = is_boot })
old_summary_map
= do
let location = ms_location $ emsModSummary old_summary
src_fn = expectJust "summariseModule" (ml_hs_file location)
case maybe_buf of
Just (_,t) ->
Just <$> check_timestamp old_summary location src_fn t
Nothing -> do
m <- tryIO (getModificationUTCTime src_fn)
case m of
Right t ->
Just <$> check_timestamp old_summary location src_fn t
Left e | isDoesNotExistError e -> find_it
| otherwise -> ioError e
| otherwise = find_it
where
dflags = hsc_dflags hsc_env
home_unit = hsc_home_unit hsc_env
check_timestamp old_summary location src_fn =
checkSummaryTimestamp
hsc_env dflags obj_allowed is_boot
(new_summary location (ms_mod $ emsModSummary old_summary) src_fn)
old_summary location
find_it = do
found <- findImportedModule hsc_env wanted_mod Nothing
case found of
Found location mod
| isJust (ml_hs_file location) ->
Just <$> just_found location mod
_ -> return Nothing
just_found location mod = do
let location' = case is_boot of
IsBoot -> addBootSuffixLocn location
NotBoot -> location
src_fn = expectJust "summarise2" (ml_hs_file location')
maybe_t <- modificationTimeIfExists src_fn
case maybe_t of
Nothing -> return $ Left $ noHsFileErr loc src_fn
Just t -> new_summary location' mod src_fn t
new_summary location mod src_fn src_timestamp
= runExceptT $ do
preimps@PreprocessedImports {..}
<- getPreprocessedImports hsc_env src_fn Nothing maybe_buf
let hsc_src
| is_boot == IsBoot = HsBootFile
| isHaskellSigFilename src_fn = HsigFile
| otherwise = HsSrcFile
when (pi_mod_name /= wanted_mod) $
throwE $ unitBag $ mkPlainMsgEnvelope pi_mod_name_loc $
text "File name does not match module name:"
$$ text "Saw:" <+> quotes (ppr pi_mod_name)
$$ text "Expected:" <+> quotes (ppr wanted_mod)
when (hsc_src == HsigFile && isNothing (lookup pi_mod_name (homeUnitInstantiations home_unit))) $
let suggested_instantiated_with =
hcat (punctuate comma $
[ ppr k <> text "=" <> ppr v
| (k,v) <- ((pi_mod_name, mkHoleModule pi_mod_name)
: homeUnitInstantiations home_unit)
])
in throwE $ unitBag $ mkPlainMsgEnvelope pi_mod_name_loc $
text "Unexpected signature:" <+> quotes (ppr pi_mod_name)
$$ if gopt Opt_BuildingCabalPackage dflags
then parens (text "Try adding" <+> quotes (ppr pi_mod_name)
<+> text "to the"
<+> quotes (text "signatures")
<+> text "field in your Cabal file.")
else parens (text "Try passing -instantiated-with=\"" <>
suggested_instantiated_with <> text "\"" $$
text "replacing <" <> ppr pi_mod_name <> text "> as necessary.")
liftIO $ makeNewModSummary hsc_env $ MakeNewModSummary
{ nms_src_fn = src_fn
, nms_src_timestamp = src_timestamp
, nms_is_boot = is_boot
, nms_hsc_src = hsc_src
, nms_location = location
, nms_mod = mod
, nms_obj_allowed = obj_allowed
, nms_preimps = preimps
}
data MakeNewModSummary
= MakeNewModSummary
{ nms_src_fn :: FilePath
, nms_src_timestamp :: UTCTime
, nms_is_boot :: IsBootInterface
, nms_hsc_src :: HscSource
, nms_location :: ModLocation
, nms_mod :: Module
, nms_obj_allowed :: Bool
, nms_preimps :: PreprocessedImports
}
makeNewModSummary :: HscEnv -> MakeNewModSummary -> IO ExtendedModSummary
makeNewModSummary hsc_env MakeNewModSummary{..} = do
let PreprocessedImports{..} = nms_preimps
let dflags = hsc_dflags hsc_env
obj_timestamp <- liftIO $
if backendProducesObject (backend dflags)
|| nms_obj_allowed
then getObjTimestamp nms_location nms_is_boot
else return Nothing
hi_timestamp <- maybeGetIfaceDate dflags nms_location
hie_timestamp <- modificationTimeIfExists (ml_hie_file nms_location)
extra_sig_imports <- findExtraSigImports hsc_env nms_hsc_src pi_mod_name
(implicit_sigs, inst_deps) <- implicitRequirementsShallow hsc_env pi_theimps
return $ ExtendedModSummary
{ emsModSummary =
ModSummary
{ ms_mod = nms_mod
, ms_hsc_src = nms_hsc_src
, ms_location = nms_location
, ms_hspp_file = pi_hspp_fn
, ms_hspp_opts = pi_local_dflags
, ms_hspp_buf = Just pi_hspp_buf
, ms_parsed_mod = Nothing
, ms_srcimps = pi_srcimps
, ms_textual_imps =
pi_theimps ++
extra_sig_imports ++
((,) Nothing . noLoc <$> implicit_sigs)
, ms_hs_date = nms_src_timestamp
, ms_iface_date = hi_timestamp
, ms_hie_date = hie_timestamp
, ms_obj_date = obj_timestamp
}
, emsInstantiatedUnits = inst_deps
}
getObjTimestamp :: ModLocation -> IsBootInterface -> IO (Maybe UTCTime)
getObjTimestamp location is_boot
= case is_boot of
IsBoot -> return Nothing
NotBoot -> modificationTimeIfExists (ml_obj_file location)
data PreprocessedImports
= PreprocessedImports
{ pi_local_dflags :: DynFlags
, pi_srcimps :: [(Maybe FastString, Located ModuleName)]
, pi_theimps :: [(Maybe FastString, Located ModuleName)]
, pi_hspp_fn :: FilePath
, pi_hspp_buf :: StringBuffer
, pi_mod_name_loc :: SrcSpan
, pi_mod_name :: ModuleName
}
getPreprocessedImports
:: HscEnv
-> FilePath
-> Maybe Phase
-> Maybe (StringBuffer, UTCTime)
-> ExceptT ErrorMessages IO PreprocessedImports
getPreprocessedImports hsc_env src_fn mb_phase maybe_buf = do
(pi_local_dflags, pi_hspp_fn)
<- ExceptT $ preprocess hsc_env src_fn (fst <$> maybe_buf) mb_phase
pi_hspp_buf <- liftIO $ hGetStringBuffer pi_hspp_fn
(pi_srcimps, pi_theimps, L pi_mod_name_loc pi_mod_name)
<- ExceptT $ do
let imp_prelude = xopt LangExt.ImplicitPrelude pi_local_dflags
popts = initParserOpts pi_local_dflags
mimps <- getImports popts imp_prelude pi_hspp_buf pi_hspp_fn src_fn
return (first (fmap pprError) mimps)
return PreprocessedImports {..}
withDeferredDiagnostics :: GhcMonad m => m a -> m a
withDeferredDiagnostics f = do
dflags <- getDynFlags
if not $ gopt Opt_DeferDiagnostics dflags
then f
else do
warnings <- liftIO $ newIORef []
errors <- liftIO $ newIORef []
fatals <- liftIO $ newIORef []
logger <- getLogger
let deferDiagnostics _dflags !reason !severity !srcSpan !msg = do
let action = putLogMsg logger dflags reason severity srcSpan msg
case severity of
SevWarning -> atomicModifyIORef' warnings $ \i -> (action: i, ())
SevError -> atomicModifyIORef' errors $ \i -> (action: i, ())
SevFatal -> atomicModifyIORef' fatals $ \i -> (action: i, ())
_ -> action
printDeferredDiagnostics = liftIO $
forM_ [warnings, errors, fatals] $ \ref -> do
actions <- atomicModifyIORef' ref $ \i -> ([], i)
sequence_ $ reverse actions
MC.bracket
(pushLogHookM (const deferDiagnostics))
(\_ -> popLogHookM >> printDeferredDiagnostics)
(\_ -> f)
noModError :: HscEnv -> SrcSpan -> ModuleName -> FindResult -> MsgEnvelope DecoratedSDoc
noModError hsc_env loc wanted_mod err
= mkPlainMsgEnvelope loc $ cannotFindModule hsc_env wanted_mod err
noHsFileErr :: SrcSpan -> String -> ErrorMessages
noHsFileErr loc path
= unitBag $ mkPlainMsgEnvelope loc $ text "Can't find" <+> text path
moduleNotFoundErr :: ModuleName -> ErrorMessages
moduleNotFoundErr mod
= unitBag $ mkPlainMsgEnvelope noSrcSpan $
text "module" <+> quotes (ppr mod) <+> text "cannot be found locally"
multiRootsErr :: [ModSummary] -> IO ()
multiRootsErr [] = panic "multiRootsErr"
multiRootsErr summs@(summ1:_)
= throwOneError $ mkPlainMsgEnvelope noSrcSpan $
text "module" <+> quotes (ppr mod) <+>
text "is defined in multiple files:" <+>
sep (map text files)
where
mod = ms_mod summ1
files = map (expectJust "checkDup" . ml_hs_file . ms_location) summs
keepGoingPruneErr :: [NodeKey] -> SDoc
keepGoingPruneErr ms
= vcat (( text "-fkeep-going in use, removing the following" <+>
text "dependencies and continuing:"):
map (nest 6 . pprNodeKey) ms )
cyclicModuleErr :: [ModuleGraphNode] -> SDoc
cyclicModuleErr mss
= ASSERT( not (null mss) )
case findCycle graph of
Nothing -> text "Unexpected non-cycle" <+> ppr mss
Just path0 -> vcat
[ case partitionNodes path0 of
([],_) -> text "Module imports form a cycle:"
(_,[]) -> text "Module instantiations form a cycle:"
_ -> text "Module imports and instantiations form a cycle:"
, nest 2 (show_path path0)]
where
graph :: [Node NodeKey ModuleGraphNode]
graph =
[ DigraphNode
{ node_payload = ms
, node_key = mkNodeKey ms
, node_dependencies = get_deps ms
}
| ms <- mss
]
get_deps :: ModuleGraphNode -> [NodeKey]
get_deps = \case
InstantiationNode iuid ->
[ NodeKey_Module $ GWIB { gwib_mod = hole, gwib_isBoot = NotBoot }
| hole <- uniqDSetToList $ instUnitHoles iuid
]
ModuleNode (ExtendedModSummary ms bds) ->
[ NodeKey_Module $ GWIB { gwib_mod = unLoc m, gwib_isBoot = IsBoot }
| m <- ms_home_srcimps ms ] ++
[ NodeKey_Module $ GWIB { gwib_mod = unLoc m, gwib_isBoot = NotBoot }
| m <- ms_home_imps ms ] ++
[ NodeKey_Unit inst_unit
| inst_unit <- bds
]
show_path :: [ModuleGraphNode] -> SDoc
show_path [] = panic "show_path"
show_path [m] = ppr_node m <+> text "imports itself"
show_path (m1:m2:ms) = vcat ( nest 6 (ppr_node m1)
: nest 6 (text "imports" <+> ppr_node m2)
: go ms )
where
go [] = [text "which imports" <+> ppr_node m1]
go (m:ms) = (text "which imports" <+> ppr_node m) : go ms
ppr_node :: ModuleGraphNode -> SDoc
ppr_node (ModuleNode m) = text "module" <+> ppr_ms (emsModSummary m)
ppr_node (InstantiationNode u) = text "instantiated unit" <+> ppr u
ppr_ms :: ModSummary -> SDoc
ppr_ms ms = quotes (ppr (moduleName (ms_mod ms))) <+>
(parens (text (msHsFilePath ms)))