@@ -25,7 +25,6 @@ import Data.Bifunctor
25
25
import qualified Data.ByteString.Base16 as B16
26
26
import Data.Either.Extra
27
27
import Data.Function
28
- import qualified Data.HashSet as HashSet
29
28
import Data.Hashable
30
29
import Data.List
31
30
import Data.IORef
@@ -65,6 +64,7 @@ import Module
65
64
import NameCache
66
65
import Packages
67
66
import Control.Exception (evaluate )
67
+ import Data.Char
68
68
69
69
-- | Given a root directory, return a Shake 'Action' which setups an
70
70
-- 'IdeGhcSession' given a file.
@@ -104,14 +104,28 @@ loadSession dir = do
104
104
105
105
return $ do
106
106
extras@ ShakeExtras {logger, eventer, restartShakeSession,
107
- withIndefiniteProgress, ideNc, knownFilesVar
107
+ withIndefiniteProgress, ideNc, knownTargetsVar
108
108
} <- getShakeExtras
109
109
110
110
IdeOptions { optTesting = IdeTesting optTesting
111
111
, optCheckProject = CheckProject checkProject
112
112
, optCustomDynFlags
113
113
} <- getIdeOptions
114
114
115
+ -- populate the knownTargetsVar with all the
116
+ -- files in the project so that `knownFiles` can learn about them and
117
+ -- we can generate a complete module graph
118
+ let extendKnownTargets newTargets = do
119
+ knownTargets <- forM newTargets $ \ TargetDetails {.. } -> do
120
+ found <- filterM (IO. doesFileExist . fromNormalizedFilePath) targetLocations
121
+ return (targetModule, found)
122
+ modifyVar_ knownTargetsVar $ traverseHashed $ \ known -> do
123
+ let known' = HM. unionWith (<>) known $ HM. fromList knownTargets
124
+ when (known /= known') $
125
+ logDebug logger $ " Known files updated: " <>
126
+ T. pack(show $ (HM. map . map ) fromNormalizedFilePath known')
127
+ evaluate known'
128
+
115
129
-- Create a new HscEnv from a hieYaml root and a set of options
116
130
-- If the hieYaml file already has an HscEnv, the new component is
117
131
-- combined with the components in the old HscEnv into a new HscEnv
@@ -212,20 +226,26 @@ loadSession dir = do
212
226
213
227
-- New HscEnv for the component in question, returns the new HscEnvEq and
214
228
-- a mapping from FilePath to the newly created HscEnvEq.
215
- let new_cache = newComponentCache logger isImplicit hscEnv uids
216
- isImplicit = isNothing hieYaml
229
+ let new_cache = newComponentCache logger hieYaml hscEnv uids
217
230
(cs, res) <- new_cache new
218
231
-- Modified cache targets for everything else in the hie.yaml file
219
232
-- which now uses the same EPS and so on
220
233
cached_targets <- concatMapM (fmap fst . new_cache) old_deps
234
+
235
+ let all_targets = cs ++ cached_targets
236
+
221
237
modifyVar_ fileToFlags $ \ var -> do
222
- pure $ Map. insert hieYaml (HM. fromList (cs ++ cached_targets)) var
238
+ pure $ Map. insert hieYaml (HM. fromList (concatMap toFlagsMap all_targets)) var
239
+
240
+ extendKnownTargets all_targets
223
241
224
242
-- Invalidate all the existing GhcSession build nodes by restarting the Shake session
225
243
invalidateShakeCache
226
244
restartShakeSession [kick]
227
245
228
- return (map fst cs ++ map fst cached_targets, second Map. keys res)
246
+ let resultCachedTargets = concatMap targetLocations all_targets
247
+
248
+ return (resultCachedTargets, second Map. keys res)
229
249
230
250
let consultCradle :: Maybe FilePath -> FilePath -> IO ([NormalizedFilePath ], (IdeResult HscEnvEq , [FilePath ]))
231
251
consultCradle hieYaml cfp = do
@@ -299,14 +319,10 @@ loadSession dir = do
299
319
void $ wait as
300
320
as <- async $ getOptions file
301
321
return (fmap snd as, wait as)
302
- unless (null cs) $
322
+ unless (null cs) $ do
323
+ cfps' <- liftIO $ filterM (IO. doesFileExist . fromNormalizedFilePath) cs
303
324
-- Typecheck all files in the project on startup
304
325
void $ shakeEnqueue extras $ mkDelayedAction " InitialLoad" Debug $ void $ do
305
- cfps' <- liftIO $ filterM (IO. doesFileExist . fromNormalizedFilePath) cs
306
- -- populate the knownFilesVar with all the
307
- -- files in the project so that `knownFiles` can learn about them and
308
- -- we can generate a complete module graph
309
- liftIO $ modifyVar_ knownFilesVar $ traverseHashed $ pure . HashSet. union (HashSet. fromList cfps')
310
326
when checkProject $ do
311
327
mmt <- uses GetModificationTime cfps'
312
328
let cs_exist = catMaybes (zipWith (<$) cfps' mmt)
@@ -320,6 +336,7 @@ loadSession dir = do
320
336
-- | Run the specific cradle on a specific FilePath via hie-bios.
321
337
-- This then builds dependencies or whatever based on the cradle, gets the
322
338
-- GHC options/dynflags needed for the session and the GHC library directory
339
+
323
340
cradleToOptsAndLibDir :: Show a => Cradle a -> FilePath
324
341
-> IO (Either [CradleError ] (ComponentOptions , FilePath ))
325
342
cradleToOptsAndLibDir cradle file = do
@@ -349,52 +366,79 @@ emptyHscEnv nc libDir = do
349
366
initDynLinker env
350
367
pure $ setNameCache nc env
351
368
352
- -- | Convert a target to a list of potential absolute paths.
353
- -- A TargetModule can be anywhere listed by the supplied include
354
- -- directories
355
- -- A target file is a relative path but with a specific prefix so just need
356
- -- to canonicalise it.
357
- targetToFile :: [FilePath ] -> TargetId -> IO [NormalizedFilePath ]
358
- targetToFile is (TargetModule mod ) = do
369
+ data TargetDetails = TargetDetails
370
+ {
371
+ targetModule :: ! ModuleName ,
372
+ targetEnv :: ! (IdeResult HscEnvEq ),
373
+ targetDepends :: ! DependencyInfo ,
374
+ targetLocations :: ! [NormalizedFilePath ]
375
+ }
376
+
377
+ fromTargetId :: [FilePath ] -- ^ import paths
378
+ -> TargetId
379
+ -> IdeResult HscEnvEq
380
+ -> DependencyInfo
381
+ -> IO [TargetDetails ]
382
+ -- For a target module we consider all the import paths
383
+ fromTargetId is (TargetModule mod ) env dep = do
359
384
let fps = [i </> moduleNameSlashes mod -<.> ext | ext <- exts, i <- is ]
360
385
exts = [" hs" , " hs-boot" , " lhs" ]
361
- mapM (fmap toNormalizedFilePath' . canonicalizePath) fps
362
- targetToFile _ (TargetFile f _) = do
363
- f' <- canonicalizePath f
364
- return [toNormalizedFilePath' f']
386
+ locs <- mapM (fmap toNormalizedFilePath' . canonicalizePath) fps
387
+ return [TargetDetails mod env dep locs]
388
+ -- For a 'TargetFile' we consider all the possible module names
389
+ fromTargetId _ (TargetFile f _) env deps = do
390
+ nf <- toNormalizedFilePath' <$> canonicalizePath f
391
+ return [TargetDetails m env deps [nf] | m <- moduleNames f]
392
+
393
+ -- >>> moduleNames "src/A/B.hs"
394
+ -- [A.B,B]
395
+ moduleNames :: FilePath -> [ModuleName ]
396
+ moduleNames f = map (mkModuleName . intercalate " ." ) $ init $ tails nameSegments
397
+ where
398
+ nameSegments = reverse
399
+ $ takeWhile (isUpper . head )
400
+ $ reverse
401
+ $ splitDirectories
402
+ $ dropExtension f
403
+
404
+ toFlagsMap :: TargetDetails -> [(NormalizedFilePath , (IdeResult HscEnvEq , DependencyInfo ))]
405
+ toFlagsMap TargetDetails {.. } =
406
+ [ (l, (targetEnv, targetDepends)) | l <- targetLocations]
407
+
365
408
366
409
setNameCache :: IORef NameCache -> HscEnv -> HscEnv
367
410
setNameCache nc hsc = hsc { hsc_NC = nc }
368
411
369
-
370
412
-- | Create a mapping from FilePaths to HscEnvEqs
371
413
newComponentCache
372
414
:: Logger
373
- -> Bool -- ^ Is this for an implicit/crappy cradle
415
+ -> Maybe FilePath -- Path to cradle
374
416
-> HscEnv
375
417
-> [(InstalledUnitId , DynFlags )]
376
418
-> ComponentInfo
377
- -> IO ([( NormalizedFilePath , ( IdeResult HscEnvEq , DependencyInfo )) ], (IdeResult HscEnvEq , DependencyInfo ))
378
- newComponentCache logger isImplicit hsc_env uids ci = do
419
+ -> IO ( [ TargetDetails ], (IdeResult HscEnvEq , DependencyInfo ))
420
+ newComponentCache logger cradlePath hsc_env uids ci = do
379
421
let df = componentDynFlags ci
380
422
let hscEnv' = hsc_env { hsc_dflags = df
381
423
, hsc_IC = (hsc_IC hsc_env) { ic_dflags = df } }
382
424
383
- let newFunc = if isImplicit then newHscEnvEqPreserveImportPaths else newHscEnvEq
425
+ let newFunc = maybe newHscEnvEqPreserveImportPaths newHscEnvEq cradlePath
384
426
henv <- newFunc hscEnv' uids
385
- let res = (([] , Just henv), componentDependencyInfo ci)
427
+ let targetEnv = ([] , Just henv)
428
+ targetDepends = componentDependencyInfo ci
429
+ res = (targetEnv, targetDepends)
386
430
logDebug logger (" New Component Cache HscEnvEq: " <> T. pack (show res))
387
431
388
- let is = importPaths df
389
- ctargets <- concatMapM (targetToFile is . targetId) (componentTargets ci)
432
+ let mk t = fromTargetId (importPaths df) (targetId t) targetEnv targetDepends
433
+ ctargets <- concatMapM mk (componentTargets ci)
434
+
390
435
-- A special target for the file which caused this wonderful
391
436
-- component to be created. In case the cradle doesn't list all the targets for
392
437
-- the component, in which case things will be horribly broken anyway.
393
438
-- Otherwise, we will immediately attempt to reload this module which
394
439
-- causes an infinite loop and high CPU usage.
395
- let special_target = (componentFP ci, res)
396
- let xs = map (,res) ctargets
397
- return (special_target: xs, res)
440
+ let special_target = TargetDetails (mkModuleName " special" ) targetEnv targetDepends [componentFP ci]
441
+ return (special_target: ctargets, res)
398
442
399
443
{- Note [Avoiding bad interface files]
400
444
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0 commit comments