[project @ 2000-11-15 15:43:30 by sewardj]
[ghc-hetmet.git] / ghc / compiler / compMan / CompManager.lhs
1 %
2 % (c) The University of Glasgow, 2000
3 %
4 \section[CompManager]{The Compilation Manager}
5
6 \begin{code}
7 module CompManager ( cmInit, cmLoadModule, 
8                      cmGetExpr, cmRunExpr,
9                      CmState, emptyCmState  -- abstract
10                    )
11 where
12
13 #include "HsVersions.h"
14
15 import List             ( nub )
16 import Maybe            ( catMaybes, maybeToList, fromMaybe )
17 import Maybes           ( maybeToBool )
18 import Outputable
19 import UniqFM           ( emptyUFM, lookupUFM, addToUFM, delListFromUFM )
20 import Digraph          ( SCC(..), stronglyConnComp, flattenSCC, flattenSCCs )
21 import Panic            ( panic )
22
23 import CmLink           ( PersistentLinkerState, emptyPLS, Linkable(..), 
24                           link, LinkResult(..), 
25                           filterModuleLinkables, modname_of_linkable,
26                           is_package_linkable, findModuleLinkable )
27 import Interpreter      ( HValue )
28 import CmSummarise      ( summarise, ModSummary(..), 
29                           name_of_summary, {-, is_source_import-} )
30 import Module           ( ModuleName, moduleName, packageOfModule, 
31                           isModuleInThisPackage, PackageName, moduleEnvElts,
32                           moduleNameUserString )
33 import CmStaticInfo     ( Package(..), PackageConfigInfo, GhciMode )
34 import DriverPipeline   ( compile, preprocess, doLink, CompResult(..) )
35 import HscTypes         ( HomeSymbolTable, HomeIfaceTable, 
36                           PersistentCompilerState, ModDetails(..) )
37 import Name             ( lookupNameEnv )
38 import PrelNames        ( mainName )
39 import HscMain          ( initPersistentCompilerState )
40 import Finder           ( findModule, emptyHomeDirCache )
41 import DriverUtil       ( BarfKind(..) )
42 import Exception        ( throwDyn )
43 import IO               ( hPutStrLn, stderr )
44 \end{code}
45
46
47
48 \begin{code}
49 cmInit :: PackageConfigInfo -> GhciMode -> IO CmState
50 cmInit raw_package_info gmode
51    = emptyCmState raw_package_info gmode
52
53 cmGetExpr :: CmState
54           -> ModuleName
55           -> String
56           -> IO (CmState, Either [SDoc] HValue)
57 cmGetExpr cmstate modhdl expr
58    = return (panic "cmGetExpr:unimp")
59
60 cmRunExpr :: HValue -> IO ()
61 cmRunExpr hval
62    = return (panic "cmRunExpr:unimp")
63
64
65 -- Persistent state just for CM, excluding link & compile subsystems
66 data PersistentCMState
67    = PersistentCMState {
68         hst   :: HomeSymbolTable,    -- home symbol table
69         hit   :: HomeIfaceTable,     -- home interface table
70         ui    :: UnlinkedImage,      -- the unlinked images
71         mg    :: ModuleGraph,        -- the module graph
72         pci   :: PackageConfigInfo,  -- NEVER CHANGES
73         gmode :: GhciMode            -- NEVER CHANGES
74      }
75
76 emptyPCMS :: PackageConfigInfo -> GhciMode -> PersistentCMState
77 emptyPCMS pci gmode
78   = PersistentCMState { hst = emptyHST, hit = emptyHIT,
79                         ui  = emptyUI,  mg  = emptyMG, 
80                         pci = pci, gmode = gmode }
81
82 emptyHIT :: HomeIfaceTable
83 emptyHIT = emptyUFM
84 emptyHST :: HomeSymbolTable
85 emptyHST = emptyUFM
86
87
88
89 -- Persistent state for the entire system
90 data CmState
91    = CmState {
92         pcms   :: PersistentCMState,       -- CM's persistent state
93         pcs    :: PersistentCompilerState, -- compile's persistent state
94         pls    :: PersistentLinkerState    -- link's persistent state
95      }
96
97 emptyCmState :: PackageConfigInfo -> GhciMode -> IO CmState
98 emptyCmState pci gmode
99     = do let pcms = emptyPCMS pci gmode
100          pcs     <- initPersistentCompilerState
101          pls     <- emptyPLS
102          return (CmState { pcms   = pcms,
103                            pcs    = pcs,
104                            pls    = pls })
105
106 -- CM internal types
107 type UnlinkedImage = [Linkable] -- the unlinked images (should be a set, really)
108 emptyUI :: UnlinkedImage
109 emptyUI = []
110
111 type ModuleGraph = [ModSummary]  -- the module graph, topologically sorted
112 emptyMG :: ModuleGraph
113 emptyMG = []
114
115 \end{code}
116
117 The real business of the compilation manager: given a system state and
118 a module name, try and bring the module up to date, probably changing
119 the system state at the same time.
120
121 \begin{code}
122 cmLoadModule :: CmState 
123              -> ModuleName
124              -> IO (CmState, Maybe ModuleName)
125
126 cmLoadModule cmstate1 rootname
127    = do -- version 1's are the original, before downsweep
128         let pcms1     = pcms   cmstate1
129         let pls1      = pls    cmstate1
130         let pcs1      = pcs    cmstate1
131         let mg1       = mg     pcms1
132         let hst1      = hst    pcms1
133         let hit1      = hit    pcms1
134         let ui1       = ui     pcms1
135    
136         let pcii      = pci   pcms1 -- this never changes
137         let ghci_mode = gmode pcms1 -- ToDo: fix!
138
139         -- Do the downsweep to reestablish the module graph
140         -- then generate version 2's by removing from HIT,HST,UI any
141         -- modules in the old MG which are not in the new one.
142
143         -- Throw away the old home dir cache
144         emptyHomeDirCache
145
146         putStr "cmLoadModule: downsweep begins\n"
147         mg2unsorted <- downsweep [rootname]
148
149         let modnames1   = map name_of_summary mg1
150         let modnames2   = map name_of_summary mg2unsorted
151         let mods_to_zap = filter (`notElem` modnames2) modnames1
152
153         let (hst2, hit2, ui2)
154                = removeFromTopLevelEnvs mods_to_zap (hst1, hit1, ui1)
155         -- should be cycle free; ignores 'import source's
156         let mg2 = topological_sort False mg2unsorted
157         -- ... whereas this takes them into account.  Only used for
158         -- backing out partially complete cycles following a failed
159         -- upsweep.
160         let mg2_with_srcimps = topological_sort True mg2unsorted
161       
162         putStrLn "after tsort:\n"
163         putStrLn (showSDoc (vcat (map ppr mg2)))
164
165         -- Because we don't take into account source imports when doing
166         -- the topological sort, there shouldn't be any cycles in mg2.
167         -- If there is, we complain and give up -- the user needs to
168         -- break the cycle using a boot file.
169
170         -- Now do the upsweep, calling compile for each module in
171         -- turn.  Final result is version 3 of everything.
172
173         let threaded2 = CmThreaded pcs1 hst2 hit2
174
175         (upsweep_complete_success, threaded3, modsDone, newLis)
176            <- upsweep_mods ui2 threaded2 mg2
177
178         let ui3 = add_to_ui ui2 newLis
179         let (CmThreaded pcs3 hst3 hit3) = threaded3
180
181         -- At this point, modsDone and newLis should have the same
182         -- length, so there is one new (or old) linkable for each 
183         -- mod which was processed (passed to compile).
184
185         -- Try and do linking in some form, depending on whether the
186         -- upsweep was completely or only partially successful.
187
188         if upsweep_complete_success
189
190          then 
191            -- Easy; just relink it all.
192            do putStrLn "UPSWEEP COMPLETELY SUCCESSFUL"
193               linkresult 
194                  <- link doLink ghci_mode (any exports_main (moduleEnvElts hst3)) 
195                          newLis pls1
196               case linkresult of
197                  LinkErrs _ _
198                     -> panic "cmLoadModule: link failed (1)"
199                  LinkOK pls3 
200                     -> do let pcms3 = PersistentCMState { hst=hst3, hit=hit3, 
201                                                           ui=ui3, mg=modsDone, 
202                                                           pci=pcii, gmode=ghci_mode }
203                           let cmstate3 
204                                  = CmState { pcms=pcms3, pcs=pcs3, pls=pls3 }
205                           return (cmstate3, Just rootname)
206
207          else 
208            -- Tricky.  We need to back out the effects of compiling any
209            -- half-done cycles, both so as to clean up the top level envs
210            -- and to avoid telling the interactive linker to link them.
211            do putStrLn "UPSWEEP PARTIALLY SUCCESSFUL"
212
213               let modsDone_names
214                      = map name_of_summary modsDone
215               let mods_to_zap_names 
216                      = findPartiallyCompletedCycles modsDone_names mg2_with_srcimps
217               let (hst4, hit4, ui4) 
218                      = removeFromTopLevelEnvs mods_to_zap_names (hst3,hit3,ui3)
219               let mods_to_keep
220                      = filter ((`notElem` mods_to_zap_names).name_of_summary) modsDone
221               let mods_to_keep_names 
222                      = map name_of_summary mods_to_keep
223               -- we could get the relevant linkables by filtering newLis, but
224               -- it seems easier to drag them out of the updated, cleaned-up UI
225               let linkables_to_link 
226                      = map (findModuleLinkable ui4) mods_to_keep_names
227
228               linkresult <- link doLink ghci_mode False linkables_to_link pls1
229               case linkresult of
230                  LinkErrs _ _
231                     -> panic "cmLoadModule: link failed (2)"
232                  LinkOK pls4
233                     -> do let pcms4 = PersistentCMState { hst=hst4, hit=hit4, 
234                                                           ui=ui4, mg=mods_to_keep,
235                                                           pci=pcii, gmode=ghci_mode }
236                           let cmstate4 
237                                  = CmState { pcms=pcms4, pcs=pcs3, pls=pls4 }
238                           return (cmstate4, 
239                                   -- choose rather arbitrarily who to return
240                                   if null mods_to_keep then Nothing 
241                                      else Just (last mods_to_keep_names))
242
243
244 -- Return (names of) all those in modsDone who are part of a cycle
245 -- as defined by theGraph.
246 findPartiallyCompletedCycles :: [ModuleName] -> [SCC ModSummary] -> [ModuleName]
247 findPartiallyCompletedCycles modsDone theGraph
248    = chew theGraph
249      where
250         chew [] = []
251         chew ((AcyclicSCC v):rest) = chew rest    -- acyclic?  not interesting.
252         chew ((CyclicSCC vs):rest)
253            = let names_in_this_cycle = nub (map name_of_summary vs)
254                  mods_in_this_cycle  
255                     = nub ([done | done <- modsDone, 
256                                    done `elem` names_in_this_cycle])
257                  chewed_rest = chew rest
258              in 
259              if   not (null mods_in_this_cycle) 
260                   && length mods_in_this_cycle < length names_in_this_cycle
261              then mods_in_this_cycle ++ chewed_rest
262              else chewed_rest
263
264
265 exports_main :: ModDetails -> Bool
266 exports_main md
267    = maybeToBool (lookupNameEnv (md_types md) mainName)
268
269
270 -- Add the given (LM-form) Linkables to the UI, overwriting previous
271 -- versions if they exist.
272 add_to_ui :: UnlinkedImage -> [Linkable] -> UnlinkedImage
273 add_to_ui ui lis
274    = foldr add1 ui lis
275      where
276         add1 :: Linkable -> UnlinkedImage -> UnlinkedImage
277         add1 li ui
278            = li : filter (\li2 -> not (for_same_module li li2)) ui
279
280         for_same_module :: Linkable -> Linkable -> Bool
281         for_same_module li1 li2 
282            = not (is_package_linkable li1)
283              && not (is_package_linkable li2)
284              && modname_of_linkable li1 == modname_of_linkable li2
285                                   
286
287 data CmThreaded  -- stuff threaded through individual module compilations
288    = CmThreaded PersistentCompilerState HomeSymbolTable HomeIfaceTable
289
290
291 -- Compile multiple modules, stopping as soon as an error appears.
292 -- There better had not be any cyclic groups here -- we check for them.
293 upsweep_mods :: UnlinkedImage         -- old linkables
294              -> CmThreaded            -- PCS & HST & HIT
295              -> [SCC ModSummary]      -- mods to do (the worklist)
296                                       -- ...... RETURNING ......
297              -> IO (Bool{-complete success?-},
298                     CmThreaded,
299                     [ModSummary],     -- mods which succeeded
300                     [Linkable])       -- new linkables
301
302 upsweep_mods oldUI threaded []
303    = return (True, threaded, [], [])
304
305 upsweep_mods oldUI threaded ((CyclicSCC ms):_)
306    = do hPutStrLn stderr ("ghc: module imports form a cycle for modules:\n\t" ++
307                           unwords (map (moduleNameUserString.name_of_summary) ms))
308         return (False, threaded, [], [])
309
310 upsweep_mods oldUI threaded ((AcyclicSCC mod):mods)
311    = do (threaded1, maybe_linkable) <- upsweep_mod oldUI threaded mod
312         case maybe_linkable of
313            Just linkable 
314               -> -- No errors; do the rest
315                  do (restOK, threaded2, modOKs, linkables) 
316                        <- upsweep_mods oldUI threaded1 mods
317                     return (restOK, threaded2, mod:modOKs, linkable:linkables)
318            Nothing -- we got a compilation error; give up now
319               -> return (False, threaded1, [], [])
320
321
322 -- Compile a single module.  Always produce a Linkable for it if 
323 -- successful.  If no compilation happened, return the old Linkable.
324 upsweep_mod :: UnlinkedImage 
325             -> CmThreaded
326             -> ModSummary
327             -> IO (CmThreaded, Maybe Linkable)
328
329 upsweep_mod oldUI threaded1 summary1
330    = do let mod_name = name_of_summary summary1
331         let (CmThreaded pcs1 hst1 hit1) = threaded1
332         let old_iface = lookupUFM hit1 (name_of_summary summary1)
333         compresult <- compile summary1 old_iface hst1 hit1 pcs1
334
335         case compresult of
336
337            -- Compilation "succeeded", but didn't return a new iface or
338            -- linkable, meaning that compilation wasn't needed, and the
339            -- new details were manufactured from the old iface.
340            CompOK details Nothing pcs2
341               -> let hst2         = addToUFM hst1 mod_name details
342                      hit2         = hit1
343                      threaded2    = CmThreaded pcs2 hst2 hit2
344                      old_linkable = findModuleLinkable oldUI mod_name
345                  in  return (threaded2, Just old_linkable)
346
347            -- Compilation really did happen, and succeeded.  A new
348            -- details, iface and linkable are returned.
349            CompOK details (Just (new_iface, new_linkable)) pcs2
350               -> let hst2      = addToUFM hst1 mod_name details
351                      hit2      = addToUFM hit1 mod_name new_iface
352                      threaded2 = CmThreaded pcs2 hst2 hit2
353                  in  return (threaded2, Just new_linkable)
354
355            -- Compilation failed.  compile may still have updated
356            -- the PCS, tho.
357            CompErrs pcs2
358               -> let threaded2 = CmThreaded pcs2 hst1 hit1
359                  in  return (threaded2, Nothing)
360
361
362 removeFromTopLevelEnvs :: [ModuleName]
363                        -> (HomeSymbolTable, HomeIfaceTable, UnlinkedImage)
364                        -> (HomeSymbolTable, HomeIfaceTable, UnlinkedImage)
365 removeFromTopLevelEnvs zap_these (hst, hit, ui)
366    = (delListFromUFM hst zap_these,
367       delListFromUFM hit zap_these,
368       filterModuleLinkables (`notElem` zap_these) ui
369      )
370
371
372 topological_sort :: Bool -> [ModSummary] -> [SCC ModSummary]
373 topological_sort include_source_imports summaries
374    = let 
375          toEdge :: ModSummary -> (ModSummary,ModuleName,[ModuleName])
376          toEdge summ
377              = (summ, name_of_summary summ, 
378                       (if include_source_imports 
379                        then ms_srcimps summ else []) ++ ms_imps summ)
380         
381          mash_edge :: (ModSummary,ModuleName,[ModuleName]) -> (ModSummary,Int,[Int])
382          mash_edge (summ, m, m_imports)
383             = case lookup m key_map of
384                  Nothing -> panic "reverse_topological_sort"
385                  Just mk -> (summ, mk, 
386                                 -- ignore imports not from the home package
387                                 catMaybes (map (flip lookup key_map) m_imports))
388
389          edges     = map toEdge summaries
390          key_map   = zip [nm | (s,nm,imps) <- edges] [1 ..] :: [(ModuleName,Int)]
391          scc_input = map mash_edge edges
392          sccs      = stronglyConnComp scc_input
393      in
394          sccs
395
396
397 -- Chase downwards from the specified root set, returning summaries
398 -- for all home modules encountered.  Only follow source-import
399 -- links.
400 downsweep :: [ModuleName] -> IO [ModSummary]
401 downsweep rootNm
402    = do rootSummaries <- mapM getSummary rootNm
403         loop (filter (isModuleInThisPackage.ms_mod) rootSummaries)
404      where
405         getSummary :: ModuleName -> IO ModSummary
406         getSummary nm
407            | trace ("getSummary: "++ showSDoc (ppr nm)) True
408            = do found <- findModule nm
409                 case found of
410                    Just (mod, location) -> summarise preprocess mod location
411                    Nothing -> throwDyn (OtherError 
412                                    ("no signs of life for module `" 
413                                      ++ showSDoc (ppr nm) ++ "'"))
414                                  
415         -- loop invariant: homeSummaries doesn't contain package modules
416         loop :: [ModSummary] -> IO [ModSummary]
417         loop homeSummaries
418            = do let allImps :: [ModuleName]
419                     allImps = (nub . concatMap ms_imps) homeSummaries
420                 let allHome   -- all modules currently in homeSummaries
421                        = map (moduleName.ms_mod) homeSummaries
422                 let neededImps
423                        = filter (`notElem` allHome) allImps
424                 neededSummaries
425                        <- mapM getSummary neededImps
426                 let newHomeSummaries
427                        = filter (isModuleInThisPackage.ms_mod) neededSummaries
428                 if null newHomeSummaries
429                  then return homeSummaries
430                  else loop (newHomeSummaries ++ homeSummaries)
431 \end{code}