@@ -25,6 +25,11 @@ module HIE.Bios.Cradle (
25
25
, makeCradleResult
26
26
-- | Cradle project configuration types
27
27
, CradleProjectConfig (.. )
28
+
29
+ -- expose to tests
30
+ , makeVersions
31
+ , isCabalMultipleCompSupported
32
+ , ProgramVersions
28
33
) where
29
34
30
35
import Control.Applicative ((<|>) , optional )
@@ -47,9 +52,10 @@ import qualified Data.Conduit.Combinators as C
47
52
import qualified Data.Conduit as C
48
53
import qualified Data.Conduit.Text as C
49
54
import qualified Data.HashMap.Strict as Map
55
+ import qualified Data.HashSet as S
50
56
import Data.Maybe (fromMaybe , maybeToList )
51
57
import Data.List
52
- import Data.List.Extra (trimEnd )
58
+ import Data.List.Extra (trimEnd , nubOrd )
53
59
import Data.Ord (Down (.. ))
54
60
import qualified Data.Text as T
55
61
import System.Environment
@@ -73,6 +79,7 @@ import GHC.ResponseFile (escapeArgs)
73
79
import Data.Version
74
80
import Data.IORef
75
81
import Text.ParserCombinators.ReadP (readP_to_S )
82
+ import Data.Tuple.Extra (fst3 , snd3 , thd3 )
76
83
77
84
----------------------------------------------------------------
78
85
@@ -129,6 +136,7 @@ data ConcreteCradle a
129
136
| ConcreteOther a
130
137
deriving Show
131
138
139
+
132
140
-- | ConcreteCradle augmented with information on which file the
133
141
-- cradle applies
134
142
data ResolvedCradle a
@@ -243,7 +251,7 @@ resolvedCradlesToCradle logger buildCustomCradle root cs = mdo
243
251
case selectCradle (prefix . fst ) absfp cradleActions of
244
252
Just (rc, act) -> do
245
253
addActionDeps (cradleDeps rc) <$> runCradle act fp prev
246
- Nothing -> return $ CradleFail $ CradleError [] ExitSuccess (err_msg fp)
254
+ Nothing -> return $ CradleFail $ CradleError [] ExitSuccess (err_msg fp) [fp]
247
255
, runGhcCmd = run_ghc_cmd
248
256
}
249
257
}
@@ -518,7 +526,7 @@ biosAction wdir bios bios_deps l fp loadStyle = do
518
526
-- delimited by newlines.
519
527
-- Execute the bios action and add dependencies of the cradle.
520
528
-- Removes all duplicates.
521
- return $ makeCradleResult (ex, std, wdir, fromMaybe [] res) deps
529
+ return $ makeCradleResult (ex, std, wdir, fromMaybe [] res) deps [fp]
522
530
523
531
callableToProcess :: Callable -> Maybe String -> IO CreateProcess
524
532
callableToProcess (Command shellCommand) file = do
@@ -788,6 +796,15 @@ cabalGhcDirs l cabalProject workDir = do
788
796
where
789
797
projectFileArgs = projectFileProcessArgs cabalProject
790
798
799
+ isCabalMultipleCompSupported :: MonadIO m => ProgramVersions -> m Bool
800
+ isCabalMultipleCompSupported vs = do
801
+ cabal_version <- liftIO $ runCachedIO $ cabalVersion vs
802
+ ghc_version <- liftIO $ runCachedIO $ ghcVersion vs
803
+ -- determine which load style is supported by this cabal cradle.
804
+ case (cabal_version, ghc_version) of
805
+ (Just cabal, Just ghc) -> pure $ ghc >= makeVersion [9 , 4 ] && cabal >= makeVersion [3 , 11 ]
806
+ _ -> pure False
807
+
791
808
cabalAction
792
809
:: ResolvedCradles a
793
810
-> FilePath
@@ -798,84 +815,84 @@ cabalAction
798
815
-> LoadStyle
799
816
-> CradleLoadResultT IO ComponentOptions
800
817
cabalAction (ResolvedCradles root cs vs) workDir mc l projectFile fp loadStyle = do
801
- cabal_version <- liftIO $ runCachedIO $ cabalVersion vs
802
- ghc_version <- liftIO $ runCachedIO $ ghcVersion vs
818
+ multiCompSupport <- isCabalMultipleCompSupported vs
803
819
-- determine which load style is supported by this cabal cradle.
804
- determinedLoadStyle <- case (cabal_version, ghc_version) of
805
- (Just cabal, Just ghc)
806
- -- Multi-component supported from cabal-install 3.11
807
- -- and ghc 9.4
808
- | LoadWithContext _ <- loadStyle ->
809
- if ghc >= makeVersion [9 ,4 ] && cabal >= makeVersion [3 ,11 ]
810
- then pure loadStyle
811
- else do
812
- liftIO $ l <& WithSeverity
813
- (LogLoadWithContextUnsupported " cabal"
814
- $ Just " cabal or ghc version is too old. We require `cabal >= 3.11` and `ghc >= 9.4`"
815
- )
816
- Warning
817
- pure LoadFile
818
- _ -> pure LoadFile
819
-
820
- let cabalArgs = case determinedLoadStyle of
821
- LoadFile -> [fromMaybe (fixTargetPath fp) mc]
822
- LoadWithContext fps -> concat
823
- [ [ " --keep-temp-files"
824
- , " --enable-multi-repl"
825
- , fromMaybe (fixTargetPath fp) mc
826
- ]
827
- , [fromMaybe (fixTargetPath old_fp) old_mc
828
- | old_fp <- fps
829
- -- Lookup the component for the old file
830
- , Just (ResolvedCradle {concreteCradle = ConcreteCabal ct}) <- [selectCradle prefix old_fp cs]
831
- -- Only include this file if the old component is in the same project
832
- , (projectConfigFromMaybe root (cabalProjectFile ct)) == projectFile
833
- , let old_mc = cabalComponent ct
834
- ]
835
- ]
820
+ determinedLoadStyle <- case loadStyle of
821
+ LoadWithContext _ | not multiCompSupport -> do
822
+ liftIO $
823
+ l
824
+ <& WithSeverity
825
+ ( LogLoadWithContextUnsupported " cabal" $
826
+ Just " cabal or ghc version is too old. We require `cabal >= 3.11` and `ghc >= 9.4`"
827
+ )
828
+ Warning
829
+ pure LoadFile
830
+ _ -> pure loadStyle
831
+
832
+ let fpModule = fromMaybe (fixTargetPath fp) mc
833
+ let (cabalArgs, loadingFiles, extraDeps) = case determinedLoadStyle of
834
+ LoadFile -> ([fpModule], [fp], [] )
835
+ LoadWithContext fps ->
836
+ let allModulesFpsDeps = ((fpModule, fp, [] ) : moduleFilesFromSameProject fps)
837
+ allModules = nubOrd $ fst3 <$> allModulesFpsDeps
838
+ allFiles = nubOrd $ snd3 <$> allModulesFpsDeps
839
+ allFpsDeps = nubOrd $ concatMap thd3 allModulesFpsDeps
840
+ in ([" --keep-temp-files" , " --enable-multi-repl" ] ++ allModules, allFiles, allFpsDeps)
836
841
837
842
liftIO $ l <& LogComputedCradleLoadStyle " cabal" determinedLoadStyle `WithSeverity ` Info
843
+ liftIO $ l <& LogCabalLoad fp mc (prefix <$> cs) loadingFiles `WithSeverity ` Debug
838
844
839
- let
840
- cabalCommand = " v2-repl"
845
+ let cabalCommand = " v2-repl"
841
846
842
- cabalProc <- cabalProcess l projectFile workDir cabalCommand cabalArgs `modCradleError` \ err -> do
847
+ cabalProc <-
848
+ cabalProcess l projectFile workDir cabalCommand cabalArgs `modCradleError` \ err -> do
843
849
deps <- cabalCradleDependencies projectFile workDir workDir
844
- pure $ err { cradleErrorDependencies = cradleErrorDependencies err ++ deps }
850
+ pure $ err {cradleErrorDependencies = cradleErrorDependencies err ++ deps}
845
851
846
852
(ex, output, stde, [(_, maybeArgs)]) <- liftIO $ readProcessWithOutputs [hie_bios_output] l workDir cabalProc
847
853
let args = fromMaybe [] maybeArgs
848
854
849
855
let errorDetails =
850
- [" Failed command: " <> prettyCmdSpec (cmdspec cabalProc)
851
- , unlines output
852
- , unlines stde
853
- , unlines $ args
854
- , " Process Environment:" ]
855
- <> prettyProcessEnv cabalProc
856
+ [ " Failed command: " <> prettyCmdSpec (cmdspec cabalProc),
857
+ unlines output,
858
+ unlines stde,
859
+ unlines args,
860
+ " Process Environment:"
861
+ ]
862
+ <> prettyProcessEnv cabalProc
856
863
857
864
when (ex /= ExitSuccess ) $ do
858
865
deps <- liftIO $ cabalCradleDependencies projectFile workDir workDir
859
866
let cmd = show ([" cabal" , cabalCommand] <> cabalArgs)
860
867
let errorMsg = " Failed to run " <> cmd <> " in directory \" " <> workDir <> " \" . Consult the logs for full command and error."
861
- throwCE (CradleError deps ex ([errorMsg] <> errorDetails))
868
+ throwCE (CradleError deps ex ([errorMsg] <> errorDetails) loadingFiles )
862
869
863
870
case processCabalWrapperArgs args of
864
871
Nothing -> do
865
872
-- Provide some dependencies an IDE can look for to trigger a reload.
866
873
-- Best effort. Assume the working directory is the
867
874
-- root of the component, so we are right in trivial cases at least.
868
875
deps <- liftIO $ cabalCradleDependencies projectFile workDir workDir
869
- throwCE (CradleError deps ex $ [" Failed to parse result of calling cabal" ] <> errorDetails)
876
+ throwCE (CradleError ( deps <> extraDeps) ex ( [" Failed to parse result of calling cabal" ] <> errorDetails) loadingFiles )
870
877
Just (componentDir, final_args) -> do
871
878
deps <- liftIO $ cabalCradleDependencies projectFile workDir componentDir
872
- CradleLoadResultT $ pure $ makeCradleResult (ex, stde, componentDir, final_args) deps
879
+ CradleLoadResultT $ pure $ makeCradleResult (ex, stde, componentDir, final_args) ( deps <> extraDeps) loadingFiles
873
880
where
874
881
-- Need to make relative on Windows, due to a Cabal bug with how it
875
- -- parses file targets with a C: drive in it
882
+ -- parses file targets with a C: drive in it. So we decide to make
883
+ -- the paths relative to the working directory.
876
884
fixTargetPath x
877
885
| isWindows && hasDrive x = makeRelative workDir x
878
886
| otherwise = x
887
+ moduleFilesFromSameProject fps =
888
+ [ (fromMaybe (fixTargetPath file) old_mc, file, deps)
889
+ | file <- fps,
890
+ -- Lookup the component for the old file
891
+ Just (ResolvedCradle {concreteCradle = ConcreteCabal ct, cradleDeps = deps}) <- [selectCradle prefix file cs],
892
+ -- Only include this file if the old component is in the same project
893
+ (projectConfigFromMaybe root (cabalProjectFile ct)) == projectFile,
894
+ let old_mc = cabalComponent ct
895
+ ]
879
896
880
897
removeInteractive :: [String ] -> [String ]
881
898
removeInteractive = filter (/= " --interactive" )
@@ -928,7 +945,7 @@ cabalWorkDir wdir =
928
945
data CradleProjectConfig
929
946
= NoExplicitConfig
930
947
| ExplicitConfig FilePath
931
- deriving Eq
948
+ deriving ( Eq , Show )
932
949
933
950
-- | Create an explicit project configuration. Expects a working directory
934
951
-- followed by an optional name of the project configuration.
@@ -987,7 +1004,7 @@ stackAction
987
1004
-> FilePath
988
1005
-> LoadStyle
989
1006
-> IO (CradleLoadResult ComponentOptions )
990
- stackAction workDir mc syaml l _fp loadStyle = do
1007
+ stackAction workDir mc syaml l fp loadStyle = do
991
1008
logCradleHasNoSupportForLoadWithContext l loadStyle " stack"
992
1009
let ghcProcArgs = (" stack" , stackYamlProcessArgs syaml <> [" exec" , " ghc" , " --" ])
993
1010
-- Same wrapper works as with cabal
@@ -1011,10 +1028,11 @@ stackAction workDir mc syaml l _fp loadStyle = do
1011
1028
-- the root of the component, so we are right in trivial cases at least.
1012
1029
deps <- stackCradleDependencies workDir workDir syaml
1013
1030
pure $ CradleFail
1014
- (CradleError deps ex1 $
1015
- [ " Failed to parse result of calling stack" ]
1031
+ (CradleError deps ex1
1032
+ ( [ " Failed to parse result of calling stack" ]
1016
1033
++ stde
1017
- ++ args
1034
+ ++ args)
1035
+ [fp]
1018
1036
)
1019
1037
1020
1038
Just (componentDir, ghc_args) -> do
@@ -1025,6 +1043,7 @@ stackAction workDir mc syaml l _fp loadStyle = do
1025
1043
, ghc_args ++ pkg_ghc_args
1026
1044
)
1027
1045
deps
1046
+ [fp]
1028
1047
1029
1048
stackProcess :: CradleProjectConfig -> [String ] -> CreateProcess
1030
1049
stackProcess syaml args = proc " stack" $ stackYamlProcessArgs syaml <> args
@@ -1217,10 +1236,10 @@ removeFileIfExists f = do
1217
1236
yes <- doesFileExist f
1218
1237
when yes (removeFile f)
1219
1238
1220
- makeCradleResult :: (ExitCode , [String ], FilePath , [String ]) -> [FilePath ] -> CradleLoadResult ComponentOptions
1221
- makeCradleResult (ex, err, componentDir, gopts) deps =
1239
+ makeCradleResult :: (ExitCode , [String ], FilePath , [String ]) -> [FilePath ] -> [ FilePath ] -> CradleLoadResult ComponentOptions
1240
+ makeCradleResult (ex, err, componentDir, gopts) deps loadingFiles =
1222
1241
case ex of
1223
- ExitFailure _ -> CradleFail (CradleError deps ex err)
1242
+ ExitFailure _ -> CradleFail (CradleError deps ex err loadingFiles )
1224
1243
_ ->
1225
1244
let compOpts = ComponentOptions gopts componentDir deps
1226
1245
in CradleSuccess compOpts
@@ -1252,11 +1271,13 @@ readProcessWithCwd' l createdProcess stdin = do
1252
1271
case mResult of
1253
1272
Just (ExitSuccess , stdo, _) -> pure stdo
1254
1273
Just (exitCode, stdo, stde) -> throwCE $
1255
- CradleError [] exitCode $
1256
- [" Error when calling " <> cmdString, stdo, stde] <> prettyProcessEnv createdProcess
1274
+ CradleError [] exitCode
1275
+ ([" Error when calling " <> cmdString, stdo, stde] <> prettyProcessEnv createdProcess)
1276
+ []
1257
1277
Nothing -> throwCE $
1258
- CradleError [] ExitSuccess $
1259
- [" Couldn't execute " <> cmdString] <> prettyProcessEnv createdProcess
1278
+ CradleError [] ExitSuccess
1279
+ ([" Couldn't execute " <> cmdString] <> prettyProcessEnv createdProcess)
1280
+ []
1260
1281
1261
1282
-- | Log that the cradle has no supported for loading with context, if and only if
1262
1283
-- 'LoadWithContext' was requested.
0 commit comments