@@ -68,6 +68,7 @@ import Development.IDE.Types.Options
68
68
import GHC.ResponseFile
69
69
import qualified HIE.Bios as HieBios
70
70
import HIE.Bios.Environment hiding (getCacheDir )
71
+ import qualified HIE.Bios.Flags as HieBios
71
72
import HIE.Bios.Types hiding (Log )
72
73
import qualified HIE.Bios.Types as HieBios
73
74
import Ide.Logger (Pretty (pretty ),
@@ -119,12 +120,19 @@ import qualified System.Random as Random
119
120
import System.Random (RandomGen )
120
121
import Text.ParserCombinators.ReadP (readP_to_S )
121
122
123
+ import Control.Exception.Extra (errorIO )
124
+ import qualified Data.HashMap.Strict as HashMap
125
+ import qualified Data.Tuple as Tuple
126
+ import Development.IDE.Core.OfInterest (OfInterestVar (OfInterestVar ),
127
+ getFilesOfInterest ,
128
+ getFilesOfInterestUntracked )
122
129
import GHC.Data.Bag
123
130
import GHC.Driver.Env (hsc_all_home_unit_ids )
124
131
import GHC.Driver.Errors.Types
125
132
import GHC.Types.Error (errMsgDiagnostic ,
126
133
singleMessage )
127
134
import GHC.Unit.State
135
+ import qualified HIE.Bios.Cradle as HieBios
128
136
129
137
data Log
130
138
= LogSettingInitialDynFlags
@@ -139,15 +147,18 @@ data Log
139
147
| LogMakingNewHscEnv ! [UnitId ]
140
148
| LogDLLLoadError ! String
141
149
| LogCradlePath ! FilePath
150
+ | LogCradlePaths ! [FilePath ]
142
151
| LogCradleNotFound ! FilePath
143
152
| LogSessionLoadingResult ! (Either [CradleError ] (ComponentOptions , FilePath , String ))
144
153
| LogCradle ! (Cradle Void )
145
154
| LogNoneCradleFound FilePath
146
155
| LogNewComponentCache ! (([FileDiagnostic ], Maybe HscEnvEq ), DependencyInfo )
147
156
| LogHieBios HieBios. Log
157
+
148
158
| LogSessionLoadingChanged
149
159
deriving instance Show Log
150
160
161
+
151
162
instance Pretty Log where
152
163
pretty = \ case
153
164
LogNoneCradleFound path ->
@@ -204,6 +215,8 @@ instance Pretty Log where
204
215
" Error dynamically loading libm.so.6:" <+> pretty errorString
205
216
LogCradlePath path ->
206
217
" Cradle path:" <+> pretty path
218
+ LogCradlePaths path ->
219
+ " Cradle paths:" <+> pretty path
207
220
LogCradleNotFound path ->
208
221
vcat
209
222
[ " No [cradle](https://github.com/mpickering/hie-bios#hie-bios) found for" <+> pretty path <> " ."
@@ -446,15 +459,17 @@ loadSessionWithOptions recorder SessionLoadingOptions{..} rootDir que = do
446
459
-- e.g. see https://github.com/haskell/ghcide/issues/126
447
460
let res' = toAbsolutePath <$> res
448
461
return $ normalise <$> res'
449
-
462
+ -- loadCradle in batch
463
+ let cradleLocs :: [FilePath ] -> IO [(Maybe FilePath )]
464
+ cradleLocs = mapM cradleLoc
450
465
return $ do
451
466
clientConfig <- getClientConfigAction
452
467
extras@ ShakeExtras {restartShakeSession, ideNc, knownTargetsVar, lspEnv
453
468
} <- getShakeExtras
454
469
let invalidateShakeCache = do
455
470
void $ modifyVar' version succ
456
471
return $ toNoFileKey GhcSessionIO
457
-
472
+ OfInterestVar filesOfInterVar <- getIdeGlobalAction
458
473
IdeOptions { optTesting = IdeTesting optTesting
459
474
, optCheckProject = getCheckProject
460
475
, optExtensions
@@ -582,6 +597,7 @@ loadSessionWithOptions recorder SessionLoadingOptions{..} rootDir que = do
582
597
void $ modifyVar' filesMap $ flip HM. union (HM. fromList (map ((,hieYaml) . fst ) $ concatMap toFlagsMap all_targets))
583
598
-- The VFS doesn't change on cradle edits, re-use the old one.
584
599
-- Invalidate all the existing GhcSession build nodes by restarting the Shake session
600
+ -- todo result only when batch cradle loading is done
585
601
keys2 <- invalidateShakeCache
586
602
restartShakeSession VFSUnmodified " new component" [] $ do
587
603
keys1 <- extendKnownTargets all_targets
@@ -602,28 +618,7 @@ loadSessionWithOptions recorder SessionLoadingOptions{..} rootDir que = do
602
618
603
619
return $ second Map. keys this_options
604
620
605
- let consultCradle :: Maybe FilePath -> FilePath -> IO (IdeResult HscEnvEq , [FilePath ])
606
- consultCradle hieYaml cfp = do
607
- let lfpLog = makeRelative rootDir cfp
608
- logWith recorder Info $ LogCradlePath lfpLog
609
- when (isNothing hieYaml) $
610
- logWith recorder Warning $ LogCradleNotFound lfpLog
611
- cradle <- loadCradle recorder hieYaml rootDir
612
- when optTesting $ mRunLspT lspEnv $
613
- sendNotification (SMethod_CustomMethod (Proxy @ " ghcide/cradle/loaded" )) (toJSON cfp)
614
-
615
- -- Display a user friendly progress message here: They probably don't know what a cradle is
616
- let progMsg = " Setting up " <> T. pack (takeBaseName (cradleRootDir cradle))
617
- <> " (for " <> T. pack lfpLog <> " )"
618
- eopts <- mRunLspTCallback lspEnv (\ act -> withIndefiniteProgress progMsg Nothing NotCancellable (const act)) $
619
- withTrace " Load cradle" $ \ addTag -> do
620
- addTag " file" lfpLog
621
- old_files <- readIORef cradle_files
622
- res <- cradleToOptsAndLibDir recorder (sessionLoading clientConfig) cradle cfp old_files
623
- addTag " result" (show res)
624
- return res
625
-
626
- logWith recorder Debug $ LogSessionLoadingResult eopts
621
+ let eoptsHscEnv (hieYaml, cfp, cradle, eopts) =
627
622
case eopts of
628
623
-- The cradle gave us some options so get to work turning them
629
624
-- into and HscEnv.
@@ -646,6 +641,42 @@ loadSessionWithOptions recorder SessionLoadingOptions{..} rootDir que = do
646
641
void $ modifyVar' filesMap $ HM. insert ncfp hieYaml
647
642
return (res, maybe [] pure hieYaml ++ concatMap cradleErrorDependencies err)
648
643
644
+ let consultCradles :: [(Maybe FilePath , FilePath )] -> IO [(IdeResult HscEnvEq , [FilePath ])]
645
+ consultCradles hyCfpList = do
646
+ let cfps = map snd hyCfpList
647
+ let lfpLogs = map (makeRelative rootDir . snd ) hyCfpList
648
+ logWith recorder Info $ LogCradlePaths lfpLogs
649
+ cradles <- mapM (\ (hieYaml, _) -> loadCradle recorder hieYaml rootDir) hyCfpList
650
+ when optTesting $ mRunLspT lspEnv $ mapM_ (\ (_, cfp) -> sendNotification (SMethod_CustomMethod (Proxy @ " ghcide/cradle/loaded" )) (toJSON cfp)) hyCfpList
651
+ let progMsg = " Setting up " <> T. pack (show (takeBaseName . cradleRootDir <$> cradles))
652
+ <> " (for " <> T. pack (show lfpLogs) <> " )"
653
+ eoptsList <- mRunLspTCallback lspEnv (\ act -> withIndefiniteProgress progMsg Nothing NotCancellable (const act)) $
654
+ do old_files <- readIORef cradle_files
655
+ res <- cradleToOptsAndLibDirs recorder (sessionLoading clientConfig) (zip cradles cfps) old_files
656
+ return res
657
+ mapM eoptsHscEnv (zipWith (\ ((hieYaml, fp), cr) eopts -> (hieYaml, fp, cr, eopts) ) (zip hyCfpList cradles) eoptsList)
658
+
659
+ let consultCradle :: Maybe FilePath -> FilePath -> IO (IdeResult HscEnvEq , [FilePath ])
660
+ consultCradle hieYaml cfp = do
661
+ let lfpLog = makeRelative rootDir cfp
662
+ logWith recorder Info $ LogCradlePath lfpLog
663
+ when (isNothing hieYaml) $
664
+ logWith recorder Warning $ LogCradleNotFound lfpLog
665
+ cradle <- loadCradle recorder hieYaml rootDir
666
+ when optTesting $ mRunLspT lspEnv $
667
+ sendNotification (SMethod_CustomMethod (Proxy @ " ghcide/cradle/loaded" )) (toJSON cfp)
668
+ -- Display a user friendly progress message here: They probably don't know what a cradle is
669
+ let progMsg = " Setting up " <> T. pack (takeBaseName (cradleRootDir cradle))
670
+ <> " (for " <> T. pack lfpLog <> " )"
671
+ eopts <- mRunLspTCallback lspEnv (\ act -> withIndefiniteProgress progMsg Nothing NotCancellable (const act)) $
672
+ withTrace " Load cradle" $ \ addTag -> do
673
+ addTag " file" lfpLog
674
+ old_files <- readIORef cradle_files
675
+ res <- cradleToOptsAndLibDir recorder (sessionLoading clientConfig) cradle cfp old_files
676
+ addTag " result" (show res)
677
+ return res
678
+ logWith recorder Debug $ LogSessionLoadingResult eopts
679
+ eoptsHscEnv (hieYaml, cfp, cradle, eopts)
649
680
let
650
681
-- | We allow users to specify a loading strategy.
651
682
-- Check whether this config was changed since the last time we have loaded
@@ -666,8 +697,7 @@ loadSessionWithOptions recorder SessionLoadingOptions{..} rootDir que = do
666
697
667
698
-- This caches the mapping from hie.yaml + Mod.hs -> [String]
668
699
-- Returns the Ghc session and the cradle dependencies
669
- let sessionOpts :: (Maybe FilePath , FilePath )
670
- -> IO (IdeResult HscEnvEq , [FilePath ])
700
+ let sessionOpts :: (Maybe FilePath , FilePath ) -> IO (IdeResult HscEnvEq , [FilePath ])
671
701
sessionOpts (hieYaml, file) = do
672
702
Extra. whenM didSessionLoadingPreferenceConfigChange $ do
673
703
logWith recorder Info LogSessionLoadingChanged
@@ -695,6 +725,44 @@ loadSessionWithOptions recorder SessionLoadingOptions{..} rootDir que = do
695
725
else return (opts, Map. keys old_di)
696
726
Nothing -> consultCradle hieYaml cfp
697
727
728
+ let readSessionOptsFromCache (hieYaml, file) = do
729
+ v <- Map. findWithDefault HM. empty hieYaml <$> readVar fileToFlags
730
+ let cfp = toAbsolutePath file
731
+ case HM. lookup (toNormalizedFilePath' cfp) v of
732
+ Just (opts, old_di) -> do
733
+ deps_ok <- checkDependencyInfo old_di
734
+ if not deps_ok
735
+ then do
736
+ -- If the dependencies are out of date then clear both caches and start
737
+ -- again.
738
+ modifyVar_ fileToFlags (const (return Map. empty))
739
+ modifyVar_ filesMap (const (return HM. empty))
740
+ -- Keep the same name cache
741
+ modifyVar_ hscEnvs (return . Map. adjust (const [] ) hieYaml )
742
+ return $ Left (hieYaml, file)
743
+ else return $ Right (file, (opts, Map. keys old_di))
744
+ Nothing -> return $ Left (hieYaml, file)
745
+
746
+ let sessionOptsList :: [(Maybe FilePath , FilePath )] -> IO (Map. Map FilePath (IdeResult HscEnvEq , [FilePath ]))
747
+ sessionOptsList yamlFiles = do
748
+ cached <- mapM readSessionOptsFromCache yamlFiles
749
+ let (toConsults, cachedResults) = partitionEithers cached
750
+ results <- consultCradles toConsults
751
+ let consultMap = Map. fromList $ zip (map snd toConsults) results
752
+ let cachedMap = Map. fromList cachedResults
753
+ return $ consultMap <> cachedMap
754
+
755
+
756
+ let getOptionsList :: [FilePath ] -> IO (Map. Map FilePath (IdeResult HscEnvEq , [FilePath ]))
757
+ getOptionsList files = do
758
+ let ncfps = toNormalizedFilePath' . toAbsolutePath <$> files
759
+ cachedHieYamlLocations <- mapM (\ ncfp -> HM. lookup ncfp <$> readVar filesMap) ncfps
760
+ hieYamls <- cradleLocs files
761
+ let yamlFiles = zip (zipWith (\ x y -> join x <|> y) cachedHieYamlLocations hieYamls) files
762
+ sessionOptsList yamlFiles
763
+
764
+
765
+
698
766
-- The main function which gets options for a file. We only want one of these running
699
767
-- at a time. Therefore the IORef contains the currently running cradle, if we try
700
768
-- to get some more options then we wait for the currently running action to finish
@@ -707,21 +775,40 @@ loadSessionWithOptions recorder SessionLoadingOptions{..} rootDir que = do
707
775
sessionOpts (join cachedHieYamlLocation <|> hieYaml, file) `Safe.catch` \ e ->
708
776
return (([renderPackageSetupException file e], Nothing ), maybe [] pure hieYaml)
709
777
778
+ let getOptionsBatch :: FilePath -> IO (IdeResult HscEnvEq , [FilePath ])
779
+ getOptionsBatch file' = do
780
+ let file = toAbsolutePath file'
781
+ hieYaml <- cradleLoc file
782
+ filesOfInterest <- HashMap. keys <$> readVar filesOfInterVar
783
+ logWith recorder Debug LogSettingInitialDynFlags
784
+ results <- getOptionsList (Set. toList $ Set. fromList $ toAbsolutePath file : map fromNormalizedFilePath filesOfInterest)
785
+ return (results Map. ! file) `Safe.catch` \ e ->
786
+ return (([renderPackageSetupException file e], Nothing ), maybe [] pure hieYaml)
787
+
710
788
returnWithVersion $ \ file -> do
711
789
-- see Note [Serializing runs in separate thread]
712
- awaitRunInThread que $ getOptions file
713
-
714
- -- | Run the specific cradle on a specific FilePath via hie-bios.
715
- -- This then builds dependencies or whatever based on the cradle, gets the
716
- -- GHC options/dynflags needed for the session and the GHC library directory
717
- cradleToOptsAndLibDir :: Recorder (WithPriority Log ) -> SessionLoadingPreferenceConfig -> Cradle Void -> FilePath -> [FilePath ]
718
- -> IO (Either [CradleError ] (ComponentOptions , FilePath , String ))
719
- cradleToOptsAndLibDir recorder loadConfig cradle file old_fps = do
720
- -- let noneCradleFoundMessage :: FilePath -> T.Text
721
- -- noneCradleFoundMessage f = T.pack $ "none cradle found for " <> f <> ", ignoring the file"
722
- -- Start off by getting the session options
723
- logWith recorder Debug $ LogCradle cradle
724
- cradleRes <- HieBios. getCompilerOptions file loadStyle cradle
790
+ awaitRunInThread que $ getOptionsBatch file
791
+
792
+ -- cradleToType :: Cradle Void -> IO ()
793
+ -- cradleToType c = do
794
+ -- case c of
795
+ -- Cradle { cradleOptsProg = Just (GHC.GhcVersion _), cradleOptsProg = Just (GHC.GhcVersion _) } -> return ()
796
+ -- _ ->
797
+ -- return ()
798
+
799
+ -- how we do batch loading of cradles depends on the the type of cradle we are using
800
+ cradleToOptsAndLibDirs :: Recorder (WithPriority Log ) -> SessionLoadingPreferenceConfig -> [(Cradle Void , FilePath )] -> [FilePath ]
801
+ -> IO [Either [CradleError ] (ComponentOptions , FilePath , String )]
802
+ cradleToOptsAndLibDirs recorder loadConfig cradleFiles old_fps = do
803
+ -- let result :: [([FilePath], CradleLoadResult ComponentOptions)]
804
+ results <- HieBios. getCompilerOptionsInBatch (LoadWithContext old_fps) cradleFiles
805
+ let resultMap :: Map. Map FilePath (CradleLoadResult ComponentOptions )
806
+ resultMap = Map. fromList $ [ (fp, r) | (fps, r) <- results, fp <- fps ]
807
+ mapM (\ (cr, fp) -> collectBiosResult recorder cr fp (resultMap Map. ! fp)) cradleFiles
808
+
809
+
810
+ collectBiosResult :: Recorder (WithPriority Log ) -> Cradle a1 -> FilePath -> CradleLoadResult a2 -> IO (Either [CradleError ] (a2 , FilePath , String ))
811
+ collectBiosResult recorder cradle file cradleRes =
725
812
case cradleRes of
726
813
CradleSuccess r -> do
727
814
-- Now get the GHC lib dir
@@ -734,12 +821,23 @@ cradleToOptsAndLibDir recorder loadConfig cradle file old_fps = do
734
821
CradleNone -> do
735
822
logWith recorder Info $ LogNoneCradleFound file
736
823
return (Left [] )
737
-
738
824
CradleFail err -> return (Left [err])
739
825
CradleNone -> do
740
826
logWith recorder Info $ LogNoneCradleFound file
741
827
return (Left [] )
742
828
829
+ -- | Run the specific cradle on a specific FilePath via hie-bios.
830
+ -- This then builds dependencies or whatever based on the cradle, gets the
831
+ -- GHC options/dynflags needed for the session and the GHC library directory
832
+ cradleToOptsAndLibDir :: Recorder (WithPriority Log ) -> SessionLoadingPreferenceConfig -> Cradle Void -> FilePath -> [FilePath ]
833
+ -> IO (Either [CradleError ] (ComponentOptions , FilePath , String ))
834
+ cradleToOptsAndLibDir recorder loadConfig cradle file old_fps = do
835
+ -- let noneCradleFoundMessage :: FilePath -> T.Text
836
+ -- noneCradleFoundMessage f = T.pack $ "none cradle found for " <> f <> ", ignoring the file"
837
+ -- Start off by getting the session options
838
+ logWith recorder Debug $ LogCradle cradle
839
+ cradleRes <- HieBios. getCompilerOptions file loadStyle cradle
840
+ collectBiosResult recorder cradle file cradleRes
743
841
where
744
842
loadStyle = case loadConfig of
745
843
PreferSingleComponentLoading -> LoadFile
@@ -1212,7 +1310,8 @@ instance Exception PackageSetupException
1212
1310
1213
1311
showPackageSetupException :: PackageSetupException -> String
1214
1312
showPackageSetupException GhcVersionMismatch {.. } = unwords
1215
- [" ghcide compiled against GHC"
1313
+ [
1314
+ " ghcide compiled against GHC"
1216
1315
,showVersion compileTime
1217
1316
," but currently using"
1218
1317
,showVersion runTime
0 commit comments