Skip to content

Commit cdc7fa5

Browse files
authored
Add Loading Style option to runAction (#433)
Allows users to decide at run-time whether they would like to use experimental features, such as `cabal`'s `multi-repl` feature that will be released in 3.12. The `LoadStyle` can not always be honoured by the respective cradle. For example, if the ghc version or cabal version isn't recent enough.
1 parent 2379eb3 commit cdc7fa5

File tree

8 files changed

+123
-50
lines changed

8 files changed

+123
-50
lines changed

exe/Main.hs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import HIE.Bios
1616
import HIE.Bios.Ghc.Check
1717
import HIE.Bios.Ghc.Gap as Gap
1818
import HIE.Bios.Internal.Debug
19+
import HIE.Bios.Types (LoadStyle(LoadFile))
1920
import Paths_hie_bios
2021

2122
----------------------------------------------------------------
@@ -84,7 +85,7 @@ main = do
8485
[] -> error "too few arguments"
8586
_ -> do
8687
res <- forM files $ \fp -> do
87-
res <- getCompilerOptions fp [] cradle
88+
res <- getCompilerOptions fp LoadFile cradle
8889
case res of
8990
CradleFail (CradleError _deps _ex err) ->
9091
return $ "Failed to show flags for \""

hie-bios.cabal

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -172,8 +172,8 @@ Library
172172
exceptions ^>= 0.10,
173173
cryptohash-sha1 >= 0.11.100 && < 0.12,
174174
directory >= 1.3.0 && < 1.4,
175-
filepath >= 1.4.1 && < 1.5,
176-
time >= 1.8.0 && < 1.13,
175+
filepath >= 1.4.1 && < 1.6,
176+
time >= 1.8.0 && < 1.14,
177177
extra >= 1.6.14 && < 1.8,
178178
prettyprinter ^>= 1.6 || ^>= 1.7.0,
179179
ghc >= 9.2.1 && < 9.9,

src/HIE/Bios/Cradle.hs

Lines changed: 69 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -284,15 +284,22 @@ resolvedCradlesToCradle logger buildCustomCradle root cs = mdo
284284
notNoneType _ = True
285285

286286

287-
resolveCradleAction :: LogAction IO (WithSeverity Log) -> (b -> CradleAction a) -> ResolvedCradles b -> FilePath -> ResolvedCradle b -> CradleAction a
288-
resolveCradleAction l buildCustomCradle cs root cradle =
287+
resolveCradleAction :: Show a => LogAction IO (WithSeverity Log) -> (b -> CradleAction a) -> ResolvedCradles b -> FilePath -> ResolvedCradle b -> CradleAction a
288+
resolveCradleAction l buildCustomCradle cs root cradle = addLoadStyleLogToCradleAction $
289289
case concreteCradle cradle of
290290
ConcreteCabal t -> cabalCradle l cs root (cabalComponent t) (projectConfigFromMaybe root (cabalProjectFile t))
291291
ConcreteStack t -> stackCradle l root (stackComponent t) (projectConfigFromMaybe root (stackYaml t))
292292
ConcreteBios bios deps mbGhc -> biosCradle l root bios deps mbGhc
293293
ConcreteDirect xs -> directCradle l root xs
294294
ConcreteNone -> noneCradle
295295
ConcreteOther a -> buildCustomCradle a
296+
where
297+
-- Add a log message to each loading operation.
298+
addLoadStyleLogToCradleAction crdlAct = crdlAct
299+
{ runCradle = \fp ls -> do
300+
l <& LogRequestedCradleLoadStyle (T.pack $ show $ actionName crdlAct) ls `WithSeverity` Debug
301+
runCradle crdlAct fp ls
302+
}
296303

297304
resolveCradleTree :: FilePath -> CradleConfig a -> [ResolvedCradle a]
298305
resolveCradleTree root (CradleConfig confDeps confTree) = go root confDeps confTree
@@ -458,7 +465,8 @@ directCradle :: LogAction IO (WithSeverity Log) -> FilePath -> [String] -> Cradl
458465
directCradle l wdir args
459466
= CradleAction
460467
{ actionName = Types.Direct
461-
, runCradle = \_ _ ->
468+
, runCradle = \_ loadStyle -> do
469+
logCradleHasNoSupportForLoadWithContext l loadStyle "direct"
462470
return (CradleSuccess (ComponentOptions (args ++ argDynamic) wdir []))
463471
, runGhcCmd = runGhcCmdOnPath l wdir
464472
}
@@ -480,7 +488,7 @@ biosCradle l wdir biosCall biosDepsCall mbGhc
480488
biosWorkDir :: FilePath -> MaybeT IO FilePath
481489
biosWorkDir = findFileUpwards (".hie-bios" ==)
482490

483-
biosDepsAction :: LogAction IO (WithSeverity Log) -> FilePath -> Maybe Callable -> FilePath -> [FilePath] -> IO [FilePath]
491+
biosDepsAction :: LogAction IO (WithSeverity Log) -> FilePath -> Maybe Callable -> FilePath -> LoadStyle -> IO [FilePath]
484492
biosDepsAction l wdir (Just biosDepsCall) fp _prevs = do
485493
biosDeps' <- callableToProcess biosDepsCall (Just fp) -- TODO multi pass the previous files too
486494
(ex, sout, serr, [(_, args)]) <- readProcessWithOutputs [hie_bios_output] l wdir biosDeps'
@@ -495,16 +503,17 @@ biosAction
495503
-> Maybe Callable
496504
-> LogAction IO (WithSeverity Log)
497505
-> FilePath
498-
-> [FilePath]
506+
-> LoadStyle
499507
-> IO (CradleLoadResult ComponentOptions)
500-
biosAction wdir bios bios_deps l fp fps = do
508+
biosAction wdir bios bios_deps l fp loadStyle = do
509+
logCradleHasNoSupportForLoadWithContext l loadStyle "bios"
501510
bios' <- callableToProcess bios (Just fp) -- TODO pass all the files instead of listToMaybe
502511
(ex, _stdo, std, [(_, res),(_, mb_deps)]) <-
503512
readProcessWithOutputs [hie_bios_output, hie_bios_deps] l wdir bios'
504513

505514
deps <- case mb_deps of
506515
Just x -> return x
507-
Nothing -> biosDepsAction l wdir bios_deps fp fps
516+
Nothing -> biosDepsAction l wdir bios_deps fp loadStyle
508517
-- Output from the program should be written to the output file and
509518
-- delimited by newlines.
510519
-- Execute the bios action and add dependencies of the cradle.
@@ -779,42 +788,56 @@ cabalGhcDirs l cabalProject workDir = do
779788
where
780789
projectFileArgs = projectFileProcessArgs cabalProject
781790

782-
783791
cabalAction
784792
:: ResolvedCradles a
785793
-> FilePath
786794
-> Maybe String
787795
-> LogAction IO (WithSeverity Log)
788796
-> CradleProjectConfig
789797
-> FilePath
790-
-> [FilePath]
798+
-> LoadStyle
791799
-> CradleLoadResultT IO ComponentOptions
792-
cabalAction (ResolvedCradles root cs vs) workDir mc l projectFile fp fps = do
800+
cabalAction (ResolvedCradles root cs vs) workDir mc l projectFile fp loadStyle = do
793801
cabal_version <- liftIO $ runCachedIO $ cabalVersion vs
794802
ghc_version <- liftIO $ runCachedIO $ ghcVersion vs
803+
-- 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+
]
836+
837+
liftIO $ l <& LogComputedCradleLoadStyle "cabal" determinedLoadStyle `WithSeverity` Info
838+
795839
let
796840
cabalCommand = "v2-repl"
797-
cabalArgs = case (cabal_version, ghc_version) of
798-
(Just cabal, Just ghc)
799-
-- Multi-component supported from cabal-install 3.11
800-
-- and ghc 9.4
801-
| ghc >= makeVersion [9,4]
802-
, cabal >= makeVersion [3,11]
803-
-> case fps of
804-
[] -> [fromMaybe (fixTargetPath fp) mc]
805-
-- Start a multi-component session with all the old files
806-
_ -> "--keep-temp-files"
807-
: "--enable-multi-repl"
808-
: fromMaybe (fixTargetPath fp) mc
809-
: [fromMaybe (fixTargetPath old_fp) old_mc
810-
| old_fp <- fps
811-
-- Lookup the component for the old file
812-
, Just (ResolvedCradle{concreteCradle = ConcreteCabal ct}) <- [selectCradle prefix old_fp cs]
813-
-- Only include this file if the old component is in the same project
814-
, (projectConfigFromMaybe root (cabalProjectFile ct)) == projectFile
815-
, let old_mc = cabalComponent ct
816-
]
817-
_ -> [fromMaybe (fixTargetPath fp) mc]
818841

819842
cabalProc <- cabalProcess l projectFile workDir cabalCommand cabalArgs `modCradleError` \err -> do
820843
deps <- cabalCradleDependencies projectFile workDir workDir
@@ -843,8 +866,7 @@ cabalAction (ResolvedCradles root cs vs) workDir mc l projectFile fp fps = do
843866
-- Best effort. Assume the working directory is the
844867
-- root of the component, so we are right in trivial cases at least.
845868
deps <- liftIO $ cabalCradleDependencies projectFile workDir workDir
846-
throwCE (CradleError deps ex $
847-
(["Failed to parse result of calling cabal" ] <> errorDetails))
869+
throwCE (CradleError deps ex $ ["Failed to parse result of calling cabal" ] <> errorDetails)
848870
Just (componentDir, final_args) -> do
849871
deps <- liftIO $ cabalCradleDependencies projectFile workDir componentDir
850872
CradleLoadResultT $ pure $ makeCradleResult (ex, stde, componentDir, final_args) deps
@@ -963,9 +985,10 @@ stackAction
963985
-> CradleProjectConfig
964986
-> LogAction IO (WithSeverity Log)
965987
-> FilePath
966-
-> [FilePath]
988+
-> LoadStyle
967989
-> IO (CradleLoadResult ComponentOptions)
968-
stackAction workDir mc syaml l _fp _fps = do
990+
stackAction workDir mc syaml l _fp loadStyle = do
991+
logCradleHasNoSupportForLoadWithContext l loadStyle "stack"
969992
let ghcProcArgs = ("stack", stackYamlProcessArgs syaml <> ["exec", "ghc", "--"])
970993
-- Same wrapper works as with cabal
971994
wrapper_fp <- withGhcWrapperTool l ghcProcArgs workDir
@@ -1234,3 +1257,14 @@ readProcessWithCwd' l createdProcess stdin = do
12341257
Nothing -> throwCE $
12351258
CradleError [] ExitSuccess $
12361259
["Couldn't execute " <> cmdString] <> prettyProcessEnv createdProcess
1260+
1261+
-- | Log that the cradle has no supported for loading with context, if and only if
1262+
-- 'LoadWithContext' was requested.
1263+
logCradleHasNoSupportForLoadWithContext :: Applicative m => LogAction m (WithSeverity Log) -> LoadStyle -> T.Text -> m ()
1264+
logCradleHasNoSupportForLoadWithContext l (LoadWithContext _) crdlName =
1265+
l <& WithSeverity
1266+
(LogLoadWithContextUnsupported crdlName
1267+
$ Just $ crdlName <> " doesn't support loading multiple components at once"
1268+
)
1269+
Info
1270+
logCradleHasNoSupportForLoadWithContext _ _ _ = pure ()

src/HIE/Bios/Flags.hs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ import Colog.Core (WithSeverity (..), Severity (..), (<&))
88
-- file or GHC session according to the provided 'Cradle'.
99
getCompilerOptions
1010
:: FilePath -- ^ The file we are loading it because of
11-
-> [FilePath] -- ^ previous files we might want to include in the build
11+
-> LoadStyle -- ^ previous files we might want to include in the build
1212
-> Cradle a
1313
-> IO (CradleLoadResult ComponentOptions)
14-
getCompilerOptions fp fps cradle = do
14+
getCompilerOptions fp loadStyle cradle = do
1515
(cradleLogger cradle) <& LogProcessOutput "invoking build tool to determine build flags (this may take some time depending on the cache)" `WithSeverity` Info
16-
runCradle (cradleOptsProg cradle) fp fps
16+
runCradle (cradleOptsProg cradle) fp loadStyle

src/HIE/Bios/Ghc/Api.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ initializeFlagsWithCradleWithMessage ::
3939
-> Cradle a -- ^ The cradle we want to load
4040
-> m (CradleLoadResult (m G.SuccessFlag, ComponentOptions)) -- ^ Whether we actually loaded the cradle or not.
4141
initializeFlagsWithCradleWithMessage msg fp cradle =
42-
fmap (initSessionWithMessage msg) <$> liftIO (getCompilerOptions fp [] cradle)
42+
fmap (initSessionWithMessage msg) <$> liftIO (getCompilerOptions fp LoadFile cradle)
4343

4444
-- | Actually perform the initialisation of the session. Initialising the session corresponds to
4545
-- parsing the command line flags, setting the targets for the session and then attempting to load

src/HIE/Bios/Internal/Debug.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ debugInfo :: Show a
3030
-> IO String
3131
debugInfo fp cradle = unlines <$> do
3232
let logger = cradleLogger cradle
33-
res <- getCompilerOptions fp [] cradle
33+
res <- getCompilerOptions fp LoadFile cradle
3434
canonFp <- canonicalizePath fp
3535
conf <- findConfig canonFp
3636
crdl <- findCradle' logger canonFp

src/HIE/Bios/Types.hs

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,10 @@ import Control.Monad.Trans.Class
1313
#if MIN_VERSION_base(4,9,0)
1414
import qualified Control.Monad.Fail as Fail
1515
#endif
16-
import Prettyprinter
17-
import System.Process.Extra (CreateProcess (env, cmdspec), CmdSpec (..))
18-
import Data.Maybe (fromMaybe)
16+
import Data.Maybe (fromMaybe)
17+
import qualified Data.Text as T
18+
import Prettyprinter
19+
import System.Process.Extra (CreateProcess (env, cmdspec), CmdSpec (..))
1920

2021
----------------------------------------------------------------
2122
-- Environment variables used by hie-bios.
@@ -91,11 +92,14 @@ data ActionName a
9192
deriving (Show, Eq, Ord, Functor)
9293

9394
data Log
94-
= LogAny String
95+
= LogAny !T.Text
9596
| LogProcessOutput String
9697
| LogCreateProcessRun CreateProcess
9798
| LogProcessRun FilePath [FilePath]
98-
deriving Show
99+
| LogRequestedCradleLoadStyle !T.Text !LoadStyle
100+
| LogComputedCradleLoadStyle !T.Text !LoadStyle
101+
| LogLoadWithContextUnsupported !T.Text !(Maybe T.Text)
102+
deriving (Show)
99103

100104
instance Pretty Log where
101105
pretty (LogAny s) = pretty s
@@ -116,11 +120,45 @@ instance Pretty Log where
116120
]
117121
where
118122
envText = map (indent 2 . pretty) $ prettyProcessEnv cp
123+
pretty (LogRequestedCradleLoadStyle crdlName ls) =
124+
"Requested to load" <+> pretty crdlName <+> "cradle" <+> case ls of
125+
LoadFile -> "using single file mode"
126+
LoadWithContext fps -> "using all files (multi-components):" <> line <> indent 4 (pretty fps)
127+
pretty (LogComputedCradleLoadStyle crdlName ls) =
128+
"Load" <+> pretty crdlName <+> "cradle" <+> case ls of
129+
LoadFile -> "using single file"
130+
LoadWithContext _ -> "using all files (multi-components)"
131+
132+
pretty (LogLoadWithContextUnsupported crdlName mReason) =
133+
pretty crdlName <+> "cradle doesn't support loading using all files (multi-components)" <>
134+
case mReason of
135+
Nothing -> "."
136+
Just reason -> ", because:" <+> pretty reason <> "."
137+
<+> "Falling back loading to single file mode."
138+
139+
-- | The 'LoadStyle' instructs a cradle on how to load a given file target.
140+
data LoadStyle
141+
= LoadFile
142+
-- ^ Instruct the cradle to load the given file target.
143+
--
144+
-- What this entails depends on the cradle. For example, the 'cabal' cradle
145+
-- will configure the whole component the file target belongs to, and produce
146+
-- component options to load the component, which is the minimal unit of code in cabal repl.
147+
-- A 'default' cradle, on the other hand, will only load the given filepath.
148+
| LoadWithContext [FilePath]
149+
-- ^ Give a cradle additional context for loading a file target.
150+
--
151+
-- The context instructs the cradle to load the file target, while also loading
152+
-- the given filepaths.
153+
-- This is useful for cradles that support loading multiple code units at once,
154+
-- e.g. cabal cradles can use the 'multi-repl' feature to set up a multiple home unit
155+
-- session in GHC.
156+
deriving (Show, Eq, Ord)
119157

120158
data CradleAction a = CradleAction {
121159
actionName :: ActionName a
122160
-- ^ Name of the action.
123-
, runCradle :: FilePath -> [FilePath] -> IO (CradleLoadResult ComponentOptions)
161+
, runCradle :: FilePath -> LoadStyle -> IO (CradleLoadResult ComponentOptions)
124162
-- ^ Options to compile the given file with.
125163
, runGhcCmd :: [String] -> IO (CradleLoadResult String)
126164
-- ^ Executes the @ghc@ binary that is usually used to

tests/Utils.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ loadComponentOptions fp = do
269269
a_fp <- normFile fp
270270
crd <- askCradle
271271
step $ "Initialise flags for: " <> fp
272-
clr <- liftIO $ getCompilerOptions a_fp [] crd
272+
clr <- liftIO $ getCompilerOptions a_fp LoadFile crd
273273
setLoadResult clr
274274

275275
loadRuntimeGhcLibDir :: TestM ()

0 commit comments

Comments
 (0)