2 % (c) The University of Glasgow, 2000
4 \section[CompManager]{The Compilation Manager}
14 module CompManager ( cmInit, cmLoadModule,
16 CmState, emptyCmState -- abstract
20 #include "HsVersions.h"
23 import Maybe ( catMaybes, maybeToList, fromMaybe )
25 import FiniteMap ( emptyFM, filterFM, lookupFM, addToFM )
26 import Digraph ( SCC(..), stronglyConnComp )
27 import Panic ( panic )
29 import CmStaticInfo ( PCI(..), mkPCI, Package(..) )
30 import Finder ( Finder, newFinder,
31 ModName, ml_modname, isPackageLoc,
33 import CmSummarise ( summarise, ModSummary(..),
34 mi_name, ms_get_imports,
35 name_of_summary, deps_of_summary )
36 --import CmCompile ( PCS, emptyPCS, HST, HIT, CompResult(..), cmCompile )
37 import CmLink ( PLS, emptyPLS, Linkable(..),
39 filterModuleLinkables, modname_of_linkable,
41 import InterpSyn ( HValue )
44 cmInit :: String{-temp debugging hack-}
47 cmInit path raw_package_info
48 = emptyCmState path raw_package_info
53 -> IO (CmState, Either [SDoc] HValue)
54 cmGetExpr cmstate modhdl expr
55 = return (error "cmGetExpr:unimp")
57 cmRunExpr :: HValue -> IO ()
59 = return (error "cmRunExpr:unimp")
61 type ModHandle = String -- ToDo: do better?
64 -- Persistent state just for CM, excluding link & compile subsystems
65 data PersistentCMState
67 hst :: HomeSymbolTable, -- home symbol table
68 hit :: HomeIfaceTable, -- home interface table
69 ui :: UnlinkedImages, -- the unlinked images
70 mg :: ModuleGraph -- the module graph
73 emptyPCMS :: PersistentCMState
74 emptyPCMS = PersistentCMState
76 hst = emptyHST, hit = emptyHIT,
77 ui = emptyUI, mg = emptyMG }
79 emptyHIT :: HomeIfaceTable
81 emptyHST :: HomeSymbolTable
86 -- Persistent state for the entire system
89 pcms :: PersistentCMState, -- CM's persistent state
90 pcs :: PersistentCompilerState, -- compile's persistent state
91 pls :: PersistentLinkerState, -- link's persistent state
92 pci :: PackageConfigInfo, -- package config info, never changes
93 finder :: Finder -- the module finder
96 emptyCmState :: String{-temp debugging hack-}
97 -> [Package] -> IO CmState
98 emptyCmState path_TMP_DEBUGGING_HACK raw_package_info
99 = do let pcms = emptyPCMS
102 pci <- mkPCI raw_package_info
103 finder <- newFinder path_TMP_DEBUGGING_HACK pci
104 return (CmState { pcms = pcms,
111 type UnlinkedImage = [Linkable] -- the unlinked images (should be a set, really)
112 emptyUI :: UnlinkedImage
115 type ModuleGraph = [SCC ModSummary] -- the module graph, topologically sorted
116 emptyMG :: ModuleGraph
121 The real business of the compilation manager: given a system state and
122 a module name, try and bring the module up to date, probably changing
123 the system state at the same time.
126 cmLoadModule :: CmState
128 -> IO (CmState, Either [SDoc] ModHandle)
130 cmLoadModule cmstate1 modname
131 = do -- version 1's are the original, before downsweep
132 let pcms1 = pcms cmstate1
133 let pls1 = pls cmstate1
134 let pcs1 = pcs cmstate1
139 -- these aren't numbered since they don't change
140 let pcii = pci cmstate1
141 let finderr = finder cmstate1
143 -- do the downsweep to reestablish the module graph
144 -- then generate version 2's by removing from HIT,HST,UI any
145 -- modules in the old MG which are not in the new one.
147 -- TODO: call newFinder to reestablish home module cache?!
149 putStr "cmLoadModule: downsweep begins\n"
150 mg2unsorted <- downsweep modname finderr
151 putStrLn (showSDoc (vcat (map ppr mg2unsorted)))
153 let modnames1 = map name_of_summary (flattenSCCs mg1)
154 let modnames2 = map name_of_summary mg2unsorted
155 let mods_to_zap = filter (`notElem` modnames2) modnames1
157 let (hst2, hit2, ui2)
158 = filterTopLevelEnvs (`notElem` mods_to_zap)
161 let mg2 = topological_sort mg2unsorted
163 putStrLn "after tsort:\n"
164 putStrLn (showSDoc (vcat (map ppr (flattenSCCs mg2))))
166 -- Now do the upsweep, calling compile for each module in
167 -- turn. Final result is version 3 of everything.
169 let threaded2 = ModThreaded pcs1 hst2 hit2
171 (threaded3, sccOKs, newLis, errs, warns)
172 <- upsweep_sccs finderr threaded2 [] [] [] [] mg2
174 let ui3 = add_to_ui ui2 newLis
175 let (ModThreaded pcs3 hst3 hit3) = threaded3
177 -- Try and do linking in some form, depending on whether the
178 -- upsweep was completely or only partially successful.
183 do let mods_to_relink = upwards_closure mg2
184 (map modname_of_linkable newLis)
185 let pkg_linkables = find_pkg_linkables_for pcii mg2 mods_to_relink
186 putStrLn ("needed package modules =\n"
187 ++ showSDoc (vcat (map ppr pkg_linkables)))
188 let sccs_to_relink = group_uis ui3 mg2 mods_to_relink
189 let all_to_relink = map AcyclicSCC pkg_linkables
191 linkresult <- link pcii all_to_relink pls1
194 -> panic "cmLoadModule: link failed (1)"
197 = PCMS { hst=hst3, hit=hit3, ui=ui3, mg=mg2 }
199 = CmState { pcms=pcms3, pcs=pcs3, pls=pls3,
200 pci=pcii, finder=finderr }
201 return (cmstate3, Right modname)
204 do let mods_to_relink = downwards_closure mg2
205 (map name_of_summary (flattenSCCs sccOKs))
206 let pkg_linkables = find_pkg_linkables_for pcii mg2 mods_to_relink
207 let sccs_to_relink = group_uis ui3 mg2 mods_to_relink
208 let all_to_relink = map AcyclicSCC pkg_linkables
210 linkresult <- link pcii all_to_relink pls1
211 let (hst4, hit4, ui4)
212 = filterTopLevelEnvs (`notElem` mods_to_relink)
216 -> panic "cmLoadModule: link failed (2)"
219 = PCMS { hst=hst4, hit=hit4, ui=ui4, mg=mg2 }
221 = CmState { pcms=pcms4, pcs=pcs3, pls=pls4,
222 pci=pcii, finder=finderr }
223 return (cmstate4, Right modname)
225 -- Given a (home) module graph and a bunch of names of (home) modules
226 -- within that graph, return the names of any packages needed by the
227 -- named modules. Do this by looking at their imports. Assumes, and
228 -- checks, that all of "mods" are mentioned in "mg".
230 -- Then, having found the packages directly needed by "mods",
231 -- (1) round up, by looking in "pci", all packages they directly or
232 -- indirectly depend on, and (2) put these packages in topological
233 -- order, since that's important for some linkers. Since cycles in
234 -- the package dependency graph aren't allowed, we can just return
235 -- the list of (package) linkables, rather than a list of SCCs.
236 find_pkg_linkables_for :: PCI -> [SCC ModSummary] -> [ModName] -> [Linkable]
237 find_pkg_linkables_for pcii mg mods
238 = let mg_summaries = flattenSCCs mg
239 mg_names = map name_of_summary mg_summaries
241 if not (all (`elem` mg_names) mods)
242 then panic "find_packages_for"
246 [deps_of_summary summ
247 | summ <- mg_summaries, name_of_summary summ `elem` mods]
248 imports_not_in_home -- imports which must be from packages
249 = nub (filter (`notElem` mg_names) all_imports)
250 mod_tab :: [(ModName, PkgName, Path)]
251 mod_tab = module_table pcii
252 home_pkgs_needed -- the packages directly needed by the home modules
253 = nub [pkg_nm | (mod_nm, pkg_nm, path) <- mod_tab,
254 mod_nm `elem` imports_not_in_home]
256 -- Discover the package dependency graph, and use it to find the
257 -- transitive closure of all the needed packages
258 pkg_depend_graph :: [(PkgName,[PkgName])]
259 pkg_depend_graph = map (\raw -> (name raw, package_deps raw))
260 (raw_package_info pcii)
262 all_pkgs_needed = simple_transitive_closure
263 pkg_depend_graph home_pkgs_needed
265 -- Make a graph, in the style which Digraph.stronglyConnComp expects,
266 -- containing entries only for the needed packages.
269 [if srcP `elem` all_pkgs_needed
270 then [(srcP, srcP, dstsP)]
272 | (srcP, dstsP) <- pkg_depend_graph]
273 tsorted = flattenSCCs (stronglyConnComp needed_graph)
278 simple_transitive_closure :: Eq a => [(a,[a])] -> [a] -> [a]
279 simple_transitive_closure graph set
280 = let set2 = nub (concatMap dsts set ++ set)
281 dsts node = fromMaybe [] (lookup node graph)
283 if length set == length set2
285 else simple_transitive_closure graph set2
288 -- For each module in mods_to_group, extract the relevant linkable
289 -- out of UI, and arrange these linkables in SCCs as defined by modGraph.
290 -- All this is so that we can pass SCCified Linkable groups to the
291 -- linker. A constraint that should be recorded somewhere is that
292 -- all sccs should either be all-interpreted or all-object, not a mixture.
293 group_uis :: UI -> [SCC ModSummary] -> [ModName] -> [SCC Linkable]
294 group_uis ui modGraph mods_to_group
295 = map extract (cleanup (fishOut modGraph mods_to_group))
297 fishOut :: [SCC ModSummary] -> [ModName] -> [(Bool,[ModName])]
300 | otherwise = panic "group_uis: modnames not in modgraph"
301 fishOut ((AcyclicSCC ms):sccs) unused
302 = case split (== (name_of_summary ms)) unused of
303 (eq, not_eq) -> (False, eq) : fishOut sccs not_eq
304 fishOut ((CyclicSCC mss):sccs) unused
305 = case split (`elem` (map name_of_summary mss)) unused of
306 (eq, not_eq) -> (True, eq) : fishOut sccs not_eq
308 cleanup :: [(Bool,[ModName])] -> [SCC ModName]
310 cleanup ((isRec,names):rest)
311 | null names = cleanup rest
312 | isRec = CyclicSCC names : cleanup rest
313 | not isRec = case names of [name] -> AcyclicSCC name : cleanup rest
314 other -> panic "group_uis(cleanup)"
316 extract :: SCC ModName -> SCC Linkable
317 extract (AcyclicSCC nm) = AcyclicSCC (getLi nm)
318 extract (CyclicSCC nms) = CyclicSCC (map getLi nms)
320 getLi nm = case [li | li <- ui, not (is_package_linkable li),
321 nm == modname_of_linkable li] of
323 other -> panic "group_uis:getLi"
325 split f xs = (filter f xs, filter (not.f) xs)
328 -- Add the given (LM-form) Linkables to the UI, overwriting previous
329 -- versions if they exist.
330 add_to_ui :: UI -> [Linkable] -> UI
334 add1 :: Linkable -> UI -> UI
336 = li : filter (\li2 -> not (for_same_module li li2)) ui
338 for_same_module :: Linkable -> Linkable -> Bool
339 for_same_module li1 li2
340 = not (is_package_linkable li1)
341 && not (is_package_linkable li2)
342 && modname_of_linkable li1 == modname_of_linkable li2
345 -- Compute upwards and downwards closures in the (home-) module graph.
347 upwards_closure :: [SCC ModSummary] -> [ModName] -> [ModName]
349 upwards_closure = up_down_closure True
350 downwards_closure = up_down_closure False
352 up_down_closure :: Bool -> [SCC ModSummary] -> [ModName] -> [ModName]
353 up_down_closure up modGraph roots
354 = let mgFlat = flattenSCCs modGraph
355 nodes = map name_of_summary mgFlat
357 fwdEdges, backEdges :: [(ModName, [ModName])]
358 -- have an entry for each mod in mgFlat, and do not
359 -- mention edges leading out of the home package
362 backEdges -- Only calculated if needed, which is just as well!
363 = [(n, [m | (m, m_imports) <- fwdEdges, n `elem` m_imports])
364 | (n, n_imports) <- fwdEdges]
367 = (name_of_summary summ,
368 -- ignore imports not from the home package
369 filter (`elem` nodes) (deps_of_summary summ))
371 simple_transitive_closure
372 (if up then backEdges else fwdEdges) (nub roots)
375 data ModThreaded -- stuff threaded through individual module compilations
376 = ModThreaded PCS HST HIT
378 -- Compile multiple SCCs, stopping as soon as an error appears
379 upsweep_sccs :: Finder -- the finder
380 -> ModThreaded -- PCS & HST & HIT
381 -> [SCC ModSummary] -- accum: SCCs which succeeded
382 -> [Linkable] -- accum: new Linkables
383 -> [SDoc] -- accum: error messages
384 -> [SDoc] -- accum: warnings
385 -> [SCC ModSummary] -- SCCs to do (the worklist)
386 -- ...... RETURNING ......
388 [SCC ModSummary], -- SCCs which succeeded
389 [Linkable], -- new linkables
390 [SDoc], -- error messages
393 upsweep_sccs finder threaded sccOKs newLis errs warns []
394 = -- No more SCCs to do.
395 return (threaded, sccOKs, newLis, errs, warns)
397 upsweep_sccs finder threaded sccOKs newLis errs warns (scc:sccs)
398 = -- Start work on a new SCC.
399 do (threaded2, lisM, errsM, warnsM)
400 <- upsweep_mods finder threaded (flatten scc)
402 then -- all the modules in the scc were ok
403 -- move on to the next SCC
404 upsweep_sccs finder threaded2
405 (scc:sccOKs) (lisM++newLis)
406 errs (warnsM++warns) sccs
407 else -- we got a compilation error; give up now
410 lisM++newLis, errsM++errs, warnsM++warns)
412 -- Compile multiple modules (one SCC), stopping as soon as an error appears
413 upsweep_mods :: Finder
416 -> IO (ModThreaded, [Linkable], [SDoc], [SDoc])
417 upsweep_mods finder threaded []
418 = return (threaded, [], [], [])
419 upsweep_mods finder threaded (mod:mods)
420 = do (threaded1, maybe_linkable, errsM, warnsM)
421 <- upsweep_mod finder threaded mod
423 then -- No errors; get contribs from the rest
424 do (threaded2, linkables, errsMM, warnsMM)
425 <- upsweep_mods finder threaded1 mods
427 (threaded2, maybeToList maybe_linkable ++ linkables,
428 errsM++errsMM, warnsM++warnsMM)
429 else -- Errors; give up _now_
430 return (threaded1, [], errsM, warnsM)
432 -- Compile a single module.
433 upsweep_mod :: Finder
436 -> IO (ModThreaded, Maybe Linkable, [SDoc], [SDoc])
438 upsweep_mod finder threaded1 summary1
439 = do let mod_name = name_of_summary summary1
440 let (ModThreaded pcs1 hst1 hit1) = threaded1
441 let old_iface = lookupFM hit1 (name_of_summary summary1)
442 compresult <- cmCompile finder summary1 old_iface hst1 pcs1
446 -- Compilation "succeeded", but didn't return a new iface or
447 -- linkable, meaning that compilation wasn't needed, and the
448 -- new details were manufactured from the old iface.
449 CompOK details Nothing pcs2 warns
450 -> let hst2 = addToFM hst1 mod_name details
452 threaded2 = ModThreaded pcs2 hst2 hit2
453 in return (threaded2, Nothing, [], warns)
455 -- Compilation really did happen, and succeeded. A new
456 -- details, iface and linkable are returned.
457 CompOK details (Just (new_iface, new_linkable)) pcs2 warns
458 -> let hst2 = addToFM hst1 mod_name details
459 hit2 = addToFM hit1 mod_name new_iface
460 threaded2 = ModThreaded pcs2 hst2 hit2
461 in return (threaded2, Just new_linkable, [], warns)
463 -- Compilation failed. compile may still have updated
465 CompErrs pcs2 errs warns
466 -> let threaded2 = ModThreaded pcs2 hst1 hit1
467 in return (threaded2, Nothing, errs, warns)
469 filterTopLevelEnvs :: (ModName -> Bool) -> (HST, HIT, UI) -> (HST, HIT, UI)
470 filterTopLevelEnvs p (hst, hit, ui)
471 = (filterFM (\k v -> p k) hst,
472 filterFM (\k v -> p k) hit,
473 filterModuleLinkables p ui
476 topological_sort :: [ModSummary] -> [SCC ModSummary]
477 topological_sort summaries
479 toEdge :: ModSummary -> (ModSummary,ModName,[ModName])
481 = (summ, name_of_summary summ, deps_of_summary summ)
483 mash_edge :: (ModSummary,ModName,[ModName]) -> (ModSummary,Int,[Int])
484 mash_edge (summ, m, m_imports)
485 = case lookup m key_map of
486 Nothing -> panic "reverse_topological_sort"
487 Just mk -> (summ, mk,
488 -- ignore imports not from the home package
489 catMaybes (map (flip lookup key_map) m_imports))
491 edges = map toEdge summaries
492 key_map = zip [nm | (s,nm,imps) <- edges] [1 ..] :: [(ModName,Int)]
493 scc_input = map mash_edge edges
494 sccs = stronglyConnComp scc_input
498 downsweep :: ModName -- module to chase from
501 downsweep rootNm finder
502 = do rootLoc <- getSummary rootNm
505 getSummary :: ModName -> IO ModSummary
507 = do found <- finder nm
509 Just (mod, location) -> summarise mod location
510 Nothing -> panic ("CompManager: can't find module `" ++
511 showSDoc (ppr nm) ++ "'")
513 -- loop invariant: homeSummaries doesn't contain package modules
514 loop :: [ModSummary] -> IO [ModSummary]
516 = do let allImps -- all imports
517 = (nub . map mi_name . concat . map ms_get_imports)
519 let allHome -- all modules currently in homeSummaries
520 = map (ml_modname.ms_loc) homeSummaries
522 = filter (`notElem` allHome) allImps
524 <- mapM getSummary neededImps
526 = filter (not.isPackageLoc.ms_loc) neededSummaries
527 if null newHomeSummaries
528 then return homeSummaries
529 else loop (newHomeSummaries ++ homeSummaries)