2 % (c) The University of Glasgow, 2000
4 \section[CompManager]{The Compilation Manager}
7 module CompManager ( cmInit, cmLoadModule,
9 CmState, emptyCmState -- abstract
13 #include "HsVersions.h"
16 import Maybe ( catMaybes, maybeToList, fromMaybe )
18 import FiniteMap ( emptyFM, filterFM, lookupFM, addToFM )
19 import Digraph ( SCC(..), stronglyConnComp )
20 import Panic ( panic )
22 import CmStaticInfo ( PCI(..), mkPCI, Package(..) )
23 import Finder ( Finder, newFinder,
24 ModName, ml_modname, isPackageLoc,
26 import CmSummarise ( summarise, ModSummary(..),
27 mi_name, ms_get_imports,
28 name_of_summary, deps_of_summary )
29 --import CmCompile ( PCS, emptyPCS, HST, HIT, CompResult(..), cmCompile )
30 import CmLink ( PLS, emptyPLS, Linkable(..),
32 filterModuleLinkables, modname_of_linkable,
34 import InterpSyn ( HValue )
37 cmInit :: String{-temp debugging hack-}
40 cmInit path raw_package_info
41 = emptyCmState path raw_package_info
46 -> IO (CmState, Either [SDoc] HValue)
47 cmGetExpr cmstate modhdl expr
48 = return (error "cmGetExpr:unimp")
50 cmRunExpr :: HValue -> IO ()
52 = return (error "cmRunExpr:unimp")
54 type ModHandle = String -- ToDo: do better?
57 -- Persistent state just for CM, excluding link & compile subsystems
58 data PersistentCMState
60 hst :: HomeSymbolTable, -- home symbol table
61 hit :: HomeIfaceTable, -- home interface table
62 ui :: UnlinkedImages, -- the unlinked images
63 mg :: ModuleGraph -- the module graph
66 emptyPCMS :: PersistentCMState
67 emptyPCMS = PersistentCMState
69 hst = emptyHST, hit = emptyHIT,
70 ui = emptyUI, mg = emptyMG }
72 emptyHIT :: HomeIfaceTable
74 emptyHST :: HomeSymbolTable
79 -- Persistent state for the entire system
82 pcms :: PersistentCMState, -- CM's persistent state
83 pcs :: PersistentCompilerState, -- compile's persistent state
84 pls :: PersistentLinkerState, -- link's persistent state
85 pci :: PackageConfigInfo, -- package config info, never changes
86 finder :: Finder -- the module finder
89 emptyCmState :: String{-temp debugging hack-}
90 -> [Package] -> IO CmState
91 emptyCmState path_TMP_DEBUGGING_HACK raw_package_info
92 = do let pcms = emptyPCMS
95 pci <- mkPCI raw_package_info
96 finder <- newFinder path_TMP_DEBUGGING_HACK pci
97 return (CmState { pcms = pcms,
104 type UnlinkedImage = [Linkable] -- the unlinked images (should be a set, really)
105 emptyUI :: UnlinkedImage
108 type ModuleGraph = [SCC ModSummary] -- the module graph, topologically sorted
109 emptyMG :: ModuleGraph
114 The real business of the compilation manager: given a system state and
115 a module name, try and bring the module up to date, probably changing
116 the system state at the same time.
119 cmLoadModule :: CmState
121 -> IO (CmState, Either [SDoc] ModHandle)
123 cmLoadModule cmstate1 modname
124 = do -- version 1's are the original, before downsweep
125 let pcms1 = pcms cmstate1
126 let pls1 = pls cmstate1
127 let pcs1 = pcs cmstate1
132 -- these aren't numbered since they don't change
133 let pcii = pci cmstate1
134 let finderr = finder cmstate1
136 -- do the downsweep to reestablish the module graph
137 -- then generate version 2's by removing from HIT,HST,UI any
138 -- modules in the old MG which are not in the new one.
140 -- TODO: call newFinder to reestablish home module cache?!
142 putStr "cmLoadModule: downsweep begins\n"
143 mg2unsorted <- downsweep modname finderr
144 putStrLn (showSDoc (vcat (map ppr mg2unsorted)))
146 let modnames1 = map name_of_summary (flattenSCCs mg1)
147 let modnames2 = map name_of_summary mg2unsorted
148 let mods_to_zap = filter (`notElem` modnames2) modnames1
150 let (hst2, hit2, ui2)
151 = filterTopLevelEnvs (`notElem` mods_to_zap)
154 let mg2 = topological_sort mg2unsorted
156 putStrLn "after tsort:\n"
157 putStrLn (showSDoc (vcat (map ppr (flattenSCCs mg2))))
159 -- Now do the upsweep, calling compile for each module in
160 -- turn. Final result is version 3 of everything.
162 let threaded2 = ModThreaded pcs1 hst2 hit2
164 (threaded3, sccOKs, newLis, errs, warns)
165 <- upsweep_sccs finderr threaded2 [] [] [] [] mg2
167 let ui3 = add_to_ui ui2 newLis
168 let (ModThreaded pcs3 hst3 hit3) = threaded3
170 -- Try and do linking in some form, depending on whether the
171 -- upsweep was completely or only partially successful.
176 do let mods_to_relink = upwards_closure mg2
177 (map modname_of_linkable newLis)
178 let pkg_linkables = find_pkg_linkables_for pcii mg2 mods_to_relink
179 putStrLn ("needed package modules =\n"
180 ++ showSDoc (vcat (map ppr pkg_linkables)))
181 let sccs_to_relink = group_uis ui3 mg2 mods_to_relink
182 let all_to_relink = map AcyclicSCC pkg_linkables
184 linkresult <- link pcii all_to_relink pls1
187 -> panic "cmLoadModule: link failed (1)"
190 = PCMS { hst=hst3, hit=hit3, ui=ui3, mg=mg2 }
192 = CmState { pcms=pcms3, pcs=pcs3, pls=pls3,
193 pci=pcii, finder=finderr }
194 return (cmstate3, Right modname)
197 do let mods_to_relink = downwards_closure mg2
198 (map name_of_summary (flattenSCCs sccOKs))
199 let pkg_linkables = find_pkg_linkables_for pcii mg2 mods_to_relink
200 let sccs_to_relink = group_uis ui3 mg2 mods_to_relink
201 let all_to_relink = map AcyclicSCC pkg_linkables
203 linkresult <- link pcii all_to_relink pls1
204 let (hst4, hit4, ui4)
205 = filterTopLevelEnvs (`notElem` mods_to_relink)
209 -> panic "cmLoadModule: link failed (2)"
212 = PCMS { hst=hst4, hit=hit4, ui=ui4, mg=mg2 }
214 = CmState { pcms=pcms4, pcs=pcs3, pls=pls4,
215 pci=pcii, finder=finderr }
216 return (cmstate4, Right modname)
218 -- Given a (home) module graph and a bunch of names of (home) modules
219 -- within that graph, return the names of any packages needed by the
220 -- named modules. Do this by looking at their imports. Assumes, and
221 -- checks, that all of "mods" are mentioned in "mg".
223 -- Then, having found the packages directly needed by "mods",
224 -- (1) round up, by looking in "pci", all packages they directly or
225 -- indirectly depend on, and (2) put these packages in topological
226 -- order, since that's important for some linkers. Since cycles in
227 -- the package dependency graph aren't allowed, we can just return
228 -- the list of (package) linkables, rather than a list of SCCs.
229 find_pkg_linkables_for :: PCI -> [SCC ModSummary] -> [ModName] -> [Linkable]
230 find_pkg_linkables_for pcii mg mods
231 = let mg_summaries = flattenSCCs mg
232 mg_names = map name_of_summary mg_summaries
234 if not (all (`elem` mg_names) mods)
235 then panic "find_packages_for"
239 [deps_of_summary summ
240 | summ <- mg_summaries, name_of_summary summ `elem` mods]
241 imports_not_in_home -- imports which must be from packages
242 = nub (filter (`notElem` mg_names) all_imports)
243 mod_tab :: [(ModName, PkgName, Path)]
244 mod_tab = module_table pcii
245 home_pkgs_needed -- the packages directly needed by the home modules
246 = nub [pkg_nm | (mod_nm, pkg_nm, path) <- mod_tab,
247 mod_nm `elem` imports_not_in_home]
249 -- Discover the package dependency graph, and use it to find the
250 -- transitive closure of all the needed packages
251 pkg_depend_graph :: [(PkgName,[PkgName])]
252 pkg_depend_graph = map (\raw -> (name raw, package_deps raw))
253 (raw_package_info pcii)
255 all_pkgs_needed = simple_transitive_closure
256 pkg_depend_graph home_pkgs_needed
258 -- Make a graph, in the style which Digraph.stronglyConnComp expects,
259 -- containing entries only for the needed packages.
262 [if srcP `elem` all_pkgs_needed
263 then [(srcP, srcP, dstsP)]
265 | (srcP, dstsP) <- pkg_depend_graph]
266 tsorted = flattenSCCs (stronglyConnComp needed_graph)
271 simple_transitive_closure :: Eq a => [(a,[a])] -> [a] -> [a]
272 simple_transitive_closure graph set
273 = let set2 = nub (concatMap dsts set ++ set)
274 dsts node = fromMaybe [] (lookup node graph)
276 if length set == length set2
278 else simple_transitive_closure graph set2
281 flattenSCCs :: [SCC a] -> [a]
282 flattenSCCs = concatMap flatten
284 flatten (AcyclicSCC v) = [v]
285 flatten (CyclicSCC vs) = vs
287 -- For each module in mods_to_group, extract the relevant linkable
288 -- out of UI, and arrange these linkables in SCCs as defined by modGraph.
289 -- All this is so that we can pass SCCified Linkable groups to the
290 -- linker. A constraint that should be recorded somewhere is that
291 -- all sccs should either be all-interpreted or all-object, not a mixture.
292 group_uis :: UI -> [SCC ModSummary] -> [ModName] -> [SCC Linkable]
293 group_uis ui modGraph mods_to_group
294 = map extract (cleanup (fishOut modGraph mods_to_group))
296 fishOut :: [SCC ModSummary] -> [ModName] -> [(Bool,[ModName])]
299 | otherwise = panic "group_uis: modnames not in modgraph"
300 fishOut ((AcyclicSCC ms):sccs) unused
301 = case split (== (name_of_summary ms)) unused of
302 (eq, not_eq) -> (False, eq) : fishOut sccs not_eq
303 fishOut ((CyclicSCC mss):sccs) unused
304 = case split (`elem` (map name_of_summary mss)) unused of
305 (eq, not_eq) -> (True, eq) : fishOut sccs not_eq
307 cleanup :: [(Bool,[ModName])] -> [SCC ModName]
309 cleanup ((isRec,names):rest)
310 | null names = cleanup rest
311 | isRec = CyclicSCC names : cleanup rest
312 | not isRec = case names of [name] -> AcyclicSCC name : cleanup rest
313 other -> panic "group_uis(cleanup)"
315 extract :: SCC ModName -> SCC Linkable
316 extract (AcyclicSCC nm) = AcyclicSCC (getLi nm)
317 extract (CyclicSCC nms) = CyclicSCC (map getLi nms)
319 getLi nm = case [li | li <- ui, not (is_package_linkable li),
320 nm == modname_of_linkable li] of
322 other -> panic "group_uis:getLi"
324 split f xs = (filter f xs, filter (not.f) xs)
327 -- Add the given (LM-form) Linkables to the UI, overwriting previous
328 -- versions if they exist.
329 add_to_ui :: UI -> [Linkable] -> UI
333 add1 :: Linkable -> UI -> UI
335 = li : filter (\li2 -> not (for_same_module li li2)) ui
337 for_same_module :: Linkable -> Linkable -> Bool
338 for_same_module li1 li2
339 = not (is_package_linkable li1)
340 && not (is_package_linkable li2)
341 && modname_of_linkable li1 == modname_of_linkable li2
344 -- Compute upwards and downwards closures in the (home-) module graph.
346 upwards_closure :: [SCC ModSummary] -> [ModName] -> [ModName]
348 upwards_closure = up_down_closure True
349 downwards_closure = up_down_closure False
351 up_down_closure :: Bool -> [SCC ModSummary] -> [ModName] -> [ModName]
352 up_down_closure up modGraph roots
353 = let mgFlat = flattenSCCs modGraph
354 nodes = map name_of_summary mgFlat
356 fwdEdges, backEdges :: [(ModName, [ModName])]
357 -- have an entry for each mod in mgFlat, and do not
358 -- mention edges leading out of the home package
361 backEdges -- Only calculated if needed, which is just as well!
362 = [(n, [m | (m, m_imports) <- fwdEdges, n `elem` m_imports])
363 | (n, n_imports) <- fwdEdges]
366 = (name_of_summary summ,
367 -- ignore imports not from the home package
368 filter (`elem` nodes) (deps_of_summary summ))
370 simple_transitive_closure
371 (if up then backEdges else fwdEdges) (nub roots)
374 data ModThreaded -- stuff threaded through individual module compilations
375 = ModThreaded PCS HST HIT
377 -- Compile multiple SCCs, stopping as soon as an error appears
378 upsweep_sccs :: Finder -- the finder
379 -> ModThreaded -- PCS & HST & HIT
380 -> [SCC ModSummary] -- accum: SCCs which succeeded
381 -> [Linkable] -- accum: new Linkables
382 -> [SDoc] -- accum: error messages
383 -> [SDoc] -- accum: warnings
384 -> [SCC ModSummary] -- SCCs to do (the worklist)
385 -- ...... RETURNING ......
387 [SCC ModSummary], -- SCCs which succeeded
388 [Linkable], -- new linkables
389 [SDoc], -- error messages
392 upsweep_sccs finder threaded sccOKs newLis errs warns []
393 = -- No more SCCs to do.
394 return (threaded, sccOKs, newLis, errs, warns)
396 upsweep_sccs finder threaded sccOKs newLis errs warns (scc:sccs)
397 = -- Start work on a new SCC.
398 do (threaded2, lisM, errsM, warnsM)
399 <- upsweep_mods finder threaded (flatten scc)
401 then -- all the modules in the scc were ok
402 -- move on to the next SCC
403 upsweep_sccs finder threaded2
404 (scc:sccOKs) (lisM++newLis)
405 errs (warnsM++warns) sccs
406 else -- we got a compilation error; give up now
409 lisM++newLis, errsM++errs, warnsM++warns)
411 -- Compile multiple modules (one SCC), stopping as soon as an error appears
412 upsweep_mods :: Finder
415 -> IO (ModThreaded, [Linkable], [SDoc], [SDoc])
416 upsweep_mods finder threaded []
417 = return (threaded, [], [], [])
418 upsweep_mods finder threaded (mod:mods)
419 = do (threaded1, maybe_linkable, errsM, warnsM)
420 <- upsweep_mod finder threaded mod
422 then -- No errors; get contribs from the rest
423 do (threaded2, linkables, errsMM, warnsMM)
424 <- upsweep_mods finder threaded1 mods
426 (threaded2, maybeToList maybe_linkable ++ linkables,
427 errsM++errsMM, warnsM++warnsMM)
428 else -- Errors; give up _now_
429 return (threaded1, [], errsM, warnsM)
431 -- Compile a single module.
432 upsweep_mod :: Finder
435 -> IO (ModThreaded, Maybe Linkable, [SDoc], [SDoc])
437 upsweep_mod finder threaded1 summary1
438 = do let mod_name = name_of_summary summary1
439 let (ModThreaded pcs1 hst1 hit1) = threaded1
440 let old_iface = lookupFM hit1 (name_of_summary summary1)
441 compresult <- cmCompile finder summary1 old_iface hst1 pcs1
445 -- Compilation "succeeded", but didn't return a new iface or
446 -- linkable, meaning that compilation wasn't needed, and the
447 -- new details were manufactured from the old iface.
448 CompOK details Nothing pcs2 warns
449 -> let hst2 = addToFM hst1 mod_name details
451 threaded2 = ModThreaded pcs2 hst2 hit2
452 in return (threaded2, Nothing, [], warns)
454 -- Compilation really did happen, and succeeded. A new
455 -- details, iface and linkable are returned.
456 CompOK details (Just (new_iface, new_linkable)) pcs2 warns
457 -> let hst2 = addToFM hst1 mod_name details
458 hit2 = addToFM hit1 mod_name new_iface
459 threaded2 = ModThreaded pcs2 hst2 hit2
460 in return (threaded2, Just new_linkable, [], warns)
462 -- Compilation failed. compile may still have updated
464 CompErrs pcs2 errs warns
465 -> let threaded2 = ModThreaded pcs2 hst1 hit1
466 in return (threaded2, Nothing, errs, warns)
468 filterTopLevelEnvs :: (ModName -> Bool) -> (HST, HIT, UI) -> (HST, HIT, UI)
469 filterTopLevelEnvs p (hst, hit, ui)
470 = (filterFM (\k v -> p k) hst,
471 filterFM (\k v -> p k) hit,
472 filterModuleLinkables p ui
475 topological_sort :: [ModSummary] -> [SCC ModSummary]
476 topological_sort summaries
478 toEdge :: ModSummary -> (ModSummary,ModName,[ModName])
480 = (summ, name_of_summary summ, deps_of_summary summ)
482 mash_edge :: (ModSummary,ModName,[ModName]) -> (ModSummary,Int,[Int])
483 mash_edge (summ, m, m_imports)
484 = case lookup m key_map of
485 Nothing -> panic "reverse_topological_sort"
486 Just mk -> (summ, mk,
487 -- ignore imports not from the home package
488 catMaybes (map (flip lookup key_map) m_imports))
490 edges = map toEdge summaries
491 key_map = zip [nm | (s,nm,imps) <- edges] [1 ..] :: [(ModName,Int)]
492 scc_input = map mash_edge edges
493 sccs = stronglyConnComp scc_input
497 downsweep :: ModName -- module to chase from
500 downsweep rootNm finder
501 = do rootLoc <- getSummary rootNm
504 getSummary :: ModName -> IO ModSummary
506 = do found <- finder nm
508 Just (mod, location) -> summarise mod location
509 Nothing -> panic ("CompManager: can't find module `" ++
510 showSDoc (ppr nm) ++ "'")
512 -- loop invariant: homeSummaries doesn't contain package modules
513 loop :: [ModSummary] -> IO [ModSummary]
515 = do let allImps -- all imports
516 = (nub . map mi_name . concat . map ms_get_imports)
518 let allHome -- all modules currently in homeSummaries
519 = map (ml_modname.ms_loc) homeSummaries
521 = filter (`notElem` allHome) allImps
523 <- mapM getSummary neededImps
525 = filter (not.isPackageLoc.ms_loc) neededSummaries
526 if null newHomeSummaries
527 then return homeSummaries
528 else loop (newHomeSummaries ++ homeSummaries)