| ... |
... |
@@ -5,6 +5,8 @@ |
|
5
|
5
|
{-# LANGUAGE RecordWildCards #-}
|
|
6
|
6
|
{-# LANGUAGE BlockArguments #-}
|
|
7
|
7
|
{-# LANGUAGE ViewPatterns #-}
|
|
|
8
|
+
|
|
|
9
|
+-- | See Note [The ModuleGraph]
|
|
8
|
10
|
module GHC.Driver.Downsweep
|
|
9
|
11
|
( downsweep
|
|
10
|
12
|
, downsweepThunk
|
| ... |
... |
@@ -91,7 +93,7 @@ import GHC.Unit.Module.Deps |
|
91
|
93
|
import qualified GHC.Unit.Home.Graph as HUG
|
|
92
|
94
|
import GHC.Unit.Module.Stage
|
|
93
|
95
|
|
|
94
|
|
-import Data.Either ( rights, partitionEithers, lefts )
|
|
|
96
|
+import Data.Either ( partitionEithers, lefts )
|
|
95
|
97
|
import qualified Data.Map as Map
|
|
96
|
98
|
import qualified Data.Set as Set
|
|
97
|
99
|
|
| ... |
... |
@@ -111,19 +113,39 @@ import Control.Monad.Trans.Reader |
|
111
|
113
|
import qualified Data.Map.Strict as M
|
|
112
|
114
|
import Control.Monad.Trans.Class
|
|
113
|
115
|
import System.IO.Unsafe (unsafeInterleaveIO)
|
|
|
116
|
+import Data.IORef
|
|
|
117
|
+import qualified Data.List.NonEmpty as NE
|
|
114
|
118
|
|
|
115
|
119
|
{-
|
|
116
|
|
-Note [Downsweep and the ModuleGraph]
|
|
117
|
|
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
120
|
+Note [The ModuleGraph]
|
|
|
121
|
+~~~~~~~~~~~~~~~~~~~~~~
|
|
|
122
|
+The 'ModuleGraph' stores the relationship between all the modules, units, and
|
|
|
123
|
+instantiations in the current session, allowing e.g. to answer questions about
|
|
|
124
|
+the transitive closure of the imports.
|
|
|
125
|
+
|
|
|
126
|
+* A /node/ of the `ModuleGraph`, of type `ModuleGraphNode`, corresponds
|
|
|
127
|
+ 1-1 with a home-package module of source code, N.hs or N.hs-boot.
|
|
|
128
|
+ See the haddocks of `ModuleGraphNode`.
|
|
|
129
|
+
|
|
|
130
|
+ The `ModuleNodeInfo` field of the `ModuleGraphNode` contains a `ModSummary`
|
|
|
131
|
+ that in turn describes where the source file is (its `ModLocation`), when it
|
|
|
132
|
+ was read, its contents etc. See Note [Module Types in the ModuleGraph].
|
|
|
133
|
+
|
|
|
134
|
+ Each node has a distinct `NodeKey` (an instance of Ord); the function
|
|
|
135
|
+ mkNodeKey :: ModuleGraphNode -> NodeKey
|
|
|
136
|
+ get the `NodeKey` of a node
|
|
118
|
137
|
|
|
119
|
|
-The ModuleGraph stores the relationship between all the modules, units, and
|
|
120
|
|
-instantiations in the current session.
|
|
|
138
|
+* An /edge/ of the `ModuleGraph` from N1 to N2 typically corresponds to a
|
|
|
139
|
+ direct import of module N2 in module N1: one edge for each import.
|
|
|
140
|
+ Imports of modules from non-home-packages are featured in the `ModuleGraph`
|
|
|
141
|
+ as `UnitNode`s, or `InstantiationNodes` when backpack is involved.
|
|
121
|
142
|
|
|
122
|
|
-When we do downsweep, we build up a new ModuleGraph, starting from the root
|
|
123
|
|
-modules. By following all the dependencies we construct a graph which allows
|
|
124
|
|
-us to answer questions about the transitive closure of the imports.
|
|
|
143
|
+ Each node contains a list of all its out-edges or, more precisely, of the
|
|
|
144
|
+ `NodeKey`s of its direct dependencies.
|
|
125
|
145
|
|
|
126
|
|
-The module graph is accessible in the HscEnv.
|
|
|
146
|
+Because a node in the `ModuleGraph` describes the precise dependencies of the module, each node has its
|
|
|
147
|
+own `UnitId`. Remember, a single module can be compiled against many different versions of a library; but
|
|
|
148
|
+once we fix its dependencies we can compile it, and give it a `UnitId`. See Note [About units] in GHC.Unit.
|
|
127
|
149
|
|
|
128
|
150
|
When is this graph constructed?
|
|
129
|
151
|
|
| ... |
... |
@@ -140,17 +162,54 @@ When is this graph constructed? |
|
140
|
162
|
|
|
141
|
163
|
The result is having a uniform graph available for the whole compilation pipeline.
|
|
142
|
164
|
|
|
143
|
|
--}
|
|
|
165
|
+See Note [Downsweep Control Flow and Caching] for implementation details of
|
|
|
166
|
+the algorithm and caching.
|
|
|
167
|
+
|
|
|
168
|
+Note [Downsweep: building and maintaining the module graph]
|
|
|
169
|
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
170
|
+The module graph can be built from scratch by starting from a set of /root nodes/
|
|
|
171
|
+and exploring their dependencies. This is done by `GHC.Driver.Downsweep.downsweep`.
|
|
|
172
|
+
|
|
|
173
|
+Another scenario is when we already /have/ a `ModuleGraph` and want to update
|
|
|
174
|
+it (e.g. to reflect any file-system changes that have taken place since the
|
|
|
175
|
+last invocation of `downsweep`) or augment it by exploring new roots (e.g. for
|
|
|
176
|
+incrementally constructing a ModuleGraph using the GHC API; See #27054). So
|
|
|
177
|
+`downsweep` takes a `Maybe ModuleGraph` as one of its arguments.
|
|
|
178
|
+
|
|
|
179
|
+Downsweep iteratively *expands* each so-called 'DownsweepNode' into a list of
|
|
|
180
|
+its dependencies, and recursively traverses all reachable nodes in a
|
|
|
181
|
+depth-first order using 'dfsBuild'. A 'DownsweepNode' is *expanded* by 'dsNodeExpand':
|
|
144
|
182
|
|
|
145
|
|
--- This caches the answer to the question, if we are in this unit, what does
|
|
146
|
|
--- an import of this module mean.
|
|
147
|
|
-type DownsweepCache = M.Map (UnitId, PkgQual, ModuleNameWithIsBoot) [Either DriverMessages ModuleNodeInfo]
|
|
|
183
|
+ dsNodeExpand :: DownsweepNode -> DownsweepM (NodeRes (ModuleGraphNode, [DownsweepNode]))
|
|
148
|
184
|
|
|
149
|
|
-moduleGraphNodeMap :: ModuleGraph -> M.Map NodeKey ModuleGraphNode
|
|
150
|
|
-moduleGraphNodeMap graph
|
|
151
|
|
- = M.fromList [(mkNodeKey node, node) | node <- mgModSummaries' graph]
|
|
|
185
|
+Most notably:
|
|
152
|
186
|
|
|
|
187
|
+ - 'DSMod' (Module-based) nodes can be expanded by preprocessing and
|
|
|
188
|
+ parsing the module header, then listing the imports (direct and SOURCE imports)
|
|
|
189
|
+ (see 'expandModuleSummary' and 'expandFixedModuleNode')
|
|
|
190
|
+
|
|
|
191
|
+ - 'DSUnit' is expanded by finding the unit dependencies of that unit by id
|
|
|
192
|
+ (see 'expandUnitNode').
|
|
|
193
|
+
|
|
|
194
|
+Besides its dependencies, expanding a 'DownsweepNode' produces a
|
|
|
195
|
+'ModuleGraphNode'. The final 'ModuleGraph' is constructed from the list of
|
|
|
196
|
+'ModuleGraphNode's accumulated by expanding all reachable 'DownsweepNode's.
|
|
|
197
|
+
|
|
|
198
|
+A 'ModuleGraphNode' is essentially the resolved version of 'DownsweepNode':
|
|
|
199
|
+it records the payload (e.g. a Module) *and* its dependencies, unlike
|
|
|
200
|
+'DownsweepNode' which has the just the payload that is used as a seed (and
|
|
|
201
|
+potentially some context information, like the current home-unit)
|
|
|
202
|
+
|
|
|
203
|
+TL;DR: We recursively traverse 'DownsweepNodes' to discover and build the 'ModuleGraph'.
|
|
|
204
|
+
|
|
|
205
|
+See also Note [Downsweep Control Flow and Caching] for implementation details.
|
|
|
206
|
+See Note [The ModuleGraph] for an overview when we do downsweep.
|
|
|
207
|
+-}
|
|
|
208
|
+
|
|
|
209
|
+-----------------------------------------------------------------------------
|
|
|
210
|
+-- * Top-level entry to downsweep
|
|
153
|
211
|
-----------------------------------------------------------------------------
|
|
|
212
|
+
|
|
154
|
213
|
--
|
|
155
|
214
|
-- | Downsweep (dependency analysis) for --make mode
|
|
156
|
215
|
--
|
| ... |
... |
@@ -162,7 +221,7 @@ moduleGraphNodeMap graph |
|
162
|
221
|
-- cache to avoid recalculating a module summary if the source is
|
|
163
|
222
|
-- unchanged.
|
|
164
|
223
|
--
|
|
165
|
|
--- Downsweeping can start from scratch for from a given module graph. In the
|
|
|
224
|
+-- Downsweeping can start from scratch or from a given module graph. In the
|
|
166
|
225
|
-- latter case, the given graph is fully included in the resulting graph, even
|
|
167
|
226
|
-- if parts of it are not reachable from any of the given roots. When an import
|
|
168
|
227
|
-- is processed, the source of the imported module is not consulted if this
|
| ... |
... |
@@ -178,6 +237,8 @@ moduleGraphNodeMap graph |
|
178
|
237
|
--
|
|
179
|
238
|
-- It will also turn on code generation for any modules that need it by calling
|
|
180
|
239
|
-- 'enableCodeGenForTH'.
|
|
|
240
|
+--
|
|
|
241
|
+-- See also Note [The ModuleGraph]
|
|
181
|
242
|
downsweep :: HscEnv
|
|
182
|
243
|
-> (GhcMessage -> AnyGhcDiagnostic)
|
|
183
|
244
|
-> Maybe Messager
|
| ... |
... |
@@ -195,8 +256,11 @@ downsweep :: HscEnv |
|
195
|
256
|
-- (Modules, IsBoot) identifiers, unless the Bool is true in
|
|
196
|
257
|
-- which case there can be repeats
|
|
197
|
258
|
downsweep hsc_env diag_wrapper msg old_summaries maybe_base_graph excl_mods allow_dup_roots = do
|
|
198
|
|
- n_jobs <- mkWorkerLimit (hsc_dflags hsc_env)
|
|
199
|
|
- (root_errs, root_summaries) <- rootSummariesParallel n_jobs hsc_env diag_wrapper msg summary
|
|
|
259
|
+ n_jobs <- mkWorkerLimit (hsc_dflags hsc_env)
|
|
|
260
|
+ summ_cache <- newIORef (mkModSummaryCache (zip old_summaries (repeat SummOld)))
|
|
|
261
|
+ imps_cache <- newIORef Map.empty
|
|
|
262
|
+ (root_errs, root_summaries) <- rootSummariesParallel n_jobs hsc_env diag_wrapper msg
|
|
|
263
|
+ (getRootSummary excl_mods summ_cache imps_cache)
|
|
200
|
264
|
let closure_errs = checkHomeUnitsClosed unit_env
|
|
201
|
265
|
unit_env = hsc_unit_env hsc_env
|
|
202
|
266
|
|
| ... |
... |
@@ -204,9 +268,13 @@ downsweep hsc_env diag_wrapper msg old_summaries maybe_base_graph excl_mods allo |
|
204
|
268
|
|
|
205
|
269
|
case all_errs of
|
|
206
|
270
|
[] -> do
|
|
207
|
|
- (downsweep_errs, downsweep_nodes) <- downsweepFromRootNodes hsc_env old_summary_map maybe_base_graph excl_mods allow_dup_roots DownsweepUseCompile (map ModuleNodeCompile root_summaries) []
|
|
|
271
|
+ (downsweep_errs, downsweep_nodes) <-
|
|
|
272
|
+ downsweepFromRootNodes hsc_env summ_cache imps_cache maybe_base_graph
|
|
|
273
|
+ excl_mods allow_dup_roots DownsweepUseCompile (map ModuleNodeCompile root_summaries) []
|
|
208
|
274
|
|
|
209
|
|
- let (other_errs, unit_nodes) = partitionEithers $ HUG.unitEnv_foldWithKey (\nodes uid hue -> nodes ++ unitModuleNodes downsweep_nodes uid hue) [] (hsc_HUG hsc_env)
|
|
|
275
|
+ let (other_errs, unit_nodes) = partitionEithers $
|
|
|
276
|
+ HUG.unitEnv_foldWithKey (\nodes uid hue -> nodes ++ unitModuleNodes downsweep_nodes uid hue) []
|
|
|
277
|
+ (hsc_HUG hsc_env)
|
|
210
|
278
|
|
|
211
|
279
|
let all_nodes = downsweep_nodes ++ unit_nodes
|
|
212
|
280
|
let all_errs = downsweep_errs ++ other_errs
|
| ... |
... |
@@ -222,22 +290,40 @@ downsweep hsc_env diag_wrapper msg old_summaries maybe_base_graph excl_mods allo |
|
222
|
290
|
return (all_errs, th_configured_nodes)
|
|
223
|
291
|
_ -> return (all_errs, emptyMG)
|
|
224
|
292
|
where
|
|
225
|
|
- summary = getRootSummary excl_mods old_summary_map
|
|
226
|
|
-
|
|
227
|
|
- -- A cache from file paths to the already summarised modules. The same file
|
|
228
|
|
- -- can be used in multiple units so the map is also keyed by which unit the
|
|
229
|
|
- -- file was used in.
|
|
230
|
|
- -- Reuse these if we can because the most expensive part of downsweep is
|
|
231
|
|
- -- reading the headers.
|
|
232
|
|
- old_summary_map :: M.Map (UnitId, OsPath) ModSummary
|
|
233
|
|
- old_summary_map =
|
|
234
|
|
- M.fromList [((ms_unitid ms, msHsFileOsPath ms), ms) | ms <- old_summaries]
|
|
235
|
|
-
|
|
236
|
293
|
-- Dependencies arising on a unit (backpack and module linking deps)
|
|
237
|
294
|
unitModuleNodes :: [ModuleGraphNode] -> UnitId -> HomeUnitEnv -> [Either (Messages DriverMessage) ModuleGraphNode]
|
|
238
|
295
|
unitModuleNodes summaries uid hue =
|
|
239
|
296
|
maybeToList (linkNodes summaries uid hue)
|
|
240
|
297
|
|
|
|
298
|
+ -- The linking plan for each module. If we need to do linking for a home unit
|
|
|
299
|
+ -- then this function returns a graph node which depends on all the modules in the home unit.
|
|
|
300
|
+
|
|
|
301
|
+ -- At the moment nothing can depend on these LinkNodes.
|
|
|
302
|
+ linkNodes :: [ModuleGraphNode] -> UnitId -> HomeUnitEnv -> Maybe (Either (Messages DriverMessage) ModuleGraphNode)
|
|
|
303
|
+ linkNodes summaries uid hue =
|
|
|
304
|
+ let dflags = homeUnitEnv_dflags hue
|
|
|
305
|
+ ofile = outputFile_ dflags
|
|
|
306
|
+
|
|
|
307
|
+ unit_nodes :: [NodeKey]
|
|
|
308
|
+ unit_nodes = map mkNodeKey (filter ((== uid) . mgNodeUnitId) summaries)
|
|
|
309
|
+ -- Issue a warning for the confusing case where the user
|
|
|
310
|
+ -- said '-o foo' but we're not going to do any linking.
|
|
|
311
|
+ -- We attempt linking if either (a) one of the modules is
|
|
|
312
|
+ -- called Main, or (b) the user said -no-hs-main, indicating
|
|
|
313
|
+ -- that main() is going to come from somewhere else.
|
|
|
314
|
+ --
|
|
|
315
|
+ no_hs_main = gopt Opt_NoHsMain dflags
|
|
|
316
|
+
|
|
|
317
|
+ main_sum = any (== NodeKey_Module (ModNodeKeyWithUid (GWIB (mainModuleNameIs dflags) NotBoot) uid)) unit_nodes
|
|
|
318
|
+
|
|
|
319
|
+ do_linking = main_sum || no_hs_main || ghcLink dflags == LinkDynLib || ghcLink dflags == LinkStaticLib || ghcLink dflags == LinkBytecodeLib
|
|
|
320
|
+
|
|
|
321
|
+ in if | isExecutableLink (ghcLink dflags) && isJust ofile && not do_linking ->
|
|
|
322
|
+ Just (Left $ singleMessage $ mkPlainErrorMsgEnvelope noSrcSpan (DriverRedirectedNoMain $ mainModuleNameIs dflags))
|
|
|
323
|
+ -- This should be an error, not a warning (#10895).
|
|
|
324
|
+ | ghcLink dflags /= NoLink, do_linking -> Just (Right (LinkNode unit_nodes uid))
|
|
|
325
|
+ | otherwise -> Nothing
|
|
|
326
|
+
|
|
241
|
327
|
-- | Calculate the module graph starting from a single ModSummary. The result is a
|
|
242
|
328
|
-- thunk, which when forced will perform the downsweep. This is useful in oneshot
|
|
243
|
329
|
-- mode where the module graph may never be needed.
|
| ... |
... |
@@ -245,7 +331,9 @@ downsweep hsc_env diag_wrapper msg old_summaries maybe_base_graph excl_mods allo |
|
245
|
331
|
downsweepThunk :: HscEnv -> ModSummary -> IO ModuleGraph
|
|
246
|
332
|
downsweepThunk hsc_env mod_summary = unsafeInterleaveIO $ do
|
|
247
|
333
|
debugTraceMsg (hsc_logger hsc_env) 3 $ text "Computing Module Graph thunk..."
|
|
248
|
|
- ~(errs, mg) <- downsweepFromRootNodes hsc_env mempty Nothing [] True DownsweepUseFixed [ModuleNodeCompile mod_summary] []
|
|
|
334
|
+ summs <- newIORef (mkModSummaryCache [(mod_summary,SummOld)])
|
|
|
335
|
+ imps <- newIORef mempty
|
|
|
336
|
+ ~(errs, mg) <- downsweepFromRootNodes hsc_env summs imps Nothing [] True DownsweepUseFixed [ModuleNodeCompile mod_summary] []
|
|
249
|
337
|
let dflags = hsc_dflags hsc_env
|
|
250
|
338
|
liftIO $ printOrThrowDiagnostics (hsc_logger hsc_env)
|
|
251
|
339
|
(initPrintConfig dflags)
|
| ... |
... |
@@ -269,82 +357,19 @@ downsweepInteractiveImports hsc_env ic = unsafeInterleaveIO $ do |
|
269
|
357
|
debugTraceMsg (hsc_logger hsc_env) 3 $ (text "Computing Interactive Module Graph thunk...")
|
|
270
|
358
|
let imps = ic_imports (hsc_IC hsc_env)
|
|
271
|
359
|
|
|
272
|
|
- let interactive_mn = icInteractiveModule ic
|
|
273
|
|
- -- No sensible value for ModLocation.. if you hit this panic then you probably
|
|
274
|
|
- -- need to add proper support for modules without any source files to the driver.
|
|
275
|
|
- let ml = pprPanic "modLocation" (ppr interactive_mn <+> ppr imps)
|
|
276
|
|
- let key = moduleToMnk interactive_mn NotBoot
|
|
277
|
|
- let node_type = ModuleNodeFixed key ml
|
|
|
360
|
+ interactive_mn = icInteractiveModule ic
|
|
278
|
361
|
|
|
279
|
362
|
-- The existing nodes in the module graph. This will be populated when GHCi runs
|
|
280
|
363
|
-- :load. Any home package modules need to already be in here.
|
|
281
|
|
- let cached_nodes = Map.fromList [ (mkNodeKey n, n) | n <- mg_mss (hsc_mod_graph hsc_env) ]
|
|
282
|
|
-
|
|
283
|
|
- (module_edges, graph) <- loopFromInteractive hsc_env (map mkEdge imps) cached_nodes
|
|
284
|
|
- let interactive_node = ModuleNode module_edges node_type
|
|
285
|
|
-
|
|
286
|
|
- let all_nodes = M.elems graph
|
|
287
|
|
- return $ mkModuleGraph (interactive_node : all_nodes)
|
|
288
|
|
-
|
|
289
|
|
- where
|
|
290
|
|
- --
|
|
291
|
|
- mkEdge :: InteractiveImport -> Either ModuleNodeEdge (UnitId, ImportLevel, PkgQual, GenWithIsBoot (Located ModuleName))
|
|
292
|
|
- -- A simple edge to a module from the same home unit
|
|
293
|
|
- mkEdge (IIModule n) =
|
|
294
|
|
- let
|
|
295
|
|
- mod_node_key = ModNodeKeyWithUid
|
|
296
|
|
- { mnkModuleName = GWIB (moduleName n) NotBoot
|
|
297
|
|
- , mnkUnitId =
|
|
298
|
|
- -- 'toUnitId' is safe here, as we can't import modules that
|
|
299
|
|
- -- don't have a 'UnitId'.
|
|
300
|
|
- toUnitId (moduleUnit n)
|
|
301
|
|
- }
|
|
302
|
|
- mod_node_edge =
|
|
303
|
|
- ModuleNodeEdge NormalLevel (NodeKey_Module mod_node_key)
|
|
304
|
|
- in Left mod_node_edge
|
|
305
|
|
- -- A complete import statement
|
|
306
|
|
- mkEdge (IIDecl i) =
|
|
307
|
|
- let lvl = convImportLevel (ideclLevelSpec i)
|
|
308
|
|
- wanted_mod = unLoc (ideclName i)
|
|
309
|
|
- is_boot = ideclSource i
|
|
310
|
|
- mb_pkg = renameRawPkgQual (hsc_unit_env hsc_env) (unLoc $ ideclName i) (ideclPkgQual i)
|
|
311
|
|
- unitId = homeUnitId $ hsc_home_unit hsc_env
|
|
312
|
|
- in Right (unitId, lvl, mb_pkg, GWIB (noLoc wanted_mod) is_boot)
|
|
313
|
|
-
|
|
314
|
|
-loopFromInteractive :: HscEnv
|
|
315
|
|
- -> [Either ModuleNodeEdge (UnitId, ImportLevel, PkgQual, GenWithIsBoot (Located ModuleName))]
|
|
316
|
|
- -> M.Map NodeKey ModuleGraphNode
|
|
317
|
|
- -> IO ([ModuleNodeEdge],M.Map NodeKey ModuleGraphNode)
|
|
318
|
|
-loopFromInteractive _ [] cached_nodes = return ([], cached_nodes)
|
|
319
|
|
-loopFromInteractive hsc_env (edge:edges) cached_nodes =
|
|
320
|
|
- case edge of
|
|
321
|
|
- Left edge -> do
|
|
322
|
|
- (edges, cached_nodes') <- loopFromInteractive hsc_env edges cached_nodes
|
|
323
|
|
- return (edge : edges, cached_nodes')
|
|
324
|
|
- Right (unitId, lvl, mb_pkg, GWIB wanted_mod is_boot) -> do
|
|
325
|
|
- let home_unit = ue_unitHomeUnit unitId (hsc_unit_env hsc_env)
|
|
326
|
|
- let k _ loc mod =
|
|
327
|
|
- let key = moduleToMnk mod is_boot
|
|
328
|
|
- in return $ FoundHome (ModuleNodeFixed key loc)
|
|
329
|
|
- found <- liftIO $ summariseModuleDispatch k hsc_env home_unit is_boot wanted_mod mb_pkg []
|
|
330
|
|
- case found of
|
|
331
|
|
- -- Case 1: Home modules have to already be in the cache.
|
|
332
|
|
- FoundHome (ModuleNodeFixed mod _) -> do
|
|
333
|
|
- let edge = ModuleNodeEdge lvl (NodeKey_Module mod)
|
|
334
|
|
- -- Note: Does not perform any further downsweep as the module must already be in the cache.
|
|
335
|
|
- (edges, cached_nodes') <- loopFromInteractive hsc_env edges cached_nodes
|
|
336
|
|
- return (edge : edges, cached_nodes')
|
|
337
|
|
- -- Case 2: External units may not be in the cache, if we haven't already initialised the
|
|
338
|
|
- -- module graph. We can construct the module graph for those here by calling loopUnit.
|
|
339
|
|
- External uid -> do
|
|
340
|
|
- let hsc_env' = hscSetActiveHomeUnit home_unit hsc_env
|
|
341
|
|
- cached_nodes' = loopUnit hsc_env' cached_nodes [uid]
|
|
342
|
|
- edge = ModuleNodeEdge lvl (NodeKey_ExternalUnit uid)
|
|
343
|
|
- (edges, cached_nodes') <- loopFromInteractive hsc_env edges cached_nodes'
|
|
344
|
|
- return (edge : edges, cached_nodes')
|
|
345
|
|
- -- And if it's not found.. just carry on and hope.
|
|
346
|
|
- _ -> loopFromInteractive hsc_env edges cached_nodes
|
|
|
364
|
+ let cached_nodes = Map.fromList [ (mkNodeKey n, NSuccess n) | n <- mg_mss (hsc_mod_graph hsc_env) ]
|
|
347
|
365
|
|
|
|
366
|
+ summ_cache <- newIORef mempty
|
|
|
367
|
+ imps_cache <- newIORef mempty
|
|
|
368
|
+ let env = DownsweepEnv hsc_env DownsweepUseFixed{-or DownsweepUseCompile?-} summ_cache imps_cache []
|
|
|
369
|
+ graph <- runDownsweepM env do
|
|
|
370
|
+ loopFromInteractive cached_nodes interactive_mn imps
|
|
|
371
|
+ let all_nodes = [s | NSuccess s <- M.elems graph ]
|
|
|
372
|
+ return $ mkModuleGraph all_nodes
|
|
348
|
373
|
|
|
349
|
374
|
-- | Create a module graph from a list of installed modules.
|
|
350
|
375
|
-- This is used by the loader when we need to load modules but there
|
| ... |
... |
@@ -373,7 +398,9 @@ downsweepInstalledModules hsc_env mods = do |
|
373
|
398
|
_ -> throwGhcException $ ProgramError $ showSDoc (hsc_dflags hsc_env) $ text "downsweepInstalledModules: Could not find installed module" <+> ppr i
|
|
374
|
399
|
|
|
375
|
400
|
nodes <- mapM process installed_mods
|
|
376
|
|
- (errs, mg) <- downsweepFromRootNodes hsc_env mempty Nothing [] True DownsweepUseFixed nodes external_uids
|
|
|
401
|
+ summs <- newIORef mempty
|
|
|
402
|
+ imps <- newIORef mempty
|
|
|
403
|
+ (errs, mg) <- downsweepFromRootNodes hsc_env summs imps Nothing [] True DownsweepUseFixed nodes external_uids
|
|
377
|
404
|
|
|
378
|
405
|
-- Similarly here, we should really not get any errors, but print them out if we do.
|
|
379
|
406
|
let dflags = hsc_dflags hsc_env
|
| ... |
... |
@@ -384,7 +411,35 @@ downsweepInstalledModules hsc_env mods = do |
|
384
|
411
|
|
|
385
|
412
|
return (mkModuleGraph mg)
|
|
386
|
413
|
|
|
|
414
|
+-----------------------------------------------------------------------------
|
|
|
415
|
+-- * Orchestrator: downsweepFromRootNodes
|
|
|
416
|
+-----------------------------------------------------------------------------
|
|
387
|
417
|
|
|
|
418
|
+type ModSummaryCache = IORef ModSummaryCacheMap
|
|
|
419
|
+type ImportsCache = IORef ImportsCacheMap
|
|
|
420
|
+
|
|
|
421
|
+-- | A cache from file paths to the already summarised modules. The same file
|
|
|
422
|
+-- can be used in multiple units so the map is actually also keyed by which
|
|
|
423
|
+-- unit the file was used in.
|
|
|
424
|
+--
|
|
|
425
|
+-- We want to reuse ModSummaries as far as possible because the most expensive
|
|
|
426
|
+-- part of downsweep is reading and parsing the headers.
|
|
|
427
|
+--
|
|
|
428
|
+-- See Note [Downsweep Control Flow and Caching]
|
|
|
429
|
+type ModSummaryCacheMap
|
|
|
430
|
+ -- The cache can't be keyed by 'Module' because that isn't sufficient to
|
|
|
431
|
+ -- distinguish .hs from .hs-boot files. Use path+unit instead.
|
|
|
432
|
+ = ( M.Map (UnitId, OsPath) (Either DriverMessages (ModSummary, SummProvenance)) )
|
|
|
433
|
+
|
|
|
434
|
+-- | A 'ModSummary's provenance during downsweep: an old previously constructed
|
|
|
435
|
+-- ModSummary, that might be potentially outdated, or a freshly constructed one
|
|
|
436
|
+-- during this downsweep which is certainly up to date?
|
|
|
437
|
+data SummProvenance
|
|
|
438
|
+ -- | Constructed during this downsweep: trivially up to date
|
|
|
439
|
+ = SummFresh
|
|
|
440
|
+ -- | Carried over from a previous run: may be stale, must be hash-checked
|
|
|
441
|
+ -- (and considered by -fforce-recomp)
|
|
|
442
|
+ | SummOld
|
|
388
|
443
|
|
|
389
|
444
|
-- | Whether downsweep should use compiler or fixed nodes. Compile nodes are used
|
|
390
|
445
|
-- by --make mode, and fixed nodes by oneshot mode.
|
| ... |
... |
@@ -397,7 +452,8 @@ data DownsweepMode = DownsweepUseCompile | DownsweepUseFixed |
|
397
|
452
|
-- This function will start at the given roots, and traverse downwards to find
|
|
398
|
453
|
-- all the dependencies, all the way to the leaf units.
|
|
399
|
454
|
downsweepFromRootNodes :: HscEnv
|
|
400
|
|
- -> M.Map (UnitId, OsPath) ModSummary
|
|
|
455
|
+ -> ModSummaryCache
|
|
|
456
|
+ -> ImportsCache
|
|
401
|
457
|
-> Maybe ModuleGraph
|
|
402
|
458
|
-> [ModuleName]
|
|
403
|
459
|
-> Bool
|
| ... |
... |
@@ -405,190 +461,347 @@ downsweepFromRootNodes :: HscEnv |
|
405
|
461
|
-> [ModuleNodeInfo] -- ^ The starting ModuleNodeInfo
|
|
406
|
462
|
-> [UnitId] -- ^ The starting units
|
|
407
|
463
|
-> IO ([DriverMessages], [ModuleGraphNode])
|
|
408
|
|
-downsweepFromRootNodes hsc_env old_summaries maybe_base_graph excl_mods allow_dup_roots mode root_nodes root_uids
|
|
409
|
|
- = do
|
|
410
|
|
- let root_map = mkRootMap root_nodes
|
|
411
|
|
- checkDuplicates root_map
|
|
412
|
|
- let env = DownsweepEnv hsc_env mode old_summaries excl_mods
|
|
413
|
|
- (deps', map0) <- runDownsweepM env $ do
|
|
414
|
|
- let base_nodes = maybe M.empty moduleGraphNodeMap maybe_base_graph
|
|
415
|
|
- (module_deps, map0) <- loopModuleNodeInfos root_nodes (base_nodes, root_map)
|
|
416
|
|
- let all_deps = loopUnit hsc_env module_deps root_uids
|
|
417
|
|
- let all_instantiations = getHomeUnitInstantiations hsc_env
|
|
418
|
|
- deps' <- loopInstantiations all_instantiations all_deps
|
|
419
|
|
- return (deps', map0)
|
|
420
|
|
-
|
|
421
|
|
-
|
|
422
|
|
- let downsweep_errs = lefts $ concat $ M.elems map0
|
|
423
|
|
- downsweep_nodes = M.elems deps'
|
|
424
|
|
-
|
|
425
|
|
- return (downsweep_errs, downsweep_nodes)
|
|
426
|
|
- where
|
|
427
|
|
- getHomeUnitInstantiations :: HscEnv -> [(UnitId, InstantiatedUnit)]
|
|
428
|
|
- getHomeUnitInstantiations hsc_env = HUG.unitEnv_foldWithKey (\nodes uid hue -> nodes ++ instantiationNodes uid (homeUnitEnv_units hue)) [] (hsc_HUG hsc_env)
|
|
429
|
|
-
|
|
430
|
|
- -- In a root module, the filename is allowed to diverge from the module
|
|
431
|
|
- -- name, so we have to check that there aren't multiple root files
|
|
432
|
|
- -- defining the same module (otherwise the duplicates will be silently
|
|
433
|
|
- -- ignored, leading to confusing behaviour).
|
|
434
|
|
- checkDuplicates
|
|
435
|
|
- :: DownsweepCache
|
|
436
|
|
- -> IO ()
|
|
437
|
|
- checkDuplicates root_map
|
|
438
|
|
- | not allow_dup_roots
|
|
439
|
|
- , dup_root:_ <- dup_roots = liftIO $ multiRootsErr sec dup_root
|
|
440
|
|
- | otherwise = pure ()
|
|
441
|
|
- where
|
|
442
|
|
- sec = initSourceErrorContext (hsc_dflags hsc_env)
|
|
443
|
|
- dup_roots :: [[ModuleNodeInfo]] -- Each at least of length 2
|
|
444
|
|
- dup_roots = filterOut isSingleton $ map rights (M.elems root_map)
|
|
445
|
|
-
|
|
446
|
|
-
|
|
447
|
|
-calcDeps :: ModSummary -> [(ImportLevel, PkgQual, GenWithIsBoot (Located ModuleName))]
|
|
448
|
|
-calcDeps ms =
|
|
449
|
|
- -- Add a dependency on the HsBoot file if it exists
|
|
450
|
|
- -- This gets passed to the loopImports function which just ignores it if it
|
|
451
|
|
- -- can't be found.
|
|
452
|
|
- [(NormalLevel, NoPkgQual, GWIB (noLoc $ ms_mod_name ms) IsBoot) | NotBoot <- [isBootSummary ms] ] ++
|
|
453
|
|
- [(lvl, b, c) | (lvl, b, c) <- msDeps ms ]
|
|
454
|
|
-
|
|
|
464
|
+downsweepFromRootNodes hsc_env summ_cache imps_cache maybe_base_graph excl_mods allow_dup_roots mode root_nodes root_uids = do
|
|
|
465
|
+ when (not allow_dup_roots) $
|
|
|
466
|
+ case root_duplicates of
|
|
|
467
|
+ [] -> return ()
|
|
|
468
|
+ (dup_root:_) -> multiRootsErr sec dup_root
|
|
|
469
|
+ modifyImpsCache imps_cache (`M.union` mkRootMap root_nodes) -- add root nodes to imports cache
|
|
|
470
|
+ let env = DownsweepEnv hsc_env mode summ_cache imps_cache excl_mods
|
|
|
471
|
+ deps' <- runDownsweepM env $ do
|
|
|
472
|
+ let base_nodes = maybe M.empty moduleGraphNodeMap maybe_base_graph
|
|
|
473
|
+ module_deps <- loopModuleNodeInfos base_nodes root_nodes
|
|
|
474
|
+ all_deps <- loopUnits module_deps (hscActiveUnitId hsc_env) root_uids
|
|
|
475
|
+ deps' <- loopInstantiations all_deps (getHomeUnitInstantiations hsc_env)
|
|
|
476
|
+ return deps'
|
|
|
477
|
+ f_cache <- readIORef summ_cache
|
|
|
478
|
+ let downsweep_errs = lefts (M.elems f_cache)
|
|
|
479
|
+ downsweep_nodes = [ s | NSuccess s <- M.elems deps' ]
|
|
|
480
|
+
|
|
|
481
|
+ return (downsweep_errs, downsweep_nodes)
|
|
|
482
|
+ where
|
|
|
483
|
+ getHomeUnitInstantiations :: HscEnv -> [(UnitId, InstantiatedUnit)]
|
|
|
484
|
+ getHomeUnitInstantiations hsc_env = HUG.unitEnv_foldWithKey
|
|
|
485
|
+ (\nodes uid hue -> nodes ++ instantiationNodes uid (homeUnitEnv_units hue)) [] (hsc_HUG hsc_env)
|
|
|
486
|
+
|
|
|
487
|
+ -- In a root module, the filename is allowed to diverge from the module
|
|
|
488
|
+ -- name, so we have to check that there aren't multiple root files
|
|
|
489
|
+ -- defining the same module (otherwise the duplicates will be silently
|
|
|
490
|
+ -- ignored, leading to confusing behaviour).
|
|
|
491
|
+ root_duplicates :: [NE.NonEmpty ModuleNodeInfo]
|
|
|
492
|
+ root_duplicates = mapMaybe takes2 (M.elems root_map)
|
|
|
493
|
+ where
|
|
|
494
|
+ takes2 (a:as@(_:_)) = Just (a NE.:| as) -- Each at least of length 2
|
|
|
495
|
+ takes2 _ = Nothing
|
|
|
496
|
+
|
|
|
497
|
+ root_map = Map.fromListWith (flip (++))
|
|
|
498
|
+ [ ((moduleNodeInfoUnitId s, moduleNodeInfoMnwib s), [s])
|
|
|
499
|
+ | s <- root_nodes ]
|
|
|
500
|
+
|
|
|
501
|
+ moduleGraphNodeMap :: ModuleGraph -> M.Map NodeKey (NodeRes ModuleGraphNode)
|
|
|
502
|
+ moduleGraphNodeMap graph
|
|
|
503
|
+ = M.fromList [(mkNodeKey node, NSuccess node) | node <- mgModSummaries' graph]
|
|
|
504
|
+
|
|
|
505
|
+ sec = initSourceErrorContext (hsc_dflags hsc_env)
|
|
|
506
|
+
|
|
|
507
|
+--------------------------------------------------------------------------------
|
|
|
508
|
+-- ** 'DownsweepM'
|
|
|
509
|
+--------------------------------------------------------------------------------
|
|
455
|
510
|
|
|
456
|
511
|
type DownsweepM a = ReaderT DownsweepEnv IO a
|
|
457
|
512
|
data DownsweepEnv = DownsweepEnv {
|
|
458
|
513
|
downsweep_hsc_env :: HscEnv
|
|
459
|
514
|
, _downsweep_mode :: DownsweepMode
|
|
460
|
|
- , _downsweep_old_summaries :: M.Map (UnitId, OsPath) ModSummary
|
|
|
515
|
+ , _downsweep_summaries_cache :: ModSummaryCache
|
|
|
516
|
+ , downsweep_imports_cache :: ImportsCache
|
|
461
|
517
|
, _downsweep_excl_mods :: [ModuleName]
|
|
462
|
518
|
}
|
|
463
|
519
|
|
|
|
520
|
+mkModSummaryCache :: [(ModSummary, SummProvenance)] -> ModSummaryCacheMap
|
|
|
521
|
+mkModSummaryCache summs = foldl' (flip (uncurry addModSummaryCache)) M.empty summs
|
|
|
522
|
+
|
|
|
523
|
+addModSummaryCache :: ModSummary -> SummProvenance -> ModSummaryCacheMap -> ModSummaryCacheMap
|
|
|
524
|
+addModSummaryCache ms pr fe = upd_fe fe
|
|
|
525
|
+ where
|
|
|
526
|
+ upd_fe fe
|
|
|
527
|
+ | Just src_fn_os <- ml_hs_file_ospath (ms_location ms)
|
|
|
528
|
+ = M.insert (ms_unitid ms, src_fn_os) (Right (ms, pr)) fe
|
|
|
529
|
+ | otherwise = fe
|
|
|
530
|
+
|
|
|
531
|
+modifySummCache :: ModSummaryCache -> (ModSummaryCacheMap -> ModSummaryCacheMap) -> IO ()
|
|
|
532
|
+modifyImpsCache :: ImportsCache -> (ImportsCacheMap -> ImportsCacheMap) -> IO ()
|
|
|
533
|
+modifySummCache r f = atomicModifyIORef' r (\c -> (f c, ()))
|
|
|
534
|
+modifyImpsCache r f = atomicModifyIORef' r (\c -> (f c, ()))
|
|
|
535
|
+
|
|
|
536
|
+-- | A cache from a module import (in given home unit context, with a package
|
|
|
537
|
+-- qualifier, and the imported module name (with or without SOURCE)) to the
|
|
|
538
|
+-- result of summarising that import (see 'summariseModuleDispatch').
|
|
|
539
|
+--
|
|
|
540
|
+-- See Note [Downsweep Control Flow and Caching]
|
|
|
541
|
+type ImportsCacheMap
|
|
|
542
|
+ = M.Map (UnitId, PkgQual, ModuleNameWithIsBoot) SummariseResult
|
|
|
543
|
+
|
|
|
544
|
+-- | Populate the 'ImportsCacheMap' with the root modules.
|
|
|
545
|
+mkRootMap :: [ModuleNodeInfo] -> ImportsCacheMap
|
|
|
546
|
+mkRootMap summaries = Map.fromList
|
|
|
547
|
+ [ ((moduleNodeInfoUnitId s, NoPkgQual, moduleNodeInfoMnwib s), FoundHome s) | s <- summaries ]
|
|
|
548
|
+
|
|
464
|
549
|
runDownsweepM :: DownsweepEnv -> DownsweepM a -> IO a
|
|
465
|
550
|
runDownsweepM env act = runReaderT act env
|
|
466
|
551
|
|
|
467
|
|
-
|
|
468
|
|
-loopInstantiations :: [(UnitId, InstantiatedUnit)]
|
|
469
|
|
- -> M.Map NodeKey ModuleGraphNode
|
|
470
|
|
- -> DownsweepM (M.Map NodeKey ModuleGraphNode)
|
|
471
|
|
-loopInstantiations [] done = pure done
|
|
472
|
|
-loopInstantiations ((home_uid, iud) :xs) done = do
|
|
473
|
|
- hsc_env <- asks downsweep_hsc_env
|
|
474
|
|
- let home_unit = ue_unitHomeUnit home_uid (hsc_unit_env hsc_env)
|
|
475
|
|
- let hsc_env' = hscSetActiveHomeUnit home_unit hsc_env
|
|
476
|
|
- done' = loopUnit hsc_env' done [instUnitInstanceOf iud]
|
|
477
|
|
- payload = InstantiationNode home_uid iud
|
|
478
|
|
- loopInstantiations xs (M.insert (mkNodeKey payload) payload done')
|
|
479
|
|
-
|
|
480
|
|
-
|
|
481
|
|
--- This loops over all the mod summaries in the dependency graph, accumulates the actual dependencies for each module/unit
|
|
482
|
|
-loopSummaries :: [ModSummary]
|
|
483
|
|
- -> (M.Map NodeKey ModuleGraphNode,
|
|
484
|
|
- DownsweepCache)
|
|
485
|
|
- -> DownsweepM ((M.Map NodeKey ModuleGraphNode), DownsweepCache)
|
|
486
|
|
-loopSummaries [] done = pure done
|
|
487
|
|
-loopSummaries (ms:next) (done, summarised)
|
|
488
|
|
- | Just {} <- M.lookup k done
|
|
489
|
|
- = loopSummaries next (done, summarised)
|
|
490
|
|
- -- Didn't work out what the imports mean yet, now do that.
|
|
491
|
|
- | otherwise = do
|
|
492
|
|
- (final_deps, done', summarised') <- loopImports (ms_unitid ms) (calcDeps ms) done summarised
|
|
493
|
|
- -- This has the effect of finding a .hs file if we are looking at the .hs-boot file.
|
|
494
|
|
- (_, done'', summarised'') <- loopImports (ms_unitid ms) (maybeToList hs_file_for_boot) done' summarised'
|
|
495
|
|
- loopSummaries next (M.insert k (ModuleNode final_deps (ModuleNodeCompile ms)) done'', summarised'')
|
|
|
552
|
+loopDownsweepNodes :: M.Map NodeKey (NodeRes ModuleGraphNode) -> [DownsweepNode] -> DownsweepM (M.Map NodeKey (NodeRes ModuleGraphNode))
|
|
|
553
|
+loopModuleNodeInfos :: M.Map NodeKey (NodeRes ModuleGraphNode) -> [ModuleNodeInfo] -> DownsweepM (M.Map NodeKey (NodeRes ModuleGraphNode))
|
|
|
554
|
+loopUnits :: M.Map NodeKey (NodeRes ModuleGraphNode) -> UnitId -> [UnitId] -> DownsweepM (M.Map NodeKey (NodeRes ModuleGraphNode))
|
|
|
555
|
+loopInstantiations :: M.Map NodeKey (NodeRes ModuleGraphNode) -> [(UnitId, InstantiatedUnit)] -> DownsweepM (M.Map NodeKey (NodeRes ModuleGraphNode))
|
|
|
556
|
+loopFromInteractive :: M.Map NodeKey (NodeRes ModuleGraphNode) -> Module -> [InteractiveImport] -> DownsweepM (M.Map NodeKey (NodeRes ModuleGraphNode))
|
|
|
557
|
+loopDownsweepNodes base_map nodes = dfsBuild (Just base_map) nodes dsNodeInfoKey dsNodeExpand
|
|
|
558
|
+loopModuleNodeInfos base_map = loopDownsweepNodes base_map . map DSMod
|
|
|
559
|
+loopUnits base_map homud = loopDownsweepNodes base_map . map (DSUnit homud)
|
|
|
560
|
+loopInstantiations base_map = loopDownsweepNodes base_map . map (uncurry DSInst)
|
|
|
561
|
+loopFromInteractive base_map m = loopDownsweepNodes base_map . (:[]) . DSInteractive m
|
|
|
562
|
+
|
|
|
563
|
+--------------------------------------------------------------------------------
|
|
|
564
|
+-- * Expanding 'DownsweepNode's into payload and node dependencies
|
|
|
565
|
+--------------------------------------------------------------------------------
|
|
|
566
|
+
|
|
|
567
|
+-- | A 'DownsweepNode' is the basic block of the downsweep algorithm which
|
|
|
568
|
+-- encompasses the types of nodes we can iteratively expand to construct the
|
|
|
569
|
+-- full module graph. See 'loopDownsweepNodes'.
|
|
|
570
|
+--
|
|
|
571
|
+-- See Note [Downsweep Control Flow and Caching]
|
|
|
572
|
+data DownsweepNode
|
|
|
573
|
+ -- | A module node to expand
|
|
|
574
|
+ = DSMod ModuleNodeInfo
|
|
|
575
|
+ -- | A unit node to expand
|
|
|
576
|
+ | DSUnit
|
|
|
577
|
+ { home_context_uid :: UnitId
|
|
|
578
|
+ -- ^ The home unit which introduced the dependency on this 'node_uid'. This
|
|
|
579
|
+ -- 'node_uid' can only be expanded in the context ('HscEnv') where
|
|
|
580
|
+ -- 'home_context_uid' is the active home unit, to make sure the package flags
|
|
|
581
|
+ -- are the ones attributed to the home package that introduced this node.
|
|
|
582
|
+ , node_uid :: UnitId
|
|
|
583
|
+ -- ^ The unit node to expand
|
|
|
584
|
+ }
|
|
|
585
|
+ -- | FIXME: document the meaning of 'DSInst'
|
|
|
586
|
+ | DSInst
|
|
|
587
|
+ { home_context_uid :: UnitId
|
|
|
588
|
+ , instantiated_ud :: InstantiatedUnit
|
|
|
589
|
+ }
|
|
|
590
|
+ -- | A group of interactive imports from this interactive Module
|
|
|
591
|
+ | DSInteractive Module [InteractiveImport]
|
|
|
592
|
+
|
|
|
593
|
+instance Outputable DownsweepNode where
|
|
|
594
|
+ ppr = \case
|
|
|
595
|
+ DSMod (ModuleNodeCompile ms) -> text "DSModC" <+> ppr (ms_mod_name ms)
|
|
|
596
|
+ DSMod (ModuleNodeFixed key _) -> text "DSModF" <+> ppr key
|
|
|
597
|
+ DSUnit{node_uid} -> text "DSUnit" <+> ppr node_uid
|
|
|
598
|
+ DSInst{instantiated_ud} -> text "DSInst" <+> ppr instantiated_ud
|
|
|
599
|
+ DSInteractive mod ii -> text "DSInteractive" <+> ppr mod <+> ppr ii
|
|
|
600
|
+
|
|
|
601
|
+-- | They key by which to cache previously visited 'DownsweepNode's
|
|
|
602
|
+dsNodeInfoKey :: DownsweepNode -> NodeKey
|
|
|
603
|
+dsNodeInfoKey = \case
|
|
|
604
|
+ DSMod (ModuleNodeCompile ms) -> NodeKey_Module (msKey ms)
|
|
|
605
|
+ DSMod (ModuleNodeFixed mod _) -> NodeKey_Module mod
|
|
|
606
|
+ DSUnit{node_uid} -> NodeKey_ExternalUnit node_uid
|
|
|
607
|
+ DSInst{instantiated_ud} -> NodeKey_Unit instantiated_ud
|
|
|
608
|
+ DSInteractive mod _imps -> NodeKey_Module $ moduleToMnk mod NotBoot
|
|
|
609
|
+
|
|
|
610
|
+dsNodeExpand :: DownsweepNode -> DownsweepM (NodeRes (ModuleGraphNode, [DownsweepNode]))
|
|
|
611
|
+dsNodeExpand = \case
|
|
|
612
|
+ DSMod (ModuleNodeCompile ms) -> expandModuleSummary ms
|
|
|
613
|
+ DSMod (ModuleNodeFixed key loc) -> expandFixedModuleNode key loc
|
|
|
614
|
+ DSUnit{ node_uid, home_context_uid } -> expandUnitNode node_uid home_context_uid
|
|
|
615
|
+ DSInst{ instantiated_ud
|
|
|
616
|
+ , home_context_uid } -> expandInstantiatedUnit instantiated_ud home_context_uid
|
|
|
617
|
+ DSInteractive imod iis -> expandInteractiveImports imod iis
|
|
|
618
|
+
|
|
|
619
|
+expandModuleSummary :: ModSummary -> DownsweepM (NodeRes (ModuleGraphNode, [DownsweepNode]))
|
|
|
620
|
+expandModuleSummary ms = do -- Didn't work out what the imports mean yet, now do that.
|
|
|
621
|
+ hsc_env <- asks downsweep_hsc_env
|
|
|
622
|
+ let home_uid = ms_unitid ms
|
|
|
623
|
+ home_unit = ue_unitHomeUnit home_uid (hsc_unit_env hsc_env)
|
|
|
624
|
+ (final_deps, todo) <- unzip <$> mapM (expandModImport home_uid home_unit) (calcDeps ms)
|
|
|
625
|
+
|
|
|
626
|
+ -- This has the effect of finding a .hs file if we are looking at the .hs-boot file.
|
|
|
627
|
+ boot_todo <-
|
|
|
628
|
+ if | HsBootFile <- ms_hsc_src ms
|
|
|
629
|
+ -> do
|
|
|
630
|
+ r <- downsweepSummarise home_unit NotBoot (noLoc $ ms_mod_name ms) NoPkgQual Nothing
|
|
|
631
|
+ case r of
|
|
|
632
|
+ FoundHome s -> pure [DSMod s]
|
|
|
633
|
+ _ -> pure []
|
|
|
634
|
+ | otherwise -> pure []
|
|
|
635
|
+
|
|
|
636
|
+ return $ NSuccess
|
|
|
637
|
+ ( ModuleNode (catMaybes final_deps) (ModuleNodeCompile ms)
|
|
|
638
|
+ , boot_todo ++ concat todo
|
|
|
639
|
+ )
|
|
496
|
640
|
where
|
|
497
|
|
- k = NodeKey_Module (msKey ms)
|
|
498
|
|
-
|
|
499
|
|
- hs_file_for_boot
|
|
500
|
|
- | HsBootFile <- ms_hsc_src ms
|
|
501
|
|
- = Just (NormalLevel, NoPkgQual, (GWIB (noLoc $ ms_mod_name ms) NotBoot))
|
|
502
|
|
- | otherwise
|
|
503
|
|
- = Nothing
|
|
504
|
|
-
|
|
505
|
|
-loopModuleNodeInfos :: [ModuleNodeInfo] -> (M.Map NodeKey ModuleGraphNode, DownsweepCache) -> DownsweepM (M.Map NodeKey ModuleGraphNode, DownsweepCache)
|
|
506
|
|
-loopModuleNodeInfos is cache = foldM (flip loopModuleNodeInfo) cache is
|
|
507
|
|
-
|
|
508
|
|
-loopModuleNodeInfo :: ModuleNodeInfo -> (M.Map NodeKey ModuleGraphNode, DownsweepCache) -> DownsweepM (M.Map NodeKey ModuleGraphNode, DownsweepCache)
|
|
509
|
|
-loopModuleNodeInfo mod_node_info (done, summarised) = do
|
|
510
|
|
- case mod_node_info of
|
|
511
|
|
- ModuleNodeCompile ms -> do
|
|
512
|
|
- loopSummaries [ms] (done, summarised)
|
|
513
|
|
- ModuleNodeFixed mod ml -> do
|
|
514
|
|
- done' <- loopFixedModule mod ml done
|
|
515
|
|
- return (done', summarised)
|
|
516
|
|
-
|
|
517
|
|
--- NB: loopFixedModule does not take a downsweep cache, because if you
|
|
518
|
|
--- ever reach a Fixed node, everything under that also must be fixed.
|
|
519
|
|
-loopFixedModule :: ModNodeKeyWithUid -> ModLocation
|
|
520
|
|
- -> M.Map NodeKey ModuleGraphNode
|
|
521
|
|
- -> DownsweepM (M.Map NodeKey ModuleGraphNode)
|
|
522
|
|
-loopFixedModule key loc done = do
|
|
523
|
|
- let nk = NodeKey_Module key
|
|
524
|
|
- hsc_env <- asks downsweep_hsc_env
|
|
525
|
|
- case M.lookup nk done of
|
|
526
|
|
- Just {} -> return done
|
|
527
|
|
- Nothing -> do
|
|
528
|
|
- -- MP: TODO, we should just read the dependency info from the interface rather than either
|
|
529
|
|
- -- a. Loading the whole thing into the EPS (this might never nececssary and causes lots of things to be permanently loaded into memory)
|
|
530
|
|
- -- b. Loading the whole interface into a buffer before discarding it. (wasted allocation and deserialisation)
|
|
531
|
|
- read_result <- liftIO $
|
|
532
|
|
- -- 1. Check if the interface is already loaded into the EPS by some other
|
|
533
|
|
- -- part of the compiler.
|
|
534
|
|
- lookupIfaceByModuleHsc hsc_env (mnkToModule key) >>= \case
|
|
535
|
|
- Just iface -> return (M.Succeeded iface)
|
|
536
|
|
- Nothing -> readIface (hsc_hooks hsc_env) (hsc_logger hsc_env) (hsc_dflags hsc_env) (hsc_NC hsc_env) (mnkToModule key) (ml_hi_file loc)
|
|
537
|
|
- case read_result of
|
|
538
|
|
- M.Succeeded iface -> do
|
|
539
|
|
- -- Computer information about this node
|
|
540
|
|
- let node_deps = ifaceDeps (mi_deps iface)
|
|
541
|
|
- edges = map mkFixedEdge node_deps
|
|
542
|
|
- node = ModuleNode edges (ModuleNodeFixed key loc)
|
|
543
|
|
- foldM (loopFixedNodeKey (mnkUnitId key)) (M.insert nk node done) (bimap snd snd <$> node_deps)
|
|
544
|
|
- -- Ignore any failure, we might try to read a .hi-boot file for
|
|
545
|
|
- -- example, even if there is not one.
|
|
546
|
|
- M.Failed {} ->
|
|
547
|
|
- return done
|
|
548
|
|
-
|
|
549
|
|
-loopFixedNodeKey :: UnitId -> M.Map NodeKey ModuleGraphNode -> Either ModNodeKeyWithUid UnitId -> DownsweepM (M.Map NodeKey ModuleGraphNode)
|
|
550
|
|
-loopFixedNodeKey _ done (Left key) = do
|
|
551
|
|
- loopFixedImports [key] done
|
|
552
|
|
-loopFixedNodeKey home_uid done (Right uid) = do
|
|
553
|
|
- -- Set active unit so that looking loopUnit finds the correct
|
|
554
|
|
- -- -package flags in the unit state.
|
|
555
|
|
- hsc_env <- asks downsweep_hsc_env
|
|
556
|
|
- let hsc_env' = hscSetActiveUnitId home_uid hsc_env
|
|
557
|
|
- return $ loopUnit hsc_env' done [uid]
|
|
558
|
|
-
|
|
559
|
|
-mkFixedEdge :: Either (ImportLevel, ModNodeKeyWithUid) (ImportLevel, UnitId) -> ModuleNodeEdge
|
|
560
|
|
-mkFixedEdge (Left (lvl, key)) = mkModuleEdge lvl (NodeKey_Module key)
|
|
561
|
|
-mkFixedEdge (Right (lvl, uid)) = mkModuleEdge lvl (NodeKey_ExternalUnit uid)
|
|
562
|
|
-
|
|
563
|
|
-ifaceDeps :: Dependencies -> [Either (ImportLevel, ModNodeKeyWithUid) (ImportLevel, UnitId)]
|
|
564
|
|
-ifaceDeps deps =
|
|
565
|
|
- [ Left (tcImportLevel lvl, ModNodeKeyWithUid dep uid)
|
|
566
|
|
- | (lvl, uid, dep) <- Set.toList (dep_direct_mods deps)
|
|
567
|
|
- ] ++
|
|
568
|
|
- [ Right (tcImportLevel lvl, uid)
|
|
569
|
|
- | (lvl, uid) <- Set.toList (dep_direct_pkgs deps)
|
|
570
|
|
- ]
|
|
571
|
|
-
|
|
572
|
|
--- Like loopImports, but we already know exactly which module we are looking for.
|
|
573
|
|
-loopFixedImports :: [ModNodeKeyWithUid]
|
|
574
|
|
- -> M.Map NodeKey ModuleGraphNode
|
|
575
|
|
- -> DownsweepM (M.Map NodeKey ModuleGraphNode)
|
|
576
|
|
-loopFixedImports [] done = pure done
|
|
577
|
|
-loopFixedImports (key:keys) done = do
|
|
578
|
|
- let nk = NodeKey_Module key
|
|
579
|
|
- hsc_env <- asks downsweep_hsc_env
|
|
580
|
|
- case M.lookup nk done of
|
|
581
|
|
- Just {} -> loopFixedImports keys done
|
|
582
|
|
- Nothing -> do
|
|
|
641
|
+ expandModImport home_uid home_unit (imp,mb_pkg,gwib) = do
|
|
|
642
|
+ let GWIB { gwib_mod = L loc mod, gwib_isBoot = is_boot } = gwib
|
|
|
643
|
+ wanted_mod = L loc mod
|
|
|
644
|
+ mb_s <- downsweepSummarise home_unit is_boot wanted_mod mb_pkg Nothing
|
|
|
645
|
+ case mb_s of
|
|
|
646
|
+ NotThere -> return
|
|
|
647
|
+ ( Nothing, [] )
|
|
|
648
|
+ External uid -> return
|
|
|
649
|
+ ( Just $ mkModuleEdge imp (NodeKey_ExternalUnit uid)
|
|
|
650
|
+ -- Specify home unit, as each unit might have a different visible package database.
|
|
|
651
|
+ , [DSUnit{node_uid = uid, home_context_uid = home_uid}] )
|
|
|
652
|
+ FoundInstantiation iud -> return
|
|
|
653
|
+ ( Just (mkModuleEdge imp (NodeKey_Unit iud)), [] )
|
|
|
654
|
+ FoundHomeWithError (_uid, _e) -> return
|
|
|
655
|
+ ( Nothing, [] )
|
|
|
656
|
+ -- the error @e@ is already stored in the summarisation cache,
|
|
|
657
|
+ -- (the IORef in DownsweepM) and will get reported at the end.
|
|
|
658
|
+ FoundHome s -> return
|
|
|
659
|
+ -- MP: This assumes that we can only instantiate non home units, which is probably fair enough for now.
|
|
|
660
|
+ ( Just $ mkModuleEdge imp (NodeKey_Module (mnKey s))
|
|
|
661
|
+ , [DSMod s] )
|
|
|
662
|
+
|
|
|
663
|
+ calcDeps :: ModSummary -> [(ImportLevel, PkgQual, GenWithIsBoot (Located ModuleName))]
|
|
|
664
|
+ calcDeps ms =
|
|
|
665
|
+ -- Add a dependency on the HsBoot file if it exists
|
|
|
666
|
+ -- This gets passed to the loopImports function which just ignores it if it
|
|
|
667
|
+ -- can't be found.
|
|
|
668
|
+ [(NormalLevel, NoPkgQual, GWIB (noLoc $ ms_mod_name ms) IsBoot) | NotBoot <- [isBootSummary ms] ] ++
|
|
|
669
|
+ [(lvl, b, c) | (lvl, b, c) <- msDeps ms ]
|
|
|
670
|
+
|
|
|
671
|
+-- | Expand a 'ModuleNodeFixed' node
|
|
|
672
|
+-- NB: If you ever reach a Fixed node, everything under that also must be fixed.
|
|
|
673
|
+expandFixedModuleNode :: ModNodeKeyWithUid -> ModLocation -> DownsweepM (NodeRes (ModuleGraphNode, [DownsweepNode]))
|
|
|
674
|
+expandFixedModuleNode key loc = do
|
|
|
675
|
+ hsc_env <- asks downsweep_hsc_env
|
|
|
676
|
+ -- MP: TODO, we should just read the dependency info from the interface rather than either
|
|
|
677
|
+ -- a. Loading the whole thing into the EPS (this might never nececssary and causes lots of things to be permanently loaded into memory)
|
|
|
678
|
+ -- b. Loading the whole interface into a buffer before discarding it. (wasted allocation and deserialisation)
|
|
|
679
|
+ read_result <- liftIO $
|
|
|
680
|
+ -- 1. Check if the interface is already loaded into the EPS by some other
|
|
|
681
|
+ -- part of the compiler.
|
|
|
682
|
+ lookupIfaceByModuleHsc hsc_env (mnkToModule key) >>= \case
|
|
|
683
|
+ Just iface -> return (M.Succeeded iface)
|
|
|
684
|
+ Nothing -> readIface (hsc_hooks hsc_env) (hsc_logger hsc_env) (hsc_dflags hsc_env) (hsc_NC hsc_env) (mnkToModule key) (ml_hi_file loc)
|
|
|
685
|
+ case read_result of
|
|
|
686
|
+ M.Succeeded iface -> do
|
|
|
687
|
+ -- Computer information about this node
|
|
|
688
|
+ let node_deps = ifaceDeps (mi_deps iface)
|
|
|
689
|
+ edges = map mkFixedEdge node_deps
|
|
|
690
|
+ node = ModuleNode edges (ModuleNodeFixed key loc)
|
|
|
691
|
+ deps' <- catMaybes <$> mapM (mk_dep hsc_env) (bimap snd snd <$> node_deps)
|
|
|
692
|
+ pure $ NSuccess (node, deps')
|
|
|
693
|
+
|
|
|
694
|
+ -- Skip any failure, we might try to read a .hi-boot file for
|
|
|
695
|
+ -- example, even if there is not one.
|
|
|
696
|
+ M.Failed {} ->
|
|
|
697
|
+ pure NSkip
|
|
|
698
|
+ where
|
|
|
699
|
+ mk_dep hsc_env (Left key) = do
|
|
|
700
|
+ -- Like expandImports, but we already know exactly which module we are looking for.
|
|
583
|
701
|
read_result <- liftIO $ findExactModule hsc_env (mnkToInstalledModule key) (mnkIsBoot key)
|
|
584
|
702
|
case read_result of
|
|
585
|
703
|
InstalledFound loc -> do
|
|
586
|
|
- done' <- loopFixedModule key loc done
|
|
587
|
|
- loopFixedImports keys done'
|
|
|
704
|
+ pure $ Just $ DSMod (ModuleNodeFixed key loc)
|
|
588
|
705
|
_otherwise ->
|
|
589
|
706
|
-- If the finder fails, just keep going, there will be another
|
|
590
|
|
- -- error later.
|
|
591
|
|
- loopFixedImports keys done
|
|
|
707
|
+ -- error later when we try to expand this dependency.
|
|
|
708
|
+ pure Nothing
|
|
|
709
|
+ mk_dep _ (Right uid_dep) = do
|
|
|
710
|
+ -- Set active unit so that looking loopUnit finds the correct
|
|
|
711
|
+ -- -package flags in the unit state.
|
|
|
712
|
+ let home_uid = mnkUnitId key
|
|
|
713
|
+ pure (Just DSUnit{node_uid=uid_dep, home_context_uid=home_uid})
|
|
|
714
|
+
|
|
|
715
|
+ mkFixedEdge :: Either (ImportLevel, ModNodeKeyWithUid) (ImportLevel, UnitId) -> ModuleNodeEdge
|
|
|
716
|
+ mkFixedEdge (Left (lvl, key)) = mkModuleEdge lvl (NodeKey_Module key)
|
|
|
717
|
+ mkFixedEdge (Right (lvl, uid)) = mkModuleEdge lvl (NodeKey_ExternalUnit uid)
|
|
|
718
|
+
|
|
|
719
|
+ ifaceDeps :: Dependencies -> [Either (ImportLevel, ModNodeKeyWithUid) (ImportLevel, UnitId)]
|
|
|
720
|
+ ifaceDeps deps =
|
|
|
721
|
+ [ Left (tcImportLevel lvl, ModNodeKeyWithUid dep uid)
|
|
|
722
|
+ | (lvl, uid, dep) <- Set.toList (dep_direct_mods deps)
|
|
|
723
|
+ ] ++
|
|
|
724
|
+ [ Right (tcImportLevel lvl, uid)
|
|
|
725
|
+ | (lvl, uid) <- Set.toList (dep_direct_pkgs deps)
|
|
|
726
|
+ ]
|
|
|
727
|
+
|
|
|
728
|
+-- | Expand a unit id under the context of a certain home unit
|
|
|
729
|
+expandUnitNode :: UnitId {-^ @node_uid@ -} -> UnitId {-^ Home unit from where @node_uid@ was introduced -}
|
|
|
730
|
+ -> DownsweepM (NodeRes (ModuleGraphNode, [DownsweepNode]))
|
|
|
731
|
+expandUnitNode node_uid home_context_uid = do
|
|
|
732
|
+ -- Set active unit so that looking loopUnit finds the correct
|
|
|
733
|
+ -- -package flags in the unit state.
|
|
|
734
|
+ hsc_env <- asks downsweep_hsc_env
|
|
|
735
|
+ let lcl_hsc_env = hscSetActiveUnitId home_context_uid hsc_env
|
|
|
736
|
+ case unitDepends <$> lookupUnitId (hsc_units lcl_hsc_env) node_uid of
|
|
|
737
|
+ Just us -> pure $ NSuccess ((UnitNode us node_uid), map (\u -> DSUnit{node_uid=u, home_context_uid{-inherit-}}) us)
|
|
|
738
|
+ Nothing -> pprPanic "loopUnit" (text "Malformed package database, missing " <+> ppr node_uid)
|
|
|
739
|
+
|
|
|
740
|
+expandInstantiatedUnit :: InstantiatedUnit -> UnitId {-^ Home unit -} -> DownsweepM (NodeRes (ModuleGraphNode, [DownsweepNode]))
|
|
|
741
|
+expandInstantiatedUnit iud home_uid = pure $ NSuccess
|
|
|
742
|
+ ( InstantiationNode home_uid iud
|
|
|
743
|
+ , [DSUnit{node_uid=instUnitInstanceOf iud, home_context_uid=home_uid}] )
|
|
|
744
|
+
|
|
|
745
|
+expandInteractiveImports :: Module -> [InteractiveImport] -> DownsweepM (NodeRes (ModuleGraphNode, [DownsweepNode]))
|
|
|
746
|
+expandInteractiveImports imod imps = do
|
|
|
747
|
+ hsc_env <- asks downsweep_hsc_env
|
|
|
748
|
+ imps_cache <- asks downsweep_imports_cache
|
|
|
749
|
+
|
|
|
750
|
+ let
|
|
|
751
|
+ -- A simple edge to a module from the same home unit
|
|
|
752
|
+ mkEdge (IIModule n) = return $
|
|
|
753
|
+ let
|
|
|
754
|
+ mod_node_key = ModNodeKeyWithUid
|
|
|
755
|
+ { mnkModuleName = GWIB (moduleName n) NotBoot
|
|
|
756
|
+ , mnkUnitId =
|
|
|
757
|
+ -- 'toUnitId' is safe here, as we can't import modules that
|
|
|
758
|
+ -- don't have a 'UnitId'.
|
|
|
759
|
+ toUnitId (moduleUnit n)
|
|
|
760
|
+ }
|
|
|
761
|
+ in (Just $ ModuleNodeEdge NormalLevel (NodeKey_Module mod_node_key), [])
|
|
|
762
|
+
|
|
|
763
|
+ -- A complete import statement
|
|
|
764
|
+ mkEdge (IIDecl i) =
|
|
|
765
|
+ let lvl = convImportLevel (ideclLevelSpec i)
|
|
|
766
|
+ wanted_mod = unLoc (ideclName i)
|
|
|
767
|
+ is_boot = ideclSource i
|
|
|
768
|
+ mb_pkg = renameRawPkgQual (hsc_unit_env hsc_env) (unLoc $ ideclName i) (ideclPkgQual i)
|
|
|
769
|
+ unitId = homeUnitId $ hsc_home_unit hsc_env
|
|
|
770
|
+ in do
|
|
|
771
|
+ let home_unit = ue_unitHomeUnit unitId (hsc_unit_env hsc_env)
|
|
|
772
|
+ let k _ loc mod =
|
|
|
773
|
+ let key = moduleToMnk mod is_boot
|
|
|
774
|
+ in return $ FoundHome (ModuleNodeFixed key loc)
|
|
|
775
|
+
|
|
|
776
|
+ found <- liftIO $ summariseModuleDispatch k hsc_env imps_cache
|
|
|
777
|
+ home_unit is_boot (noLoc wanted_mod) mb_pkg []
|
|
|
778
|
+ case found of
|
|
|
779
|
+ -- Case 1: Home modules have to already be in the cache.
|
|
|
780
|
+ FoundHome (ModuleNodeFixed mod _) -> do
|
|
|
781
|
+ let edge = ModuleNodeEdge lvl (NodeKey_Module mod)
|
|
|
782
|
+ -- Note: Does not perform any further downsweep as the module must already be in the cache.
|
|
|
783
|
+ return (Just edge, [])
|
|
|
784
|
+ -- Case 2: External units may not be in the cache, if we haven't already initialised the
|
|
|
785
|
+ -- module graph. We can construct the module graph for those here by calling loopUnit.
|
|
|
786
|
+ External uid -> do
|
|
|
787
|
+ let edge = ModuleNodeEdge lvl (NodeKey_ExternalUnit uid)
|
|
|
788
|
+ return (Just edge, [DSUnit{node_uid=uid, home_context_uid=homeUnitId home_unit}])
|
|
|
789
|
+ -- And if it's not found.. just carry on and hope.
|
|
|
790
|
+ _ -> return (Nothing, [])
|
|
|
791
|
+
|
|
|
792
|
+ (module_edges, todo) <- unzip <$> mapM mkEdge imps
|
|
|
793
|
+ pure $ NSuccess
|
|
|
794
|
+ ( ModuleNode (catMaybes module_edges) node_type, concat todo )
|
|
|
795
|
+ where
|
|
|
796
|
+ -- No sensible value for ModLocation.. if you hit this panic then you probably
|
|
|
797
|
+ -- need to add proper support for modules without any source files to the driver.
|
|
|
798
|
+ ml = pprPanic "modLocation" (ppr imod <+> ppr imps)
|
|
|
799
|
+ key = moduleToMnk imod NotBoot
|
|
|
800
|
+ node_type = ModuleNodeFixed key ml
|
|
|
801
|
+
|
|
|
802
|
+--------------------------------------------------------------------------------
|
|
|
803
|
+-- * Constructing Module Summaries
|
|
|
804
|
+--------------------------------------------------------------------------------
|
|
592
|
805
|
|
|
593
|
806
|
downsweepSummarise :: HomeUnit
|
|
594
|
807
|
-> IsBootInterface
|
| ... |
... |
@@ -597,90 +810,22 @@ downsweepSummarise :: HomeUnit |
|
597
|
810
|
-> Maybe (StringBuffer, UTCTime)
|
|
598
|
811
|
-> DownsweepM SummariseResult
|
|
599
|
812
|
downsweepSummarise home_unit is_boot wanted_mod mb_pkg maybe_buf = do
|
|
600
|
|
- DownsweepEnv hsc_env mode old_summaries excl_mods <- ask
|
|
601
|
|
- case mode of
|
|
602
|
|
- DownsweepUseCompile -> liftIO $ summariseModule hsc_env home_unit old_summaries is_boot wanted_mod mb_pkg maybe_buf excl_mods
|
|
603
|
|
- DownsweepUseFixed -> liftIO $ summariseModuleInterface hsc_env home_unit is_boot wanted_mod mb_pkg excl_mods
|
|
604
|
|
-
|
|
605
|
|
-
|
|
606
|
|
--- This loops over each import in each summary. It is mutually recursive with
|
|
607
|
|
--- loopSummaries if we discover a new module by doing this.
|
|
608
|
|
-loopImports
|
|
609
|
|
- :: UnitId
|
|
610
|
|
- -- ^ UnitId of home unit of summary whose imports are being processed
|
|
611
|
|
- -> [(ImportLevel, PkgQual, GenWithIsBoot (Located ModuleName))]
|
|
612
|
|
- -- ^ Work list: process these modules
|
|
613
|
|
- -> M.Map NodeKey ModuleGraphNode
|
|
614
|
|
- -> DownsweepCache
|
|
615
|
|
- -- ^ Visited set; the range is a list because
|
|
616
|
|
- -- the roots can have the same module names
|
|
617
|
|
- -- if allow_dup_roots is True
|
|
618
|
|
- -> DownsweepM ([ModuleNodeEdge],
|
|
619
|
|
- M.Map NodeKey ModuleGraphNode, DownsweepCache)
|
|
620
|
|
- -- ^ The result is the completed NodeMap
|
|
621
|
|
-loopImports _ [] done summarised = return ([], done, summarised)
|
|
622
|
|
-loopImports home_uid ((imp, mb_pkg, gwib) : ss) done summarised
|
|
623
|
|
- | Just summs <- M.lookup cache_key summarised
|
|
624
|
|
- = case summs of
|
|
625
|
|
- [Right ms] -> do
|
|
626
|
|
- let nk = mkModuleEdge imp (NodeKey_Module (mnKey ms))
|
|
627
|
|
- (rest, summarised', done') <- loopImportsNext done summarised
|
|
628
|
|
- return (nk: rest, summarised', done')
|
|
629
|
|
- [Left _err] ->
|
|
630
|
|
- loopImportsNext done summarised
|
|
631
|
|
- _errs -> do
|
|
632
|
|
- loopImportsNext done summarised
|
|
633
|
|
- | otherwise
|
|
634
|
|
- = do
|
|
635
|
|
- hsc_env <- asks downsweep_hsc_env
|
|
636
|
|
- let home_unit = ue_unitHomeUnit home_uid (hsc_unit_env hsc_env)
|
|
637
|
|
- mb_s <- downsweepSummarise home_unit
|
|
638
|
|
- is_boot wanted_mod mb_pkg
|
|
639
|
|
- Nothing
|
|
640
|
|
- case mb_s of
|
|
641
|
|
- NotThere -> loopImportsNext done summarised
|
|
642
|
|
- External uid -> do
|
|
643
|
|
- -- Pass an updated hsc_env to loopUnit, as each unit might
|
|
644
|
|
- -- have a different visible package database.
|
|
645
|
|
- let hsc_env' = hscSetActiveHomeUnit home_unit hsc_env
|
|
646
|
|
- let done' = loopUnit hsc_env' done [uid]
|
|
647
|
|
- (other_deps, done'', summarised') <- loopImportsNext done' summarised
|
|
648
|
|
- return (mkModuleEdge imp (NodeKey_ExternalUnit uid) : other_deps, done'', summarised')
|
|
649
|
|
- FoundInstantiation iud -> do
|
|
650
|
|
- (other_deps, done', summarised') <- loopImportsNext done summarised
|
|
651
|
|
- return (mkModuleEdge imp (NodeKey_Unit iud) : other_deps, done', summarised')
|
|
652
|
|
- FoundHomeWithError (_uid, e) -> loopImportsNext done (Map.insert cache_key [(Left e)] summarised)
|
|
653
|
|
- FoundHome s -> do
|
|
654
|
|
- (done', summarised') <-
|
|
655
|
|
- loopModuleNodeInfo s (done, Map.insert cache_key [Right s] summarised)
|
|
656
|
|
- (other_deps, final_done, final_summarised) <- loopImportsNext done' summarised'
|
|
657
|
|
-
|
|
658
|
|
- -- MP: This assumes that we can only instantiate non home units, which is probably fair enough for now.
|
|
659
|
|
- return (mkModuleEdge imp (NodeKey_Module (mnKey s)) : other_deps, final_done, final_summarised)
|
|
660
|
|
- where
|
|
661
|
|
- loopImportsNext = loopImports home_uid ss
|
|
662
|
|
- cache_key = (home_uid, mb_pkg, unLoc <$> gwib)
|
|
663
|
|
- GWIB { gwib_mod = L loc mod, gwib_isBoot = is_boot } = gwib
|
|
664
|
|
- wanted_mod = L loc mod
|
|
665
|
|
-
|
|
666
|
|
-loopUnit :: HscEnv -> Map.Map NodeKey ModuleGraphNode -> [UnitId] -> Map.Map NodeKey ModuleGraphNode
|
|
667
|
|
-loopUnit _ cache [] = cache
|
|
668
|
|
-loopUnit lcl_hsc_env cache (u:uxs) = do
|
|
669
|
|
- let nk = (NodeKey_ExternalUnit u)
|
|
670
|
|
- case Map.lookup nk cache of
|
|
671
|
|
- Just {} -> loopUnit lcl_hsc_env cache uxs
|
|
672
|
|
- Nothing -> case unitDepends <$> lookupUnitId (hsc_units lcl_hsc_env) u of
|
|
673
|
|
- Just us -> loopUnit lcl_hsc_env (loopUnit lcl_hsc_env (Map.insert nk (UnitNode us u) cache) us) uxs
|
|
674
|
|
- Nothing -> pprPanic "loopUnit" (text "Malformed package database, missing " <+> ppr u)
|
|
675
|
|
-
|
|
676
|
|
-multiRootsErr :: SourceErrorContext -> [ModuleNodeInfo] -> IO ()
|
|
677
|
|
-multiRootsErr _ [] = panic "multiRootsErr"
|
|
678
|
|
-multiRootsErr sec summs@(summ1:_)
|
|
|
813
|
+ DownsweepEnv hsc_env mode summaries_cache_ref imports_cache_ref excl_mods <- ask
|
|
|
814
|
+ liftIO $ case mode of
|
|
|
815
|
+ DownsweepUseCompile ->
|
|
|
816
|
+ summariseModule hsc_env home_unit summaries_cache_ref imports_cache_ref
|
|
|
817
|
+ is_boot wanted_mod mb_pkg maybe_buf excl_mods
|
|
|
818
|
+ DownsweepUseFixed ->
|
|
|
819
|
+ summariseModuleInterface hsc_env home_unit imports_cache_ref is_boot
|
|
|
820
|
+ wanted_mod mb_pkg excl_mods
|
|
|
821
|
+
|
|
|
822
|
+multiRootsErr :: SourceErrorContext -> NE.NonEmpty ModuleNodeInfo -> IO ()
|
|
|
823
|
+multiRootsErr sec (summ1 NE.:| summs)
|
|
679
|
824
|
= throwOneError sec $ fmap GhcDriverMessage $
|
|
680
|
825
|
mkPlainErrorMsgEnvelope noSrcSpan $ DriverDuplicatedModuleDeclaration mod files
|
|
681
|
826
|
where
|
|
682
|
827
|
mod = moduleNodeInfoModule summ1
|
|
683
|
|
- files = mapMaybe (ml_hs_file . moduleNodeInfoLocation) summs
|
|
|
828
|
+ files = mapMaybe (ml_hs_file . moduleNodeInfoLocation) (summ1:summs)
|
|
684
|
829
|
|
|
685
|
830
|
moduleNotFoundErr :: UnitId -> ModuleName -> DriverMessages
|
|
686
|
831
|
moduleNotFoundErr uid mod = singleMessage $ mkPlainErrorMsgEnvelope noSrcSpan (DriverModuleNotFound uid mod)
|
| ... |
... |
@@ -703,55 +848,27 @@ instantiationNodes uid unit_state = map (uid,) iuids_to_check |
|
703
|
848
|
, recur <- (indef :) $ goUnitId $ moduleUnit $ snd inst
|
|
704
|
849
|
]
|
|
705
|
850
|
|
|
706
|
|
--- The linking plan for each module. If we need to do linking for a home unit
|
|
707
|
|
--- then this function returns a graph node which depends on all the modules in the home unit.
|
|
708
|
|
-
|
|
709
|
|
--- At the moment nothing can depend on these LinkNodes.
|
|
710
|
|
-linkNodes :: [ModuleGraphNode] -> UnitId -> HomeUnitEnv -> Maybe (Either (Messages DriverMessage) ModuleGraphNode)
|
|
711
|
|
-linkNodes summaries uid hue =
|
|
712
|
|
- let dflags = homeUnitEnv_dflags hue
|
|
713
|
|
- ofile = outputFile_ dflags
|
|
714
|
|
-
|
|
715
|
|
- unit_nodes :: [NodeKey]
|
|
716
|
|
- unit_nodes = map mkNodeKey (filter ((== uid) . mgNodeUnitId) summaries)
|
|
717
|
|
- -- Issue a warning for the confusing case where the user
|
|
718
|
|
- -- said '-o foo' but we're not going to do any linking.
|
|
719
|
|
- -- We attempt linking if either (a) one of the modules is
|
|
720
|
|
- -- called Main, or (b) the user said -no-hs-main, indicating
|
|
721
|
|
- -- that main() is going to come from somewhere else.
|
|
722
|
|
- --
|
|
723
|
|
- no_hs_main = gopt Opt_NoHsMain dflags
|
|
724
|
|
-
|
|
725
|
|
- main_sum = any (== NodeKey_Module (ModNodeKeyWithUid (GWIB (mainModuleNameIs dflags) NotBoot) uid)) unit_nodes
|
|
726
|
|
-
|
|
727
|
|
- do_linking = main_sum || no_hs_main || ghcLink dflags == LinkDynLib || ghcLink dflags == LinkStaticLib || ghcLink dflags == LinkBytecodeLib
|
|
728
|
|
-
|
|
729
|
|
- in if | isExecutableLink (ghcLink dflags) && isJust ofile && not do_linking ->
|
|
730
|
|
- Just (Left $ singleMessage $ mkPlainErrorMsgEnvelope noSrcSpan (DriverRedirectedNoMain $ mainModuleNameIs dflags))
|
|
731
|
|
- -- This should be an error, not a warning (#10895).
|
|
732
|
|
- | ghcLink dflags /= NoLink, do_linking -> Just (Right (LinkNode unit_nodes uid))
|
|
733
|
|
- | otherwise -> Nothing
|
|
734
|
|
-
|
|
735
|
851
|
getRootSummary ::
|
|
736
|
852
|
[ModuleName] ->
|
|
737
|
|
- M.Map (UnitId, OsPath) ModSummary ->
|
|
|
853
|
+ ModSummaryCache ->
|
|
|
854
|
+ ImportsCache ->
|
|
738
|
855
|
HscEnv ->
|
|
739
|
856
|
Target ->
|
|
740
|
857
|
IO (Either DriverMessages ModSummary)
|
|
741
|
|
-getRootSummary excl_mods old_summary_map hsc_env target
|
|
|
858
|
+getRootSummary excl_mods summ_cache imports_cache hsc_env target
|
|
742
|
859
|
| TargetFile file mb_phase <- targetId
|
|
743
|
860
|
= do
|
|
744
|
861
|
let offset_file = augmentByWorkingDirectory dflags file
|
|
745
|
862
|
exists <- liftIO $ doesFileExist offset_file
|
|
746
|
863
|
if exists || isJust maybe_buf
|
|
747
|
|
- then summariseFile hsc_env home_unit old_summary_map offset_file mb_phase
|
|
|
864
|
+ then summariseFile hsc_env home_unit summ_cache offset_file mb_phase
|
|
748
|
865
|
maybe_buf
|
|
749
|
866
|
else
|
|
750
|
867
|
return $ Left $ singleMessage $
|
|
751
|
868
|
mkPlainErrorMsgEnvelope noSrcSpan (DriverFileNotFound offset_file)
|
|
752
|
869
|
| TargetModule modl <- targetId
|
|
753
|
870
|
= do
|
|
754
|
|
- maybe_summary <- summariseModule hsc_env home_unit old_summary_map NotBoot
|
|
|
871
|
+ maybe_summary <- summariseModule hsc_env home_unit summ_cache imports_cache NotBoot
|
|
755
|
872
|
(L rootLoc modl) (ThisPkg (homeUnitId home_unit))
|
|
756
|
873
|
maybe_buf excl_mods
|
|
757
|
874
|
pure case maybe_summary of
|
| ... |
... |
@@ -815,6 +932,10 @@ rootSummariesParallel n_jobs hsc_env diag_wrapper msg get_summary = do |
|
815
|
932
|
throwIO e
|
|
816
|
933
|
a -> pure a
|
|
817
|
934
|
|
|
|
935
|
+--------------------------------------------------------------------------------
|
|
|
936
|
+-- * Check/validate properties and error out
|
|
|
937
|
+--------------------------------------------------------------------------------
|
|
|
938
|
+
|
|
818
|
939
|
-- | This function checks then important property that if both p and q are home units
|
|
819
|
940
|
-- then any dependency of p, which transitively depends on q is also a home unit.
|
|
820
|
941
|
--
|
| ... |
... |
@@ -862,6 +983,10 @@ checkHomeUnitsClosed ue |
|
862
|
983
|
let todo'' = (depends Set.\\ done) `Set.union` todo'
|
|
863
|
984
|
in DigraphNode uid uid (Set.toList depends) : go (Set.insert uid done) todo''
|
|
864
|
985
|
|
|
|
986
|
+--------------------------------------------------------------------------------
|
|
|
987
|
+-- * Enable Code Gen for Template Haskell
|
|
|
988
|
+--------------------------------------------------------------------------------
|
|
|
989
|
+
|
|
865
|
990
|
-- | Update the every ModSummary that is depended on
|
|
866
|
991
|
-- by a module that needs template haskell. We enable codegen to
|
|
867
|
992
|
-- the specified target, disable optimization and change the .hi
|
| ... |
... |
@@ -1179,15 +1304,9 @@ Potential TODOS: |
|
1179
|
1304
|
generating temporary ones.
|
|
1180
|
1305
|
-}
|
|
1181
|
1306
|
|
|
1182
|
|
--- | Populate the Downsweep cache with the root modules.
|
|
1183
|
|
-mkRootMap
|
|
1184
|
|
- :: [ModuleNodeInfo]
|
|
1185
|
|
- -> DownsweepCache
|
|
1186
|
|
-mkRootMap summaries = Map.fromListWith (flip (++))
|
|
1187
|
|
- [ ((moduleNodeInfoUnitId s, NoPkgQual, moduleNodeInfoMnwib s), [Right s]) | s <- summaries ]
|
|
1188
|
|
-
|
|
1189
|
1307
|
-----------------------------------------------------------------------------
|
|
1190
|
|
--- Summarising modules
|
|
|
1308
|
+-- * Pre-processing and Summarising and modules
|
|
|
1309
|
+-----------------------------------------------------------------------------
|
|
1191
|
1310
|
|
|
1192
|
1311
|
-- We have two types of summarisation:
|
|
1193
|
1312
|
--
|
| ... |
... |
@@ -1202,33 +1321,39 @@ mkRootMap summaries = Map.fromListWith (flip (++)) |
|
1202
|
1321
|
summariseFile
|
|
1203
|
1322
|
:: HscEnv
|
|
1204
|
1323
|
-> HomeUnit
|
|
1205
|
|
- -> M.Map (UnitId, OsPath) ModSummary -- old summaries
|
|
|
1324
|
+ -> ModSummaryCache
|
|
1206
|
1325
|
-> FilePath -- source file name
|
|
1207
|
1326
|
-> Maybe Phase -- start phase
|
|
1208
|
1327
|
-> Maybe (StringBuffer,UTCTime)
|
|
1209
|
1328
|
-> IO (Either DriverMessages ModSummary)
|
|
1210
|
1329
|
|
|
1211
|
|
-summariseFile hsc_env' home_unit old_summaries src_fn mb_phase maybe_buf
|
|
1212
|
|
- -- we can use a cached summary if one is available and the
|
|
1213
|
|
- -- source file hasn't changed,
|
|
1214
|
|
- | Just old_summary <- M.lookup (homeUnitId home_unit, src_fn_os) old_summaries
|
|
1215
|
|
- = do
|
|
1216
|
|
- let location = ms_location $ old_summary
|
|
1217
|
|
-
|
|
1218
|
|
- src_hash <- get_src_hash
|
|
1219
|
|
- -- The file exists; we checked in getRootSummary above.
|
|
1220
|
|
- -- If it gets removed subsequently, then this
|
|
1221
|
|
- -- getFileHash may fail, but that's the right
|
|
1222
|
|
- -- behaviour.
|
|
1223
|
|
-
|
|
1224
|
|
- -- return the cached summary if the source didn't change
|
|
1225
|
|
- checkSummaryHash
|
|
1226
|
|
- hsc_env (new_summary src_fn)
|
|
1227
|
|
- old_summary location src_hash
|
|
1228
|
|
-
|
|
1229
|
|
- | otherwise
|
|
1230
|
|
- = do src_hash <- get_src_hash
|
|
1231
|
|
- new_summary src_fn src_hash
|
|
|
1330
|
+summariseFile hsc_env' home_unit summ_cache_ref src_fn mb_phase maybe_buf
|
|
|
1331
|
+ = do file_summ_cache <- readIORef summ_cache_ref
|
|
|
1332
|
+ case M.lookup (homeUnitId home_unit, src_fn_os) file_summ_cache of
|
|
|
1333
|
+ Just (Right (chd_summary, SummFresh)) ->
|
|
|
1334
|
+ -- Fresh: use it straight away
|
|
|
1335
|
+ pure (Right chd_summary)
|
|
|
1336
|
+ Just (Right (old_summary, SummOld)) -> do
|
|
|
1337
|
+ -- we can use a cached summary if one is available and the
|
|
|
1338
|
+ -- source file hasn't changed,
|
|
|
1339
|
+ let location = ms_location $ old_summary
|
|
|
1340
|
+
|
|
|
1341
|
+ src_hash <- get_src_hash
|
|
|
1342
|
+ -- The file exists; we checked in getRootSummary above.
|
|
|
1343
|
+ -- If it gets removed subsequently, then this
|
|
|
1344
|
+ -- getFileHash may fail, but that's the right
|
|
|
1345
|
+ -- behaviour.
|
|
|
1346
|
+
|
|
|
1347
|
+ -- return the cached summary if the source didn't change
|
|
|
1348
|
+ res <- checkSummaryHash
|
|
|
1349
|
+ hsc_env (new_summary src_fn)
|
|
|
1350
|
+ old_summary location src_hash
|
|
|
1351
|
+ case res of
|
|
|
1352
|
+ Right ms -> modifySummCache summ_cache_ref (addModSummaryCache ms SummFresh)
|
|
|
1353
|
+ Left _ -> pure ()
|
|
|
1354
|
+ return res
|
|
|
1355
|
+ _ -> do src_hash <- get_src_hash
|
|
|
1356
|
+ new_summary src_fn src_hash
|
|
1232
|
1357
|
where
|
|
1233
|
1358
|
-- change the main active unit so all operations happen relative to the given unit
|
|
1234
|
1359
|
hsc_env = hscSetActiveHomeUnit home_unit hsc_env'
|
| ... |
... |
@@ -1239,7 +1364,8 @@ summariseFile hsc_env' home_unit old_summaries src_fn mb_phase maybe_buf |
|
1239
|
1364
|
Just (buf,_) -> return $ fingerprintStringBuffer buf
|
|
1240
|
1365
|
Nothing -> liftIO $ getFileHash src_fn
|
|
1241
|
1366
|
|
|
1242
|
|
- new_summary src_fn src_hash = runExceptT $ do
|
|
|
1367
|
+ new_summary src_fn src_hash = do
|
|
|
1368
|
+ res <- runExceptT $ do
|
|
1243
|
1369
|
preimps@PreprocessedImports {..}
|
|
1244
|
1370
|
<- getPreprocessedImports hsc_env src_fn mb_phase maybe_buf
|
|
1245
|
1371
|
|
| ... |
... |
@@ -1270,6 +1396,10 @@ summariseFile hsc_env' home_unit old_summaries src_fn mb_phase maybe_buf |
|
1270
|
1396
|
, nms_mod = mod
|
|
1271
|
1397
|
, nms_preimps = preimps
|
|
1272
|
1398
|
}
|
|
|
1399
|
+ modifySummCache summ_cache_ref $ case res of
|
|
|
1400
|
+ Left e -> M.insert (homeUnitId home_unit, src_fn_os) (Left e)
|
|
|
1401
|
+ Right ms -> addModSummaryCache ms SummFresh
|
|
|
1402
|
+ return res
|
|
1273
|
1403
|
|
|
1274
|
1404
|
checkSummaryHash
|
|
1275
|
1405
|
:: HscEnv
|
| ... |
... |
@@ -1322,15 +1452,16 @@ data SummariseResult = |
|
1322
|
1452
|
-- --make mode.
|
|
1323
|
1453
|
summariseModule :: HscEnv
|
|
1324
|
1454
|
-> HomeUnit
|
|
1325
|
|
- -> M.Map (UnitId, OsPath) ModSummary
|
|
|
1455
|
+ -> ModSummaryCache
|
|
|
1456
|
+ -> ImportsCache
|
|
1326
|
1457
|
-> IsBootInterface
|
|
1327
|
1458
|
-> Located ModuleName
|
|
1328
|
1459
|
-> PkgQual
|
|
1329
|
1460
|
-> Maybe (StringBuffer, UTCTime)
|
|
1330
|
1461
|
-> [ModuleName]
|
|
1331
|
1462
|
-> IO SummariseResult
|
|
1332
|
|
-summariseModule hsc_env home_unit old_summaries is_boot wanted_mod mb_pkg maybe_buf excl_mods =
|
|
1333
|
|
- summariseModuleDispatch k hsc_env home_unit is_boot wanted_mod mb_pkg excl_mods
|
|
|
1463
|
+summariseModule hsc_env home_unit old_summaries imps_cache is_boot wanted_mod mb_pkg maybe_buf excl_mods =
|
|
|
1464
|
+ summariseModuleDispatch k hsc_env imps_cache home_unit is_boot wanted_mod mb_pkg excl_mods
|
|
1334
|
1465
|
where
|
|
1335
|
1466
|
k = summariseModuleWithSource home_unit old_summaries is_boot maybe_buf
|
|
1336
|
1467
|
|
| ... |
... |
@@ -1339,13 +1470,14 @@ summariseModule hsc_env home_unit old_summaries is_boot wanted_mod mb_pkg maybe_ |
|
1339
|
1470
|
-- This version always returns a ModuleNodeFixed node.
|
|
1340
|
1471
|
summariseModuleInterface :: HscEnv
|
|
1341
|
1472
|
-> HomeUnit
|
|
|
1473
|
+ -> ImportsCache
|
|
1342
|
1474
|
-> IsBootInterface
|
|
1343
|
1475
|
-> Located ModuleName
|
|
1344
|
1476
|
-> PkgQual
|
|
1345
|
1477
|
-> [ModuleName]
|
|
1346
|
1478
|
-> IO SummariseResult
|
|
1347
|
|
-summariseModuleInterface hsc_env home_unit is_boot wanted_mod mb_pkg excl_mods =
|
|
1348
|
|
- summariseModuleDispatch k hsc_env home_unit is_boot wanted_mod mb_pkg excl_mods
|
|
|
1479
|
+summariseModuleInterface hsc_env home_unit imps_cache is_boot wanted_mod mb_pkg excl_mods =
|
|
|
1480
|
+ summariseModuleDispatch k hsc_env imps_cache home_unit is_boot wanted_mod mb_pkg excl_mods
|
|
1349
|
1481
|
where
|
|
1350
|
1482
|
k _hsc_env loc mod = do
|
|
1351
|
1483
|
-- The finder will return a path to the .hi-boot even if it doesn't actually
|
| ... |
... |
@@ -1362,6 +1494,7 @@ summariseModuleInterface hsc_env home_unit is_boot wanted_mod mb_pkg excl_mods = |
|
1362
|
1494
|
summariseModuleDispatch
|
|
1363
|
1495
|
:: (HscEnv -> ModLocation -> Module -> IO SummariseResult) -- ^ Continuation about how to summarise a home module.
|
|
1364
|
1496
|
-> HscEnv
|
|
|
1497
|
+ -> ImportsCache
|
|
1365
|
1498
|
-> HomeUnit
|
|
1366
|
1499
|
-> IsBootInterface -- True <=> a {-# SOURCE #-} import
|
|
1367
|
1500
|
-> Located ModuleName -- Imported module to be summarised
|
| ... |
... |
@@ -1370,7 +1503,7 @@ summariseModuleDispatch |
|
1370
|
1503
|
-> IO SummariseResult
|
|
1371
|
1504
|
|
|
1372
|
1505
|
|
|
1373
|
|
-summariseModuleDispatch k hsc_env' home_unit is_boot (L _ wanted_mod) mb_pkg excl_mods
|
|
|
1506
|
+summariseModuleDispatch k hsc_env' imps_cache_ref home_unit is_boot (L _ wanted_mod) mb_pkg excl_mods
|
|
1374
|
1507
|
| wanted_mod `elem` excl_mods
|
|
1375
|
1508
|
= return NotThere
|
|
1376
|
1509
|
| otherwise = find_it
|
| ... |
... |
@@ -1380,112 +1513,148 @@ summariseModuleDispatch k hsc_env' home_unit is_boot (L _ wanted_mod) mb_pkg exc |
|
1380
|
1513
|
hsc_env = hscSetActiveHomeUnit home_unit hsc_env'
|
|
1381
|
1514
|
|
|
1382
|
1515
|
find_it :: IO SummariseResult
|
|
1383
|
|
-
|
|
1384
|
1516
|
find_it = do
|
|
1385
|
|
- found <- findImportedModuleWithIsBoot hsc_env wanted_mod is_boot mb_pkg
|
|
1386
|
|
- case found of
|
|
1387
|
|
- Found location mod
|
|
1388
|
|
- | moduleUnitId mod `Set.member` hsc_all_home_unit_ids hsc_env ->
|
|
1389
|
|
- -- Home package
|
|
1390
|
|
- k hsc_env location mod
|
|
1391
|
|
- | VirtUnit iud <- moduleUnit mod
|
|
1392
|
|
- , not (isHomeModule home_unit mod)
|
|
1393
|
|
- -> return $ FoundInstantiation iud
|
|
1394
|
|
- | otherwise -> return $ External (moduleUnitId mod)
|
|
1395
|
|
- _ -> return NotThere
|
|
1396
|
|
- -- Not found
|
|
1397
|
|
- -- (If it is TRULY not found at all, we'll
|
|
1398
|
|
- -- error when we actually try to compile)
|
|
1399
|
|
-
|
|
|
1517
|
+ imps_cache <- readIORef imps_cache_ref
|
|
|
1518
|
+ case M.lookup cache_key imps_cache of
|
|
|
1519
|
+ Just result -> return result
|
|
|
1520
|
+ Nothing -> do
|
|
|
1521
|
+ found <- findImportedModuleWithIsBoot hsc_env wanted_mod is_boot mb_pkg
|
|
|
1522
|
+ r <- case found of
|
|
|
1523
|
+ Found location mod
|
|
|
1524
|
+ | moduleUnitId mod `Set.member` hsc_all_home_unit_ids hsc_env ->
|
|
|
1525
|
+ -- Home package
|
|
|
1526
|
+ k hsc_env location mod
|
|
|
1527
|
+ | VirtUnit iud <- moduleUnit mod
|
|
|
1528
|
+ , not (isHomeModule home_unit mod)
|
|
|
1529
|
+ -> return $ FoundInstantiation iud
|
|
|
1530
|
+ | otherwise -> return $ External (moduleUnitId mod)
|
|
|
1531
|
+ _ -> return NotThere
|
|
|
1532
|
+ -- Not found
|
|
|
1533
|
+ -- (If it is TRULY not found at all, we'll
|
|
|
1534
|
+ -- error when we actually try to compile)
|
|
|
1535
|
+ modifyImpsCache imps_cache_ref (M.insert cache_key r)
|
|
|
1536
|
+ return r
|
|
|
1537
|
+
|
|
|
1538
|
+ cache_key = ( homeUnitId home_unit, mb_pkg
|
|
|
1539
|
+ , GWIB{ gwib_mod = wanted_mod, gwib_isBoot = is_boot })
|
|
1400
|
1540
|
|
|
1401
|
1541
|
-- | The continuation to summarise a home module if we want to find the source file
|
|
1402
|
1542
|
-- for it and potentially compile it.
|
|
1403
|
1543
|
summariseModuleWithSource
|
|
1404
|
1544
|
:: HomeUnit
|
|
1405
|
|
- -> M.Map (UnitId, OsPath) ModSummary
|
|
1406
|
|
- -- ^ Map of old summaries
|
|
|
1545
|
+ -> ModSummaryCache
|
|
|
1546
|
+ -- ^ Cache of constructed summaries
|
|
1407
|
1547
|
-> IsBootInterface -- True <=> a {-# SOURCE #-} import
|
|
1408
|
1548
|
-> Maybe (StringBuffer, UTCTime)
|
|
1409
|
1549
|
-> HscEnv
|
|
1410
|
1550
|
-> ModLocation
|
|
1411
|
1551
|
-> Module
|
|
1412
|
1552
|
-> IO SummariseResult
|
|
1413
|
|
-summariseModuleWithSource home_unit old_summary_map is_boot maybe_buf hsc_env location mod = do
|
|
1414
|
|
- -- Adjust location to point to the hs-boot source file,
|
|
1415
|
|
- -- hi file, object file, when is_boot says so
|
|
1416
|
|
- let src_fn = expectJust (ml_hs_file location)
|
|
1417
|
|
-
|
|
1418
|
|
- -- Check that it exists
|
|
1419
|
|
- -- It might have been deleted since the Finder last found it
|
|
|
1553
|
+summariseModuleWithSource home_unit summ_cache_ref is_boot maybe_buf hsc_env location mod = do
|
|
|
1554
|
+ -- Adjust location to point to the hs-boot source file,
|
|
|
1555
|
+ -- hi file, object file, when is_boot says so
|
|
|
1556
|
+ let src_fn = expectJust (ml_hs_file location)
|
|
|
1557
|
+ summ_cache <- readIORef summ_cache_ref
|
|
|
1558
|
+
|
|
|
1559
|
+ -- Reject the cache result if the module name doesn't match the inferred
|
|
|
1560
|
+ -- module name based on the file name.
|
|
|
1561
|
+ -- See (W1) in Note [Downsweep Control Flow and Caching]
|
|
|
1562
|
+ let cached = do
|
|
|
1563
|
+ p <- ml_hs_file_ospath location
|
|
|
1564
|
+ res <- M.lookup (moduleUnitId mod, p) summ_cache
|
|
|
1565
|
+ case res of
|
|
|
1566
|
+ Right (ms, _) | msKey ms /= moduleToMnk mod is_boot ->
|
|
|
1567
|
+ -- Module name doesn't match the file path name.
|
|
|
1568
|
+ -- We fall through to @new_summary@, where this will be
|
|
|
1569
|
+ -- discovered and the correct error message will be thrown.
|
|
|
1570
|
+ Nothing
|
|
|
1571
|
+ _ -> Just res
|
|
|
1572
|
+
|
|
|
1573
|
+ case cached of
|
|
|
1574
|
+ Just (Right (chd_summary, SummFresh)) ->
|
|
|
1575
|
+ -- Fresh! just return it
|
|
|
1576
|
+ pure $ FoundHome (ModuleNodeCompile chd_summary)
|
|
|
1577
|
+
|
|
|
1578
|
+ Just (Left err) ->
|
|
|
1579
|
+ -- Failure, don't try to summarise it again
|
|
|
1580
|
+ pure $ FoundHomeWithError (moduleUnitId mod, err)
|
|
|
1581
|
+
|
|
|
1582
|
+ mb_old -> do
|
|
|
1583
|
+ -- Either Nothing or a potentially old summary, must check.
|
|
|
1584
|
+
|
|
|
1585
|
+ -- Check that it exists
|
|
|
1586
|
+ -- It might have been deleted since the Finder last found it
|
|
1420
|
1587
|
maybe_h <- fileHashIfExists src_fn
|
|
1421
|
1588
|
case maybe_h of
|
|
1422
|
1589
|
-- This situation can also happen if we have found the .hs file but the
|
|
1423
|
1590
|
-- .hs-boot file doesn't exist.
|
|
1424
|
1591
|
Nothing -> return NotThere
|
|
1425
|
1592
|
Just h -> do
|
|
1426
|
|
- fresult <- new_summary_cache_check location mod src_fn h
|
|
|
1593
|
+ fresult <- case mb_old of
|
|
|
1594
|
+ Just (Right (old_summary, SummOld)) ->
|
|
|
1595
|
+ -- check the hash on the source file, and return the cached
|
|
|
1596
|
+ -- summary if it hasn't changed. If the file has changed then
|
|
|
1597
|
+ -- need to resummarise.
|
|
|
1598
|
+ case maybe_buf of
|
|
|
1599
|
+ Just (buf,_) ->
|
|
|
1600
|
+ checkSummaryHash hsc_env (new_summary location mod src_fn) old_summary location (fingerprintStringBuffer buf)
|
|
|
1601
|
+ Nothing ->
|
|
|
1602
|
+ checkSummaryHash hsc_env (new_summary location mod src_fn) old_summary location h
|
|
|
1603
|
+ Nothing ->
|
|
|
1604
|
+ new_summary location mod src_fn h
|
|
1427
|
1605
|
return $ case fresult of
|
|
1428
|
1606
|
Left err -> FoundHomeWithError (moduleUnitId mod, err)
|
|
1429
|
1607
|
Right ms -> FoundHome (ModuleNodeCompile ms)
|
|
1430
|
|
-
|
|
1431
|
1608
|
where
|
|
1432
|
1609
|
dflags = hsc_dflags hsc_env
|
|
1433
|
|
- new_summary_cache_check loc mod src_fn h
|
|
1434
|
|
- | Just old_summary <- Map.lookup ((toUnitId (moduleUnit mod), src_fn_os)) old_summary_map =
|
|
1435
|
|
-
|
|
1436
|
|
- -- check the hash on the source file, and
|
|
1437
|
|
- -- return the cached summary if it hasn't changed. If the
|
|
1438
|
|
- -- file has changed then need to resummarise.
|
|
1439
|
|
- case maybe_buf of
|
|
1440
|
|
- Just (buf,_) ->
|
|
1441
|
|
- checkSummaryHash hsc_env (new_summary loc mod src_fn) old_summary loc (fingerprintStringBuffer buf)
|
|
1442
|
|
- Nothing ->
|
|
1443
|
|
- checkSummaryHash hsc_env (new_summary loc mod src_fn) old_summary loc h
|
|
1444
|
|
- | otherwise = new_summary loc mod src_fn h
|
|
1445
|
|
- where
|
|
1446
|
|
- src_fn_os = unsafeEncodeUtf src_fn
|
|
1447
|
|
-
|
|
1448
|
1610
|
new_summary :: ModLocation
|
|
1449
|
1611
|
-> Module
|
|
1450
|
1612
|
-> FilePath
|
|
1451
|
1613
|
-> Fingerprint
|
|
1452
|
1614
|
-> IO (Either DriverMessages ModSummary)
|
|
1453
|
1615
|
new_summary location mod src_fn src_hash
|
|
1454
|
|
- = runExceptT $ do
|
|
1455
|
|
- preimps@PreprocessedImports {..}
|
|
1456
|
|
- -- Remember to set the active unit here, otherwise the wrong include paths are passed to CPP
|
|
1457
|
|
- -- See multiHomeUnits_cpp2 test
|
|
1458
|
|
- <- getPreprocessedImports (hscSetActiveUnitId (moduleUnitId mod) hsc_env) src_fn Nothing maybe_buf
|
|
1459
|
|
-
|
|
1460
|
|
- -- NB: Despite the fact that is_boot is a top-level parameter, we
|
|
1461
|
|
- -- don't actually know coming into this function what the HscSource
|
|
1462
|
|
- -- of the module in question is. This is because we may be processing
|
|
1463
|
|
- -- this module because another module in the graph imported it: in this
|
|
1464
|
|
- -- case, we know if it's a boot or not because of the {-# SOURCE #-}
|
|
1465
|
|
- -- annotation, but we don't know if it's a signature or a regular
|
|
1466
|
|
- -- module until we actually look it up on the filesystem.
|
|
1467
|
|
- let hsc_src
|
|
1468
|
|
- | is_boot == IsBoot = HsBootFile
|
|
1469
|
|
- | isHaskellSigFilename src_fn = HsigFile
|
|
1470
|
|
- | otherwise = HsSrcFile
|
|
1471
|
|
-
|
|
1472
|
|
- when (pi_mod_name /= moduleName mod) $
|
|
1473
|
|
- throwE $ singleMessage $ mkPlainErrorMsgEnvelope pi_mod_name_loc
|
|
1474
|
|
- $ DriverFileModuleNameMismatch pi_mod_name (moduleName mod)
|
|
1475
|
|
-
|
|
1476
|
|
- let instantiations = homeUnitInstantiations home_unit
|
|
1477
|
|
- when (hsc_src == HsigFile && isNothing (lookup pi_mod_name instantiations)) $
|
|
1478
|
|
- throwE $ singleMessage $ mkPlainErrorMsgEnvelope pi_mod_name_loc
|
|
1479
|
|
- $ DriverUnexpectedSignature pi_mod_name (checkBuildingCabalPackage dflags) instantiations
|
|
1480
|
|
-
|
|
1481
|
|
- liftIO $ makeNewModSummary hsc_env $ MakeNewModSummary
|
|
1482
|
|
- { nms_src_fn = src_fn
|
|
1483
|
|
- , nms_src_hash = src_hash
|
|
1484
|
|
- , nms_hsc_src = hsc_src
|
|
1485
|
|
- , nms_location = location
|
|
1486
|
|
- , nms_mod = mod
|
|
1487
|
|
- , nms_preimps = preimps
|
|
1488
|
|
- }
|
|
|
1616
|
+ = do
|
|
|
1617
|
+ res <- runExceptT $ do
|
|
|
1618
|
+ preimps@PreprocessedImports {..}
|
|
|
1619
|
+ -- Remember to set the active unit here, otherwise the wrong include paths are passed to CPP
|
|
|
1620
|
+ -- See multiHomeUnits_cpp2 test
|
|
|
1621
|
+ <- getPreprocessedImports (hscSetActiveUnitId (moduleUnitId mod) hsc_env) src_fn Nothing maybe_buf
|
|
|
1622
|
+
|
|
|
1623
|
+ -- NB: Despite the fact that is_boot is a top-level parameter, we
|
|
|
1624
|
+ -- don't actually know coming into this function what the HscSource
|
|
|
1625
|
+ -- of the module in question is. This is because we may be processing
|
|
|
1626
|
+ -- this module because another module in the graph imported it: in this
|
|
|
1627
|
+ -- case, we know if it's a boot or not because of the {-# SOURCE #-}
|
|
|
1628
|
+ -- annotation, but we don't know if it's a signature or a regular
|
|
|
1629
|
+ -- module until we actually look it up on the filesystem.
|
|
|
1630
|
+ let hsc_src
|
|
|
1631
|
+ | is_boot == IsBoot = HsBootFile
|
|
|
1632
|
+ | isHaskellSigFilename src_fn = HsigFile
|
|
|
1633
|
+ | otherwise = HsSrcFile
|
|
|
1634
|
+
|
|
|
1635
|
+ when (pi_mod_name /= moduleName mod) $
|
|
|
1636
|
+ throwE $ singleMessage $ mkPlainErrorMsgEnvelope pi_mod_name_loc
|
|
|
1637
|
+ $ DriverFileModuleNameMismatch pi_mod_name (moduleName mod)
|
|
|
1638
|
+
|
|
|
1639
|
+ let instantiations = homeUnitInstantiations home_unit
|
|
|
1640
|
+ when (hsc_src == HsigFile && isNothing (lookup pi_mod_name instantiations)) $
|
|
|
1641
|
+ throwE $ singleMessage $ mkPlainErrorMsgEnvelope pi_mod_name_loc
|
|
|
1642
|
+ $ DriverUnexpectedSignature pi_mod_name (checkBuildingCabalPackage dflags) instantiations
|
|
|
1643
|
+
|
|
|
1644
|
+ liftIO $ makeNewModSummary hsc_env $ MakeNewModSummary
|
|
|
1645
|
+ { nms_src_fn = src_fn
|
|
|
1646
|
+ , nms_src_hash = src_hash
|
|
|
1647
|
+ , nms_hsc_src = hsc_src
|
|
|
1648
|
+ , nms_location = location
|
|
|
1649
|
+ , nms_mod = mod
|
|
|
1650
|
+ , nms_preimps = preimps
|
|
|
1651
|
+ }
|
|
|
1652
|
+ modifySummCache summ_cache_ref $ case res of
|
|
|
1653
|
+ Left e -> case ml_hs_file_ospath location of
|
|
|
1654
|
+ Just p -> M.insert (moduleUnitId mod, p) (Left e)
|
|
|
1655
|
+ Nothing -> id
|
|
|
1656
|
+ Right ms -> addModSummaryCache ms SummFresh
|
|
|
1657
|
+ return res
|
|
1489
|
1658
|
|
|
1490
|
1659
|
-- | Convenience named arguments for 'makeNewModSummary' only used to make
|
|
1491
|
1660
|
-- code more readable, not exported.
|
| ... |
... |
@@ -1508,7 +1677,6 @@ makeNewModSummary hsc_env MakeNewModSummary{..} = do |
|
1508
|
1677
|
hie_timestamp <- modificationTimeIfExists (ml_hie_file_ospath nms_location)
|
|
1509
|
1678
|
bytecode_timestamp <- modificationTimeIfExists (ml_bytecode_file_ospath nms_location)
|
|
1510
|
1679
|
extra_sig_imports <- findExtraSigImports hsc_env nms_hsc_src pi_mod_name
|
|
1511
|
|
- (implicit_sigs, _inst_deps) <- implicitRequirementsShallow (hscSetActiveUnitId (moduleUnitId nms_mod) hsc_env) pi_theimps
|
|
1512
|
1680
|
|
|
1513
|
1681
|
return $
|
|
1514
|
1682
|
ModSummary
|
| ... |
... |
@@ -1522,7 +1690,6 @@ makeNewModSummary hsc_env MakeNewModSummary{..} = do |
|
1522
|
1690
|
, ms_srcimps = pi_srcimps
|
|
1523
|
1691
|
, ms_textual_imps =
|
|
1524
|
1692
|
((,,) NormalLevel NoPkgQual . noLoc <$> extra_sig_imports) ++
|
|
1525
|
|
- ((,,) NormalLevel NoPkgQual . noLoc <$> implicit_sigs) ++
|
|
1526
|
1693
|
pi_theimps
|
|
1527
|
1694
|
, ms_hs_hash = nms_src_hash
|
|
1528
|
1695
|
, ms_iface_date = hi_timestamp
|
| ... |
... |
@@ -1568,3 +1735,160 @@ getPreprocessedImports hsc_env src_fn mb_phase maybe_buf = do |
|
1568
|
1735
|
let pi_srcimps = pi_srcimps'
|
|
1569
|
1736
|
let pi_theimps = rn_imps pi_theimps'
|
|
1570
|
1737
|
return PreprocessedImports {..}
|
|
|
1738
|
+
|
|
|
1739
|
+--------------------------------------------------------------------------------
|
|
|
1740
|
+-- * Generic traversal of iteratively-built graph: dfsBuild
|
|
|
1741
|
+--------------------------------------------------------------------------------
|
|
|
1742
|
+
|
|
|
1743
|
+-- | The result of expanding a node in 'dfsBuild'.
|
|
|
1744
|
+data NodeRes v
|
|
|
1745
|
+ -- | Computed the node payload successfully
|
|
|
1746
|
+ = NSuccess v
|
|
|
1747
|
+ -- | Skip a node! This means this node doesn't produce a payload and we can
|
|
|
1748
|
+ -- just ignore it if we ever come across it.
|
|
|
1749
|
+ --
|
|
|
1750
|
+ -- In practice, this might happen because of an error or maybe from an
|
|
|
1751
|
+ -- attempt to expand e.g. an hs-boot node just to see if it sticks, but we
|
|
|
1752
|
+ -- don't distinguish these uses. Skip just means ignore this node and don't
|
|
|
1753
|
+ -- abort.
|
|
|
1754
|
+ | NSkip
|
|
|
1755
|
+
|
|
|
1756
|
+-- | In a depth-first order, and starting from the given roots, traverse a
|
|
|
1757
|
+-- graph by iteratively expanding a node into a payload and a list of children
|
|
|
1758
|
+-- nodes to visit next.
|
|
|
1759
|
+--
|
|
|
1760
|
+-- A node is NEVER visited/expanded more than once, as long as the node key
|
|
|
1761
|
+-- @k@, computed from the node @n@, uniquely identifies that node.
|
|
|
1762
|
+--
|
|
|
1763
|
+-- The first argument @base_map@ is the starting set of already visited nodes
|
|
|
1764
|
+-- (these nodes won't be expanded again!).
|
|
|
1765
|
+--
|
|
|
1766
|
+-- The result is a mapping from the key of every node transitively reachable
|
|
|
1767
|
+-- from the root nodes (inclusively) to the payload returned by expanding that
|
|
|
1768
|
+-- node. The result includes the previously visited nodes given in @base_map@,
|
|
|
1769
|
+-- s.t. @dfsBuild base_map [] _ _ == base_map@.
|
|
|
1770
|
+--
|
|
|
1771
|
+-- The @expand@ function returns an 'NResult'. See the 'NResult' documentation
|
|
|
1772
|
+-- for more information about each result type.
|
|
|
1773
|
+--
|
|
|
1774
|
+-- Error handling and exiting early can be achieved by selecting a @Monad m@
|
|
|
1775
|
+-- accordingly, such as @Control.Monad.Except.Except@
|
|
|
1776
|
+--
|
|
|
1777
|
+-- Example usage: @n@ is instanced to @DownsweepNode@, @k@ is @NodeKey@, and @v@ is @ModuleNodeEdge@.
|
|
|
1778
|
+--
|
|
|
1779
|
+-- See also Note [Downsweep Control Flow and Caching]
|
|
|
1780
|
+dfsBuild :: (Ord k, Monad m)
|
|
|
1781
|
+ => Maybe (Map.Map k (NodeRes v))
|
|
|
1782
|
+ -- ^ Base map, existing results. We won't re-expand any of the nodes
|
|
|
1783
|
+ -- already present in this map.
|
|
|
1784
|
+ -> [n]
|
|
|
1785
|
+ -- ^ The root nodes from where to start traversal
|
|
|
1786
|
+ -> (n -> k)
|
|
|
1787
|
+ -- ^ Compute the key which uniquely identifies this node
|
|
|
1788
|
+ -> (n -> m (NodeRes (v,[n])))
|
|
|
1789
|
+ -- ^ Expand this node into its payload result and into the list of
|
|
|
1790
|
+ -- children nodes to visit next.
|
|
|
1791
|
+ -> m (Map.Map k (NodeRes v))
|
|
|
1792
|
+ -- ^ The result accumulates the payload of expanding the root nodes
|
|
|
1793
|
+ -- and all nodes transitively reachable from those roots.
|
|
|
1794
|
+dfsBuild base_map roots key expand = go roots (fromMaybe Map.empty base_map)
|
|
|
1795
|
+ where
|
|
|
1796
|
+ go [] visited = pure visited
|
|
|
1797
|
+ go (s:ss) visited
|
|
|
1798
|
+ | k `Map.member` visited
|
|
|
1799
|
+ = go ss visited
|
|
|
1800
|
+ | otherwise
|
|
|
1801
|
+ = do r <- expand s
|
|
|
1802
|
+ case r of
|
|
|
1803
|
+ NSkip ->
|
|
|
1804
|
+ go ss
|
|
|
1805
|
+ (Map.insert k NSkip visited) -- Skip!
|
|
|
1806
|
+ NSuccess (v,ns) ->
|
|
|
1807
|
+ go (ns ++ ss)
|
|
|
1808
|
+ (Map.insert k (NSuccess v) visited)
|
|
|
1809
|
+ where
|
|
|
1810
|
+ k = key s
|
|
|
1811
|
+
|
|
|
1812
|
+{-
|
|
|
1813
|
+Note [Downsweep Control Flow and Caching]
|
|
|
1814
|
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
1815
|
+The control flow of downsweep is extracted into a single function `dfsBuild`,
|
|
|
1816
|
+which takes care of iteratively expanding and traversing all nodes of the
|
|
|
1817
|
+in-construction module graph necessary to build a full `ModuleGraph` at the
|
|
|
1818
|
+end.
|
|
|
1819
|
+
|
|
|
1820
|
+There are three levels of caching going on, all of which are necessary to make
|
|
|
1821
|
+sure we don't do repeated work (notably, we NEVER summarise the same module
|
|
|
1822
|
+twice).
|
|
|
1823
|
+
|
|
|
1824
|
+1. `dfsBuild` accumulates the final module graph and never revisits the
|
|
|
1825
|
+ same node of the module graph. Cache is keyed by the final
|
|
|
1826
|
+ `ModuleGraph`s `NodeKey`s.
|
|
|
1827
|
+
|
|
|
1828
|
+ For example, suppose
|
|
|
1829
|
+
|
|
|
1830
|
+ A imports B and C
|
|
|
1831
|
+ B imports D
|
|
|
1832
|
+ C imports D
|
|
|
1833
|
+
|
|
|
1834
|
+ Then, starting from A we will expand A and push B and C to the worklist;
|
|
|
1835
|
+ then, going back to B, we expand B which pushes D to the worklist. After
|
|
|
1836
|
+ processing D, we go to C, which imports D, but we have already visited that
|
|
|
1837
|
+ module so we can just use the already-constructed `ModuleGraphNode` for D.
|
|
|
1838
|
+
|
|
|
1839
|
+2. For Module A in home-unit u1, each import in the list of imports
|
|
|
1840
|
+ needs to be *found* (call to `findImportedModuleWithIsBoot`): at this
|
|
|
1841
|
+ point, we only have the `ModuleName` of the import, not the `Module`.
|
|
|
1842
|
+ This *finding* is somewhat expensive, so we cache it as well
|
|
|
1843
|
+ (`ImportsCache`). The cache key is the home-unit to which the module
|
|
|
1844
|
+ belongs~[1], the import package qualifier, and the ModuleName.
|
|
|
1845
|
+
|
|
|
1846
|
+ Same example, suppose
|
|
|
1847
|
+
|
|
|
1848
|
+ A imports B and C
|
|
|
1849
|
+ B imports D
|
|
|
1850
|
+ C imports D
|
|
|
1851
|
+
|
|
|
1852
|
+ When expanding B, we will findImportedModule "import D".
|
|
|
1853
|
+ When expanding C, we would findImportedModule "import D", but we can just
|
|
|
1854
|
+ look it up in the cache
|
|
|
1855
|
+
|
|
|
1856
|
+ [1] Different home-units will have different package flags, which means
|
|
|
1857
|
+ potentially different `Module` resolution for the same `ModuleName`.
|
|
|
1858
|
+
|
|
|
1859
|
+3. The most expensive operation we want to avoid is summarising a
|
|
|
1860
|
+ `Module` into a `ModSummary`, which notably involves parsing the
|
|
|
1861
|
+ module header from scratch.
|
|
|
1862
|
+ The third cache, in essence, maps a `Module` to its `ModSummary`
|
|
|
1863
|
+ (named `ModSummaryCache`). This cache upholds the invariant: we NEVER
|
|
|
1864
|
+ summarise the same module twice. In practice, the cache key is the
|
|
|
1865
|
+ Module's UnitId and the Source path; the reason is we need to
|
|
|
1866
|
+ distinguish between `.hs` and `.hs-boot` files, as their summaries
|
|
|
1867
|
+ will differ.
|
|
|
1868
|
+
|
|
|
1869
|
+ Note that this covers more than just (1), because we summarise all imports
|
|
|
1870
|
+ of a single module when expanding it (see 'expandModuleSummary'), before
|
|
|
1871
|
+ returning from the expansion function.
|
|
|
1872
|
+
|
|
|
1873
|
+ Note that (2) can't guarantee this alone: Two ModuleName imports in
|
|
|
1874
|
+ separate units can (and likely do) map to the same `Module`.
|
|
|
1875
|
+
|
|
|
1876
|
+(W1)
|
|
|
1877
|
+ In `summariseModuleWithSource`, on a cache hit, we must check if the module
|
|
|
1878
|
+ name matches the file name, because the cache might have been populated by
|
|
|
1879
|
+ `summariseFile`:
|
|
|
1880
|
+
|
|
|
1881
|
+ - `summariseFile` is used for summarising file targets, where
|
|
|
1882
|
+ the file name needn't match the module name: e.g., the `Main` module is
|
|
|
1883
|
+ sometimes not defined in a file named `Main.hs`.
|
|
|
1884
|
+
|
|
|
1885
|
+ - `summariseModuleWithSource` is used for summarising module targets, like
|
|
|
1886
|
+ an `import Bar`, where `Bar.hs` must contain `module Bar where`
|
|
|
1887
|
+ specifically (since we will later look for .hi files based on the module
|
|
|
1888
|
+ name).
|
|
|
1889
|
+
|
|
|
1890
|
+ See tests T27461a and T27461b.
|
|
|
1891
|
+
|
|
|
1892
|
+See also Note [Downsweep: building and maintaining the module graph] and
|
|
|
1893
|
+Note [The ModuleGraph].
|
|
|
1894
|
+-} |