Skip to content

Commit 269c1b1

Browse files
authored
Merge pull request #10996 from haskell/wip/for-repl
Add --with-repl flag to modify program the "repl" starts with
2 parents d8c21ab + ba9eb40 commit 269c1b1

File tree

19 files changed

+232
-57
lines changed

19 files changed

+232
-57
lines changed

Cabal/src/Distribution/Simple/GHC/Build/Link.hs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -751,10 +751,10 @@ runReplOrWriteFlags ghcProg lbi rflags ghcOpts pkg_name target =
751751
verbosity = fromFlag $ setupVerbosity common
752752
tempFileOptions = commonSetupTempFileOptions common
753753
in case replOptionsFlagOutput (replReplOptions rflags) of
754-
NoFlag ->
755-
runGHCWithResponseFile
756-
"ghc.rsp"
757-
Nothing
754+
NoFlag -> do
755+
-- If a specific GHC implementation is specified, use it
756+
runReplProgram
757+
(flagToMaybe $ replWithRepl (replReplOptions rflags))
758758
tempFileOptions
759759
verbosity
760760
ghcProg

Cabal/src/Distribution/Simple/Program/GHC.hs

Lines changed: 20 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ module Distribution.Simple.Program.GHC
1616
, renderGhcOptions
1717
, runGHC
1818
, runGHCWithResponseFile
19+
, runReplProgram
1920
, packageDbArgsDb
2021
, normaliseGhcArgs
2122
) where
@@ -668,37 +669,7 @@ runGHCWithResponseFile fileNameTemplate encoding tempFileOptions verbosity ghcPr
668669

669670
args = progInvokeArgs invocation
670671

671-
-- Don't use response files if the first argument is `--interactive`, for
672-
-- two related reasons.
673-
--
674-
-- `hie-bios` relies on a hack to intercept the command-line that `Cabal`
675-
-- supplies to `ghc`. Specifically, `hie-bios` creates a script around
676-
-- `ghc` that detects if the first option is `--interactive` and if so then
677-
-- instead of running `ghc` it prints the command-line that `ghc` was given
678-
-- instead of running the command:
679-
--
680-
-- https://github.com/haskell/hie-bios/blob/ce863dba7b57ded20160b4f11a487e4ff8372c08/wrappers/cabal#L7
681-
--
682-
-- … so we can't store that flag in the response file, otherwise that will
683-
-- break. However, even if we were to add a special-case to keep that flag
684-
-- out of the response file things would still break because `hie-bios`
685-
-- stores the arguments to `ghc` that the wrapper script outputs and reuses
686-
-- them later. That breaks if you use a response file because it will
687-
-- store an argument like `@…/ghc36000-0.rsp` which is a temporary path
688-
-- that no longer exists after the wrapper script completes.
689-
--
690-
-- The work-around here is that we don't use a response file at all if the
691-
-- first argument (and only the first argument) to `ghc` is
692-
-- `--interactive`. This ensures that `hie-bios` and all downstream
693-
-- utilities (e.g. `haskell-language-server`) continue working.
694-
--
695-
--
696-
useResponseFile =
697-
case args of
698-
"--interactive" : _ -> False
699-
_ -> compilerSupportsResponseFiles
700-
701-
if not useResponseFile
672+
if not compilerSupportsResponseFiles
702673
then runProgramInvocation verbosity invocation
703674
else do
704675
let (rtsArgs, otherArgs) = splitRTSArgs args
@@ -721,6 +692,24 @@ runGHCWithResponseFile fileNameTemplate encoding tempFileOptions verbosity ghcPr
721692

722693
runProgramInvocation verbosity newInvocation
723694

695+
-- Start the repl. Either use `ghc`, or the program specified by the --with-repl flag.
696+
runReplProgram
697+
:: Maybe FilePath
698+
-- ^ --with-repl argument
699+
-> TempFileOptions
700+
-> Verbosity
701+
-> ConfiguredProgram
702+
-> Compiler
703+
-> Platform
704+
-> Maybe (SymbolicPath CWD (Dir Pkg))
705+
-> GhcOptions
706+
-> IO ()
707+
runReplProgram withReplProg tempFileOptions verbosity ghcProg comp platform mbWorkDir ghcOpts =
708+
let replProg = case withReplProg of
709+
Just path -> ghcProg{programLocation = FoundOnSystem path}
710+
Nothing -> ghcProg
711+
in runGHCWithResponseFile "ghci.rsp" Nothing tempFileOptions verbosity replProg comp platform mbWorkDir ghcOpts
712+
724713
ghcInvocation
725714
:: Verbosity
726715
-> ConfiguredProgram

Cabal/src/Distribution/Simple/Setup/Repl.hs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ data ReplOptions = ReplOptions
5454
{ replOptionsFlags :: [String]
5555
, replOptionsNoLoad :: Flag Bool
5656
, replOptionsFlagOutput :: Flag FilePath
57+
, replWithRepl :: Flag FilePath
5758
}
5859
deriving (Show, Generic)
5960

@@ -85,7 +86,7 @@ instance Binary ReplOptions
8586
instance Structured ReplOptions
8687

8788
instance Monoid ReplOptions where
88-
mempty = ReplOptions mempty (Flag False) NoFlag
89+
mempty = ReplOptions mempty (Flag False) NoFlag NoFlag
8990
mappend = (<>)
9091

9192
instance Semigroup ReplOptions where
@@ -229,4 +230,11 @@ replOptions _ =
229230
replOptionsFlagOutput
230231
(\p flags -> flags{replOptionsFlagOutput = p})
231232
(reqArg "DIR" (succeedReadE Flag) flagToList)
233+
, option
234+
[]
235+
["with-repl"]
236+
"Give the path to a program to use for REPL"
237+
replWithRepl
238+
(\v flags -> flags{replWithRepl = v})
239+
(reqArgFlag "PATH")
232240
]

cabal-install-solver/src/Distribution/Solver/Types/ConstraintSource.hs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ data ConstraintSource =
4242
-- from Cabal >= 3.11
4343
| ConstraintSourceMultiRepl
4444

45+
-- | Constraint introduced by --with-repl, which requires features
46+
-- from Cabal >= 3.15
47+
| ConstraintSourceWithRepl
48+
4549
-- | Constraint introduced by --enable-profiling-shared, which requires features
4650
-- from Cabal >= 3.13
4751
| ConstraintSourceProfiledDynamic
@@ -81,6 +85,8 @@ instance Pretty ConstraintSource where
8185
text "config file, command line flag, or user target"
8286
ConstraintSourceMultiRepl ->
8387
text "--enable-multi-repl"
88+
ConstraintSourceWithRepl ->
89+
text "--with-repl"
8490
ConstraintSourceProfiledDynamic ->
8591
text "--enable-profiling-shared"
8692
ConstraintSourceUnknown -> text "unknown source"

cabal-install/src/Distribution/Client/CmdRepl.hs

Lines changed: 37 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ import Distribution.Simple.Utils
116116
, wrapText
117117
)
118118
import Distribution.Solver.Types.ConstraintSource
119-
( ConstraintSource (ConstraintSourceMultiRepl)
119+
( ConstraintSource (ConstraintSourceMultiRepl, ConstraintSourceWithRepl)
120120
)
121121
import Distribution.Solver.Types.PackageConstraint
122122
( PackageProperty (PackagePropertyVersion)
@@ -180,12 +180,10 @@ import Distribution.Client.ReplFlags
180180
, topReplOptions
181181
)
182182
import Distribution.Compat.Binary (decode)
183-
import Distribution.Simple.Flag (fromFlagOrDefault, pattern Flag)
183+
import Distribution.Simple.Flag (flagToMaybe, fromFlagOrDefault, pattern Flag)
184184
import Distribution.Simple.Program.Builtin (ghcProgram)
185185
import Distribution.Simple.Program.Db (requireProgram)
186186
import Distribution.Simple.Program.Types
187-
( ConfiguredProgram (programOverrideEnv)
188-
)
189187
import System.Directory
190188
( doesFileExist
191189
, getCurrentDirectory
@@ -325,15 +323,34 @@ replAction flags@NixStyleFlags{extraFlags = r@ReplFlags{..}, ..} targetStrings g
325323
-- We need to do this before solving, but the compiler version is only known
326324
-- after solving (phaseConfigureCompiler), so instead of using
327325
-- multiReplDecision we just check the flag.
328-
let baseCtx' =
329-
if fromFlagOrDefault False $
326+
let multiReplEnabled =
327+
fromFlagOrDefault False $
330328
projectConfigMultiRepl (projectConfigShared $ projectConfig baseCtx)
331329
<> replUseMulti
330+
331+
withReplEnabled =
332+
isJust $ flagToMaybe $ replWithRepl configureReplOptions
333+
334+
addConstraintWhen cond constraint base_ctx =
335+
if cond
332336
then
333-
baseCtx
337+
base_ctx
334338
& lProjectConfig . lProjectConfigShared . lProjectConfigConstraints
335-
%~ (multiReplCabalConstraint :)
336-
else baseCtx
339+
%~ (constraint :)
340+
else base_ctx
341+
342+
-- This is the constraint setup.Cabal>=3.11. 3.11 is when Cabal options
343+
-- used for multi-repl were introduced.
344+
-- Idelly we'd apply this constraint only on the closure of repl targets,
345+
-- but that would require another solver run for marginal advantages that
346+
-- will further shrink as 3.11 is adopted.
347+
addMultiReplConstraint = addConstraintWhen multiReplEnabled $ requireCabal [3, 11] ConstraintSourceMultiRepl
348+
349+
-- Similarly, if you use `--with-repl` then your version of `Cabal` needs to
350+
-- support the `--with-repl` flag.
351+
addWithReplConstraint = addConstraintWhen withReplEnabled $ requireCabal [3, 15] ConstraintSourceWithRepl
352+
353+
baseCtx' = addMultiReplConstraint $ addWithReplConstraint baseCtx
337354

338355
(originalComponent, baseCtx'') <-
339356
if null (envPackages replEnvFlags)
@@ -481,7 +498,7 @@ replAction flags@NixStyleFlags{extraFlags = r@ReplFlags{..}, ..} targetStrings g
481498
}
482499

483500
-- run ghc --interactive with
484-
runGHCWithResponseFile "ghci_multi.rsp" Nothing tempFileOptions verbosity ghcProg' compiler platform Nothing ghc_opts
501+
runReplProgram (flagToMaybe $ replWithRepl replOpts') tempFileOptions verbosity ghcProg' compiler platform Nothing ghc_opts
485502
else do
486503
-- single target repl
487504
replOpts'' <- case targetCtx of
@@ -526,17 +543,16 @@ replAction flags@NixStyleFlags{extraFlags = r@ReplFlags{..}, ..} targetStrings g
526543

527544
return targets
528545

529-
-- This is the constraint setup.Cabal>=3.11. 3.11 is when Cabal options
530-
-- used for multi-repl were introduced.
531-
-- Idelly we'd apply this constraint only on the closure of repl targets,
532-
-- but that would require another solver run for marginal advantages that
533-
-- will further shrink as 3.11 is adopted.
534-
multiReplCabalConstraint =
535-
( UserConstraint
536-
(UserAnySetupQualifier (mkPackageName "Cabal"))
537-
(PackagePropertyVersion $ orLaterVersion $ mkVersion [3, 11])
538-
, ConstraintSourceMultiRepl
539-
)
546+
-- | Create a constraint which requires a later version of Cabal.
547+
-- This is used for commands which require a specific feature from the Cabal library
548+
-- such as multi-repl or the --with-repl flag.
549+
requireCabal :: [Int] -> ConstraintSource -> (UserConstraint, ConstraintSource)
550+
requireCabal version source =
551+
( UserConstraint
552+
(UserAnySetupQualifier (mkPackageName "Cabal"))
553+
(PackagePropertyVersion $ orLaterVersion $ mkVersion version)
554+
, source
555+
)
540556

541557
-- | First version of GHC which supports multiple home packages
542558
minMultipleHomeUnitsVersion :: Version
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
module ModuleA where
2+
3+
x :: Int
4+
x = 42
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
cabal-version: 2.4
2+
name: cabal-with-repl
3+
version: 0.1.0.0
4+
build-type: Simple
5+
6+
library
7+
exposed-modules: ModuleA
8+
build-depends: base
9+
default-language: Haskell2010
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import Test.Cabal.Prelude
2+
import System.Directory (getCurrentDirectory)
3+
import System.FilePath ((</>))
4+
5+
main = do
6+
-- Test that --with-repl works with a valid GHC path
7+
cabalTest' "with-repl-valid-path" $ do
8+
cabal' "clean" []
9+
-- Get the path to the system GHC
10+
ghc_prog <- requireProgramM ghcProgram
11+
res <- cabalWithStdin "v2-repl" ["--with-repl=" ++ programPath ghc_prog] ""
12+
assertOutputContains "Ok, one module loaded." res
13+
assertOutputContains "GHCi, version" res
14+
15+
-- Test that --with-repl fails with an invalid path
16+
cabalTest' "with-repl-invalid-path" $ do
17+
cabal' "clean" []
18+
res <- fails $ cabalWithStdin "v2-repl" ["--with-repl=/nonexistent/path/to/ghc"] ""
19+
assertOutputContains "does not exist" res
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# cabal clean
2+
# cabal v2-repl
3+
Resolving dependencies...
4+
Build profile: -w ghc-<GHCVER> -O1
5+
In order, the following will be built:
6+
- cabal-with-repl-0.1.0.0 (interactive) (lib) (first run)
7+
Configuring library for cabal-with-repl-0.1.0.0...
8+
Preprocessing library for cabal-with-repl-0.1.0.0...
9+
Error: [Cabal-7125]
10+
repl failed for cabal-with-repl-0.1.0.0-inplace.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# cabal clean
2+
# cabal v2-repl
3+
Resolving dependencies...
4+
Build profile: -w ghc-<GHCVER> -O1
5+
In order, the following will be built:
6+
- cabal-with-repl-0.1.0.0 (interactive) (lib) (first run)
7+
Configuring library for cabal-with-repl-0.1.0.0...
8+
Preprocessing library for cabal-with-repl-0.1.0.0...

0 commit comments

Comments
 (0)