@@ -70,7 +70,7 @@ import qualified HIE.Bios.Types as Types
7070import qualified HIE.Bios.Ghc.Gap as Gap
7171
7272import GHC.Fingerprint (fingerprintString )
73- import GHC.ResponseFile (escapeArgs )
73+ import GHC.ResponseFile (escapeArgs , expandResponse )
7474
7575import Data.Version
7676import Data.IORef
@@ -612,10 +612,64 @@ cabalCradle l cs wdir mc projectFile
612612 -- ./dist-newstyle/tmp/environment.-24811: createDirectory: does not exist (No such file or directory)
613613 liftIO $ createDirectoryIfMissing True (buildDir </> " tmp" )
614614 -- Need to pass -v0 otherwise we get "resolving dependencies..."
615- cabalProc <- cabalProcess l vs projectFile wdir " v2-exec " $ [ " ghc " , " -v0 " , " -- " ] ++ args
615+ cabalProc <- cabalExecGhc l vs projectFile wdir args
616616 Process. readProcessWithCwd' l cabalProc " "
617617 }
618618
619+ cabalExecGhc :: LogAction IO (WithSeverity Log ) -> ProgramVersions -> CradleProjectConfig -> FilePath -> [String ] -> CradleLoadResultT IO CreateProcess
620+ cabalExecGhc l vs projectFile wdir args = do
621+ cabalProcess l vs projectFile wdir " v2-exec" $ [" ghc" , " -v0" , " --" ] ++ args
622+
623+ processCabalLoadStyle :: MonadIO m => LogAction IO (WithSeverity Log ) -> ResolvedCradles a -> CradleProjectConfig -> [Char ] -> Maybe FilePath -> [Char ] -> LoadStyle -> m ([FilePath ], [FilePath ], [FilePath ])
624+ processCabalLoadStyle l cradles projectFile workDir mc fp loadStyle = do
625+ let fpModule = fromMaybe (fixTargetPath fp) mc
626+ let (cabalArgs, loadingFiles, extraDeps) = case loadStyle of
627+ LoadFile -> ([fpModule], [fp], [] )
628+ LoadWithContext fps ->
629+ let allModulesFpsDeps = ((fpModule, fp, [] ) : moduleFilesFromSameProject fps)
630+ allModules = nubOrd $ fst3 <$> allModulesFpsDeps
631+ allFiles = nubOrd $ snd3 <$> allModulesFpsDeps
632+ allFpsDeps = nubOrd $ concatMap thd3 allModulesFpsDeps
633+ in ([" --keep-temp-files" , " --enable-multi-repl" ] ++ allModules, allFiles, allFpsDeps)
634+
635+ liftIO $ l <& LogComputedCradleLoadStyle " cabal" loadStyle `WithSeverity ` Info
636+ liftIO $ l <& LogCabalLoad fp mc (prefix <$> resolvedCradles cradles) loadingFiles `WithSeverity ` Debug
637+ pure (cabalArgs, loadingFiles, extraDeps)
638+ where
639+ -- Need to make relative on Windows, due to a Cabal bug with how it
640+ -- parses file targets with a C: drive in it. So we decide to make
641+ -- the paths relative to the working directory.
642+ fixTargetPath x
643+ | isWindows && hasDrive x = makeRelative workDir x
644+ | otherwise = x
645+ moduleFilesFromSameProject fps =
646+ [ (fromMaybe (fixTargetPath file) old_mc, file, deps)
647+ | file <- fps,
648+ -- Lookup the component for the old file
649+ Just (ResolvedCradle {concreteCradle = ConcreteCabal ct, cradleDeps = deps}) <- [selectCradle prefix file (resolvedCradles cradles)],
650+ -- Only include this file if the old component is in the same project
651+ (projectConfigFromMaybe (cradleRoot cradles) (cabalProjectFile ct)) == projectFile,
652+ let old_mc = cabalComponent ct
653+ ]
654+
655+ cabalLoadFilesBefore315 :: LogAction IO (WithSeverity Log ) -> ProgramVersions -> CradleProjectConfig -> [Char ] -> [String ] -> CradleLoadResultT IO CreateProcess
656+ cabalLoadFilesBefore315 l progVersions projectFile workDir args = do
657+ let cabalCommand = " v2-repl"
658+
659+ cabalProcess l progVersions projectFile workDir cabalCommand args `modCradleError` \ err -> do
660+ deps <- cabalCradleDependencies projectFile workDir workDir
661+ pure $ err {cradleErrorDependencies = cradleErrorDependencies err ++ deps}
662+
663+ cabalLoadFilesWithRepl :: LogAction IO (WithSeverity Log ) -> CradleProjectConfig -> FilePath -> [String ] -> CradleLoadResultT IO CreateProcess
664+ cabalLoadFilesWithRepl l projectFile workDir args = do
665+ let cabalCommand = " v2-repl"
666+
667+ newEnvironment <- liftIO Process. getCleanEnvironment
668+ wrapper_fp <- liftIO $ withReplWrapperTool l (proc " ghc" ) workDir
669+ pure $ (proc " cabal" ([cabalCommand, " --keep-temp-files" , " --with-repl" , wrapper_fp] <> projectFileProcessArgs projectFile <> args))
670+ { env = Just newEnvironment
671+ , cwd = Just workDir
672+ }
619673
620674-- | Execute a cabal process in our custom cache-build directory configured
621675-- with the custom ghc executable.
@@ -653,7 +707,7 @@ cabalProcess l vs cabalProject workDir command args = do
653707
654708 setupCabalCommand :: FilePath -> IO CreateProcess
655709 setupCabalCommand ghcPkgPath = do
656- wrapper_fp <- withGhcWrapperTool l (" ghc" , [] ) workDir
710+ wrapper_fp <- withGhcWrapperTool l (proc " ghc" ) workDir
657711 buildDir <- cabalBuildDir workDir
658712 let extraCabalArgs =
659713 [ " --builddir=" <> buildDir
@@ -688,7 +742,7 @@ withGhcPkgTool ghcPathAbs libdir = do
688742 ghcPkgPath = guessGhcPkgFromGhc ghcName
689743 if isWindows
690744 then pure ghcPkgPath
691- else withWrapperTool ghcPkgPath
745+ else withGhcPkgShim ghcPkgPath
692746 where
693747 ghcDir = takeDirectory ghcPathAbs
694748
@@ -707,7 +761,7 @@ withGhcPkgTool ghcPathAbs libdir = do
707761 --
708762 -- If we used the raw executable, i.e. not wrapped in a shim, then 'cabal'
709763 -- can not use the given 'ghc-pkg'.
710- withWrapperTool ghcPkg = do
764+ withGhcPkgShim ghcPkg = do
711765 let globalPackageDb = libdir </> " package.conf.d"
712766 -- This is the same as the wrapper-shims ghc-pkg usually comes with.
713767 contents = unlines
@@ -762,32 +816,40 @@ processCabalWrapperArgs args =
762816 in Just (dir, final_args)
763817 _ -> Nothing
764818
765- -- | GHC process information.
766- -- Consists of the filepath to the ghc executable and
767- -- arguments to the executable.
768- type GhcProc = (FilePath , [String ])
819+ -- | GHC process that accepts GHC arguments.
820+ type GhcProc = [String ] -> CreateProcess
769821
770822-- | Generate a fake GHC that can be passed to cabal or stack
771823-- when run with --interactive, it will print out its
772824-- command-line arguments and exit
773825withGhcWrapperTool :: LogAction IO (WithSeverity Log ) -> GhcProc -> FilePath -> IO FilePath
774- withGhcWrapperTool l (mbGhc, ghcArgs) wdir = do
775- let wrapperContents = if isWindows then cabalWrapperHs else cabalWrapper
776- withExtension fp = if isWindows then fp <.> " exe" else fp
777- srcHash = show (fingerprintString wrapperContents)
778- cacheFile (withExtension " wrapper" ) srcHash $ \ wrapper_fp ->
779- if isWindows
780- then
781- withSystemTempDirectory " hie-bios" $ \ tmpDir -> do
782- let wrapper_hs = wrapper_fp -<.> " hs"
783- writeFile wrapper_hs wrapperContents
784- let ghcArgsWithExtras = ghcArgs ++ [" -rtsopts=ignore" , " -outputdir" , tmpDir, " -o" , wrapper_fp, wrapper_hs]
785- let ghcProc = (proc mbGhc ghcArgsWithExtras)
786- { cwd = Just wdir
787- }
788- l <& LogCreateProcessRun ghcProc `WithSeverity ` Debug
789- readCreateProcess ghcProc " " >>= putStr
790- else writeFile wrapper_fp wrapperContents
826+ withGhcWrapperTool l mkGhcCall wdir = do
827+ withWrapperTool l mkGhcCall wdir " wrapper" cabalWrapperHs cabalWrapper
828+
829+ -- | Generate a script/binary that can be passed to cabal's '--with-repl'.
830+ -- On windows, this compiles a Haskell file, while on other systems, we persist
831+ withReplWrapperTool :: LogAction IO (WithSeverity Log ) -> GhcProc -> FilePath -> IO FilePath
832+ withReplWrapperTool l mkGhcCall wdir =
833+ withWrapperTool l mkGhcCall wdir " repl-wrapper" cabalWithReplWrapperHs cabalWithReplWrapper
834+
835+ withWrapperTool :: LogAction IO (WithSeverity Log ) -> GhcProc -> String -> FilePath -> String -> String -> IO FilePath
836+ withWrapperTool l mkGhcCall wdir baseName windowsWrapper unixWrapper = do
837+ let wrapperContents = if isWindows then windowsWrapper else unixWrapper
838+ withExtension fp = if isWindows then fp <.> " exe" else fp
839+ srcHash = show (fingerprintString wrapperContents)
840+ cacheFile (withExtension baseName) srcHash $ \ wrapper_fp ->
841+ if isWindows
842+ then
843+ withSystemTempDirectory " hie-bios" $ \ tmpDir -> do
844+ let wrapper_hs = wrapper_fp -<.> " hs"
845+ writeFile wrapper_hs wrapperContents
846+ let ghcArgs = [" -rtsopts=ignore" , " -outputdir" , tmpDir, " -o" , wrapper_fp, wrapper_hs]
847+ let ghcProc = (mkGhcCall ghcArgs)
848+ { cwd = Just wdir
849+ }
850+ l <& LogCreateProcessRun ghcProc `WithSeverity ` Debug
851+ readCreateProcess ghcProc " " >>= putStr
852+ else writeFile wrapper_fp wrapperContents
791853
792854-- | Create and cache a file in hie-bios's cache directory.
793855--
@@ -885,6 +947,20 @@ isCabalMultipleCompSupported vs = do
885947 (Just cabal, Just ghc) -> pure $ ghc >= makeVersion [9 , 4 ] && cabal >= makeVersion [3 , 11 ]
886948 _ -> pure False
887949
950+ data CabalLoadFeature
951+ = CabalWithRepl
952+ | CabalWithGhcShimWrapper
953+
954+ determineCabalLoadFeature :: MonadIO m => ProgramVersions -> m CabalLoadFeature
955+ determineCabalLoadFeature vs = do
956+ cabal_version <- liftIO $ runCachedIO $ cabalVersion vs
957+ -- determine which load style is supported by this cabal cradle.
958+ case cabal_version of
959+ Just ver
960+ | ver >= makeVersion [3 , 15 ] -> pure CabalWithRepl
961+ | otherwise -> pure CabalWithGhcShimWrapper
962+ _ -> pure CabalWithGhcShimWrapper
963+
888964cabalAction
889965 :: ResolvedCradles a
890966 -> FilePath
@@ -895,7 +971,8 @@ cabalAction
895971 -> LoadStyle
896972 -> CradleLoadResultT IO ComponentOptions
897973cabalAction cradles workDir mc l projectFile fp loadStyle = do
898- multiCompSupport <- isCabalMultipleCompSupported (cradleProgramVersions cradles)
974+ let progVersions = cradleProgramVersions cradles
975+ multiCompSupport <- isCabalMultipleCompSupported progVersions
899976 -- determine which load style is supported by this cabal cradle.
900977 determinedLoadStyle <- case loadStyle of
901978 LoadWithContext _ | not multiCompSupport -> do
@@ -909,25 +986,12 @@ cabalAction cradles workDir mc l projectFile fp loadStyle = do
909986 pure LoadFile
910987 _ -> pure loadStyle
911988
912- let fpModule = fromMaybe (fixTargetPath fp) mc
913- let (cabalArgs, loadingFiles, extraDeps) = case determinedLoadStyle of
914- LoadFile -> ([fpModule], [fp], [] )
915- LoadWithContext fps ->
916- let allModulesFpsDeps = ((fpModule, fp, [] ) : moduleFilesFromSameProject fps)
917- allModules = nubOrd $ fst3 <$> allModulesFpsDeps
918- allFiles = nubOrd $ snd3 <$> allModulesFpsDeps
919- allFpsDeps = nubOrd $ concatMap thd3 allModulesFpsDeps
920- in ([" --keep-temp-files" , " --enable-multi-repl" ] ++ allModules, allFiles, allFpsDeps)
921-
922- liftIO $ l <& LogComputedCradleLoadStyle " cabal" determinedLoadStyle `WithSeverity ` Info
923- liftIO $ l <& LogCabalLoad fp mc (prefix <$> resolvedCradles cradles) loadingFiles `WithSeverity ` Debug
924-
925- let cabalCommand = " v2-repl"
989+ (cabalArgs, loadingFiles, extraDeps) <- processCabalLoadStyle l cradles projectFile workDir mc fp determinedLoadStyle
926990
927- cabalProc <-
928- cabalProcess l (cradleProgramVersions cradles) projectFile workDir cabalCommand cabalArgs `modCradleError` \ err -> do
929- deps <- cabalCradleDependencies projectFile workDir workDir
930- pure $ err {cradleErrorDependencies = cradleErrorDependencies err ++ deps}
991+ cabalFeatures <- determineCabalLoadFeature progVersions
992+ cabalProc <- case cabalFeatures of
993+ CabalWithRepl -> cabalLoadFilesWithRepl l projectFile workDir cabalArgs
994+ CabalWithGhcShimWrapper -> cabalLoadFilesBefore315 l progVersions projectFile workDir cabalArgs
931995
932996 (ex, output, stde, [(_, maybeArgs)]) <- liftIO $ Process. readProcessWithOutputs [hie_bios_output] l workDir cabalProc
933997 let args = fromMaybe [] maybeArgs
@@ -943,7 +1007,7 @@ cabalAction cradles workDir mc l projectFile fp loadStyle = do
9431007
9441008 when (ex /= ExitSuccess ) $ do
9451009 deps <- liftIO $ cabalCradleDependencies projectFile workDir workDir
946- let cmd = show ([ " cabal " , cabalCommand] <> cabalArgs )
1010+ let cmd = prettyCmdSpec (cmdspec cabalProc )
9471011 let errorMsg = " Failed to run " <> cmd <> " in directory \" " <> workDir <> " \" . Consult the logs for full command and error."
9481012 throwCE (CradleError deps ex ([errorMsg] <> errorDetails) loadingFiles)
9491013
@@ -954,25 +1018,17 @@ cabalAction cradles workDir mc l projectFile fp loadStyle = do
9541018 -- root of the component, so we are right in trivial cases at least.
9551019 deps <- liftIO $ cabalCradleDependencies projectFile workDir workDir
9561020 throwCE (CradleError (deps <> extraDeps) ex ([" Failed to parse result of calling cabal" ] <> errorDetails) loadingFiles)
957- Just (componentDir, final_args ) -> do
1021+ Just (componentDir, ghc_args ) -> do
9581022 deps <- liftIO $ cabalCradleDependencies projectFile workDir componentDir
1023+ final_args <- case cabalFeatures of
1024+ CabalWithRepl -> liftIO $ processCabalGhcResponseFile ghc_args
1025+ CabalWithGhcShimWrapper -> pure ghc_args
9591026 CradleLoadResultT $ pure $ makeCradleResult (ex, stde, componentDir, final_args) (deps <> extraDeps) loadingFiles
960- where
961- -- Need to make relative on Windows, due to a Cabal bug with how it
962- -- parses file targets with a C: drive in it. So we decide to make
963- -- the paths relative to the working directory.
964- fixTargetPath x
965- | isWindows && hasDrive x = makeRelative workDir x
966- | otherwise = x
967- moduleFilesFromSameProject fps =
968- [ (fromMaybe (fixTargetPath file) old_mc, file, deps)
969- | file <- fps,
970- -- Lookup the component for the old file
971- Just (ResolvedCradle {concreteCradle = ConcreteCabal ct, cradleDeps = deps}) <- [selectCradle prefix file (resolvedCradles cradles)],
972- -- Only include this file if the old component is in the same project
973- (projectConfigFromMaybe (cradleRoot cradles) (cabalProjectFile ct)) == projectFile,
974- let old_mc = cabalComponent ct
975- ]
1027+
1028+ processCabalGhcResponseFile :: [String ] -> IO [String ]
1029+ processCabalGhcResponseFile args = do
1030+ expanded_args <- expandResponse args
1031+ pure $ removeInteractive expanded_args
9761032
9771033removeInteractive :: [String ] -> [String ]
9781034removeInteractive = filter (/= " --interactive" )
@@ -1086,9 +1142,9 @@ stackAction
10861142 -> IO (CradleLoadResult ComponentOptions )
10871143stackAction workDir mc syaml l fp loadStyle = do
10881144 logCradleHasNoSupportForLoadWithContext l loadStyle " stack"
1089- let ghcProcArgs = ( " stack" , stackYamlProcessArgs syaml <> [" exec" , " ghc" , " --" ])
1145+ let ghcProc args = proc " stack" ( stackYamlProcessArgs syaml <> [" exec" , " ghc" , " --" ] <> args )
10901146 -- Same wrapper works as with cabal
1091- wrapper_fp <- withGhcWrapperTool l ghcProcArgs workDir
1147+ wrapper_fp <- withGhcWrapperTool l ghcProc workDir
10921148 (ex1, _stdo, stde, [(_, maybeArgs)]) <-
10931149 Process. readProcessWithOutputs [hie_bios_output] l workDir
10941150 $ stackProcess syaml
0 commit comments