From ba9eb4072354dec6ad773fb5859eab22e1ba023e Mon Sep 17 00:00:00 2001 From: Matthew Pickering Date: Tue, 17 Jun 2025 14:03:17 +0100 Subject: [PATCH] Add --with-repl flag to modify program the "repl" starts with Programs like doctest and hie-bios want to use `cabal-install` in order to discover the correct options to start a GHC session. Previously they used the `--with-compiler` option, but this led to complications since the wrapper was called for compiling all dependencies and so on, the shim had to be more complicated and forward arguments onto the user's version of GHC. The `--with-repl` command allows you to pass a program which is used instead of GHC at the final invocation of the repl. Therefore the wrappers don't have to deal with being a complete shim but can concentrate on intercepting the arguments at the end. This commit removes the special hack to not use response files with --interactive mode. Tools are expected to deal with them appropiately, which is much easier now only one invocation is passed to the wrapper. Fixes #9115 --- .../src/Distribution/Simple/GHC/Build/Link.hs | 8 +-- Cabal/src/Distribution/Simple/Program/GHC.hs | 51 +++++++--------- Cabal/src/Distribution/Simple/Setup/Repl.hs | 10 +++- .../Solver/Types/ConstraintSource.hs | 6 ++ .../src/Distribution/Client/CmdRepl.hs | 58 ++++++++++++------- .../WithRepl/SimpleTests/ModuleA.hs | 4 ++ .../SimpleTests/cabal-with-repl.cabal | 9 +++ .../WithRepl/SimpleTests/cabal.test.hs | 19 ++++++ .../cabal.with-repl-invalid-path.out | 10 ++++ .../cabal.with-repl-valid-path.out | 8 +++ .../PackageTests/WithRepl/WithExe/Main.hs | 4 ++ .../PackageTests/WithRepl/WithExe/Main2.hs | 3 + .../PackageTests/WithRepl/WithExe/ModuleA.hs | 4 ++ .../WithExe/cabal-with-repl-exe.cabal | 14 +++++ .../WithRepl/WithExe/cabal.project | 1 + .../WithRepl/WithExe/cabal.test.hs | 32 ++++++++++ cabal-testsuite/src/Test/Cabal/Prelude.hs | 4 ++ changelog.d/issue-9115.md | 26 +++++++++ doc/cabal-commands.rst | 18 ++++++ 19 files changed, 232 insertions(+), 57 deletions(-) create mode 100644 cabal-testsuite/PackageTests/WithRepl/SimpleTests/ModuleA.hs create mode 100644 cabal-testsuite/PackageTests/WithRepl/SimpleTests/cabal-with-repl.cabal create mode 100644 cabal-testsuite/PackageTests/WithRepl/SimpleTests/cabal.test.hs create mode 100644 cabal-testsuite/PackageTests/WithRepl/SimpleTests/cabal.with-repl-invalid-path.out create mode 100644 cabal-testsuite/PackageTests/WithRepl/SimpleTests/cabal.with-repl-valid-path.out create mode 100644 cabal-testsuite/PackageTests/WithRepl/WithExe/Main.hs create mode 100644 cabal-testsuite/PackageTests/WithRepl/WithExe/Main2.hs create mode 100644 cabal-testsuite/PackageTests/WithRepl/WithExe/ModuleA.hs create mode 100644 cabal-testsuite/PackageTests/WithRepl/WithExe/cabal-with-repl-exe.cabal create mode 100644 cabal-testsuite/PackageTests/WithRepl/WithExe/cabal.project create mode 100644 cabal-testsuite/PackageTests/WithRepl/WithExe/cabal.test.hs create mode 100644 changelog.d/issue-9115.md diff --git a/Cabal/src/Distribution/Simple/GHC/Build/Link.hs b/Cabal/src/Distribution/Simple/GHC/Build/Link.hs index 962d0d95c7a..bba36bb3809 100644 --- a/Cabal/src/Distribution/Simple/GHC/Build/Link.hs +++ b/Cabal/src/Distribution/Simple/GHC/Build/Link.hs @@ -751,10 +751,10 @@ runReplOrWriteFlags ghcProg lbi rflags ghcOpts pkg_name target = verbosity = fromFlag $ setupVerbosity common tempFileOptions = commonSetupTempFileOptions common in case replOptionsFlagOutput (replReplOptions rflags) of - NoFlag -> - runGHCWithResponseFile - "ghc.rsp" - Nothing + NoFlag -> do + -- If a specific GHC implementation is specified, use it + runReplProgram + (flagToMaybe $ replWithRepl (replReplOptions rflags)) tempFileOptions verbosity ghcProg diff --git a/Cabal/src/Distribution/Simple/Program/GHC.hs b/Cabal/src/Distribution/Simple/Program/GHC.hs index 29b722ad278..17bc27f151c 100644 --- a/Cabal/src/Distribution/Simple/Program/GHC.hs +++ b/Cabal/src/Distribution/Simple/Program/GHC.hs @@ -16,6 +16,7 @@ module Distribution.Simple.Program.GHC , renderGhcOptions , runGHC , runGHCWithResponseFile + , runReplProgram , packageDbArgsDb , normaliseGhcArgs ) where @@ -668,37 +669,7 @@ runGHCWithResponseFile fileNameTemplate encoding tempFileOptions verbosity ghcPr args = progInvokeArgs invocation - -- Don't use response files if the first argument is `--interactive`, for - -- two related reasons. - -- - -- `hie-bios` relies on a hack to intercept the command-line that `Cabal` - -- supplies to `ghc`. Specifically, `hie-bios` creates a script around - -- `ghc` that detects if the first option is `--interactive` and if so then - -- instead of running `ghc` it prints the command-line that `ghc` was given - -- instead of running the command: - -- - -- https://github.com/haskell/hie-bios/blob/ce863dba7b57ded20160b4f11a487e4ff8372c08/wrappers/cabal#L7 - -- - -- … so we can't store that flag in the response file, otherwise that will - -- break. However, even if we were to add a special-case to keep that flag - -- out of the response file things would still break because `hie-bios` - -- stores the arguments to `ghc` that the wrapper script outputs and reuses - -- them later. That breaks if you use a response file because it will - -- store an argument like `@…/ghc36000-0.rsp` which is a temporary path - -- that no longer exists after the wrapper script completes. - -- - -- The work-around here is that we don't use a response file at all if the - -- first argument (and only the first argument) to `ghc` is - -- `--interactive`. This ensures that `hie-bios` and all downstream - -- utilities (e.g. `haskell-language-server`) continue working. - -- - -- - useResponseFile = - case args of - "--interactive" : _ -> False - _ -> compilerSupportsResponseFiles - - if not useResponseFile + if not compilerSupportsResponseFiles then runProgramInvocation verbosity invocation else do let (rtsArgs, otherArgs) = splitRTSArgs args @@ -721,6 +692,24 @@ runGHCWithResponseFile fileNameTemplate encoding tempFileOptions verbosity ghcPr runProgramInvocation verbosity newInvocation +-- Start the repl. Either use `ghc`, or the program specified by the --with-repl flag. +runReplProgram + :: Maybe FilePath + -- ^ --with-repl argument + -> TempFileOptions + -> Verbosity + -> ConfiguredProgram + -> Compiler + -> Platform + -> Maybe (SymbolicPath CWD (Dir Pkg)) + -> GhcOptions + -> IO () +runReplProgram withReplProg tempFileOptions verbosity ghcProg comp platform mbWorkDir ghcOpts = + let replProg = case withReplProg of + Just path -> ghcProg{programLocation = FoundOnSystem path} + Nothing -> ghcProg + in runGHCWithResponseFile "ghci.rsp" Nothing tempFileOptions verbosity replProg comp platform mbWorkDir ghcOpts + ghcInvocation :: Verbosity -> ConfiguredProgram diff --git a/Cabal/src/Distribution/Simple/Setup/Repl.hs b/Cabal/src/Distribution/Simple/Setup/Repl.hs index 4b73cb34b99..ceec4649ad8 100644 --- a/Cabal/src/Distribution/Simple/Setup/Repl.hs +++ b/Cabal/src/Distribution/Simple/Setup/Repl.hs @@ -54,6 +54,7 @@ data ReplOptions = ReplOptions { replOptionsFlags :: [String] , replOptionsNoLoad :: Flag Bool , replOptionsFlagOutput :: Flag FilePath + , replWithRepl :: Flag FilePath } deriving (Show, Generic) @@ -85,7 +86,7 @@ instance Binary ReplOptions instance Structured ReplOptions instance Monoid ReplOptions where - mempty = ReplOptions mempty (Flag False) NoFlag + mempty = ReplOptions mempty (Flag False) NoFlag NoFlag mappend = (<>) instance Semigroup ReplOptions where @@ -229,4 +230,11 @@ replOptions _ = replOptionsFlagOutput (\p flags -> flags{replOptionsFlagOutput = p}) (reqArg "DIR" (succeedReadE Flag) flagToList) + , option + [] + ["with-repl"] + "Give the path to a program to use for REPL" + replWithRepl + (\v flags -> flags{replWithRepl = v}) + (reqArgFlag "PATH") ] diff --git a/cabal-install-solver/src/Distribution/Solver/Types/ConstraintSource.hs b/cabal-install-solver/src/Distribution/Solver/Types/ConstraintSource.hs index 3f171b3c6d7..0deb786959b 100644 --- a/cabal-install-solver/src/Distribution/Solver/Types/ConstraintSource.hs +++ b/cabal-install-solver/src/Distribution/Solver/Types/ConstraintSource.hs @@ -42,6 +42,10 @@ data ConstraintSource = -- from Cabal >= 3.11 | ConstraintSourceMultiRepl + -- | Constraint introduced by --with-repl, which requires features + -- from Cabal >= 3.15 + | ConstraintSourceWithRepl + -- | Constraint introduced by --enable-profiling-shared, which requires features -- from Cabal >= 3.13 | ConstraintSourceProfiledDynamic @@ -81,6 +85,8 @@ instance Pretty ConstraintSource where text "config file, command line flag, or user target" ConstraintSourceMultiRepl -> text "--enable-multi-repl" + ConstraintSourceWithRepl -> + text "--with-repl" ConstraintSourceProfiledDynamic -> text "--enable-profiling-shared" ConstraintSourceUnknown -> text "unknown source" diff --git a/cabal-install/src/Distribution/Client/CmdRepl.hs b/cabal-install/src/Distribution/Client/CmdRepl.hs index e8c64c6999f..7fb3f700d09 100644 --- a/cabal-install/src/Distribution/Client/CmdRepl.hs +++ b/cabal-install/src/Distribution/Client/CmdRepl.hs @@ -116,7 +116,7 @@ import Distribution.Simple.Utils , wrapText ) import Distribution.Solver.Types.ConstraintSource - ( ConstraintSource (ConstraintSourceMultiRepl) + ( ConstraintSource (ConstraintSourceMultiRepl, ConstraintSourceWithRepl) ) import Distribution.Solver.Types.PackageConstraint ( PackageProperty (PackagePropertyVersion) @@ -180,12 +180,10 @@ import Distribution.Client.ReplFlags , topReplOptions ) import Distribution.Compat.Binary (decode) -import Distribution.Simple.Flag (fromFlagOrDefault, pattern Flag) +import Distribution.Simple.Flag (flagToMaybe, fromFlagOrDefault, pattern Flag) import Distribution.Simple.Program.Builtin (ghcProgram) import Distribution.Simple.Program.Db (requireProgram) import Distribution.Simple.Program.Types - ( ConfiguredProgram (programOverrideEnv) - ) import System.Directory ( doesFileExist , getCurrentDirectory @@ -325,15 +323,34 @@ replAction flags@NixStyleFlags{extraFlags = r@ReplFlags{..}, ..} targetStrings g -- We need to do this before solving, but the compiler version is only known -- after solving (phaseConfigureCompiler), so instead of using -- multiReplDecision we just check the flag. - let baseCtx' = - if fromFlagOrDefault False $ + let multiReplEnabled = + fromFlagOrDefault False $ projectConfigMultiRepl (projectConfigShared $ projectConfig baseCtx) <> replUseMulti + + withReplEnabled = + isJust $ flagToMaybe $ replWithRepl configureReplOptions + + addConstraintWhen cond constraint base_ctx = + if cond then - baseCtx + base_ctx & lProjectConfig . lProjectConfigShared . lProjectConfigConstraints - %~ (multiReplCabalConstraint :) - else baseCtx + %~ (constraint :) + else base_ctx + + -- This is the constraint setup.Cabal>=3.11. 3.11 is when Cabal options + -- used for multi-repl were introduced. + -- Idelly we'd apply this constraint only on the closure of repl targets, + -- but that would require another solver run for marginal advantages that + -- will further shrink as 3.11 is adopted. + addMultiReplConstraint = addConstraintWhen multiReplEnabled $ requireCabal [3, 11] ConstraintSourceMultiRepl + + -- Similarly, if you use `--with-repl` then your version of `Cabal` needs to + -- support the `--with-repl` flag. + addWithReplConstraint = addConstraintWhen withReplEnabled $ requireCabal [3, 15] ConstraintSourceWithRepl + + baseCtx' = addMultiReplConstraint $ addWithReplConstraint baseCtx (originalComponent, baseCtx'') <- if null (envPackages replEnvFlags) @@ -481,7 +498,7 @@ replAction flags@NixStyleFlags{extraFlags = r@ReplFlags{..}, ..} targetStrings g } -- run ghc --interactive with - runGHCWithResponseFile "ghci_multi.rsp" Nothing tempFileOptions verbosity ghcProg' compiler platform Nothing ghc_opts + runReplProgram (flagToMaybe $ replWithRepl replOpts') tempFileOptions verbosity ghcProg' compiler platform Nothing ghc_opts else do -- single target repl replOpts'' <- case targetCtx of @@ -526,17 +543,16 @@ replAction flags@NixStyleFlags{extraFlags = r@ReplFlags{..}, ..} targetStrings g return targets - -- This is the constraint setup.Cabal>=3.11. 3.11 is when Cabal options - -- used for multi-repl were introduced. - -- Idelly we'd apply this constraint only on the closure of repl targets, - -- but that would require another solver run for marginal advantages that - -- will further shrink as 3.11 is adopted. - multiReplCabalConstraint = - ( UserConstraint - (UserAnySetupQualifier (mkPackageName "Cabal")) - (PackagePropertyVersion $ orLaterVersion $ mkVersion [3, 11]) - , ConstraintSourceMultiRepl - ) +-- | Create a constraint which requires a later version of Cabal. +-- This is used for commands which require a specific feature from the Cabal library +-- such as multi-repl or the --with-repl flag. +requireCabal :: [Int] -> ConstraintSource -> (UserConstraint, ConstraintSource) +requireCabal version source = + ( UserConstraint + (UserAnySetupQualifier (mkPackageName "Cabal")) + (PackagePropertyVersion $ orLaterVersion $ mkVersion version) + , source + ) -- | First version of GHC which supports multiple home packages minMultipleHomeUnitsVersion :: Version diff --git a/cabal-testsuite/PackageTests/WithRepl/SimpleTests/ModuleA.hs b/cabal-testsuite/PackageTests/WithRepl/SimpleTests/ModuleA.hs new file mode 100644 index 00000000000..3a6beb2e2c4 --- /dev/null +++ b/cabal-testsuite/PackageTests/WithRepl/SimpleTests/ModuleA.hs @@ -0,0 +1,4 @@ +module ModuleA where + +x :: Int +x = 42 diff --git a/cabal-testsuite/PackageTests/WithRepl/SimpleTests/cabal-with-repl.cabal b/cabal-testsuite/PackageTests/WithRepl/SimpleTests/cabal-with-repl.cabal new file mode 100644 index 00000000000..852360cf05c --- /dev/null +++ b/cabal-testsuite/PackageTests/WithRepl/SimpleTests/cabal-with-repl.cabal @@ -0,0 +1,9 @@ +cabal-version: 2.4 +name: cabal-with-repl +version: 0.1.0.0 +build-type: Simple + +library + exposed-modules: ModuleA + build-depends: base + default-language: Haskell2010 diff --git a/cabal-testsuite/PackageTests/WithRepl/SimpleTests/cabal.test.hs b/cabal-testsuite/PackageTests/WithRepl/SimpleTests/cabal.test.hs new file mode 100644 index 00000000000..71c584120f5 --- /dev/null +++ b/cabal-testsuite/PackageTests/WithRepl/SimpleTests/cabal.test.hs @@ -0,0 +1,19 @@ +import Test.Cabal.Prelude +import System.Directory (getCurrentDirectory) +import System.FilePath (()) + +main = do + -- Test that --with-repl works with a valid GHC path + cabalTest' "with-repl-valid-path" $ do + cabal' "clean" [] + -- Get the path to the system GHC + ghc_prog <- requireProgramM ghcProgram + res <- cabalWithStdin "v2-repl" ["--with-repl=" ++ programPath ghc_prog] "" + assertOutputContains "Ok, one module loaded." res + assertOutputContains "GHCi, version" res + + -- Test that --with-repl fails with an invalid path + cabalTest' "with-repl-invalid-path" $ do + cabal' "clean" [] + res <- fails $ cabalWithStdin "v2-repl" ["--with-repl=/nonexistent/path/to/ghc"] "" + assertOutputContains "does not exist" res diff --git a/cabal-testsuite/PackageTests/WithRepl/SimpleTests/cabal.with-repl-invalid-path.out b/cabal-testsuite/PackageTests/WithRepl/SimpleTests/cabal.with-repl-invalid-path.out new file mode 100644 index 00000000000..b59b62a074a --- /dev/null +++ b/cabal-testsuite/PackageTests/WithRepl/SimpleTests/cabal.with-repl-invalid-path.out @@ -0,0 +1,10 @@ +# cabal clean +# cabal v2-repl +Resolving dependencies... +Build profile: -w ghc- -O1 +In order, the following will be built: + - cabal-with-repl-0.1.0.0 (interactive) (lib) (first run) +Configuring library for cabal-with-repl-0.1.0.0... +Preprocessing library for cabal-with-repl-0.1.0.0... +Error: [Cabal-7125] +repl failed for cabal-with-repl-0.1.0.0-inplace. diff --git a/cabal-testsuite/PackageTests/WithRepl/SimpleTests/cabal.with-repl-valid-path.out b/cabal-testsuite/PackageTests/WithRepl/SimpleTests/cabal.with-repl-valid-path.out new file mode 100644 index 00000000000..f1ca1ffc808 --- /dev/null +++ b/cabal-testsuite/PackageTests/WithRepl/SimpleTests/cabal.with-repl-valid-path.out @@ -0,0 +1,8 @@ +# cabal clean +# cabal v2-repl +Resolving dependencies... +Build profile: -w ghc- -O1 +In order, the following will be built: + - cabal-with-repl-0.1.0.0 (interactive) (lib) (first run) +Configuring library for cabal-with-repl-0.1.0.0... +Preprocessing library for cabal-with-repl-0.1.0.0... diff --git a/cabal-testsuite/PackageTests/WithRepl/WithExe/Main.hs b/cabal-testsuite/PackageTests/WithRepl/WithExe/Main.hs new file mode 100644 index 00000000000..fe589eaec4f --- /dev/null +++ b/cabal-testsuite/PackageTests/WithRepl/WithExe/Main.hs @@ -0,0 +1,4 @@ +import ModuleA + +main :: IO () +main = print $ "My specific executable" diff --git a/cabal-testsuite/PackageTests/WithRepl/WithExe/Main2.hs b/cabal-testsuite/PackageTests/WithRepl/WithExe/Main2.hs new file mode 100644 index 00000000000..77c6e482831 --- /dev/null +++ b/cabal-testsuite/PackageTests/WithRepl/WithExe/Main2.hs @@ -0,0 +1,3 @@ +module Main where + +main = print "My other executable" diff --git a/cabal-testsuite/PackageTests/WithRepl/WithExe/ModuleA.hs b/cabal-testsuite/PackageTests/WithRepl/WithExe/ModuleA.hs new file mode 100644 index 00000000000..8810d78ced9 --- /dev/null +++ b/cabal-testsuite/PackageTests/WithRepl/WithExe/ModuleA.hs @@ -0,0 +1,4 @@ +module ModuleA where + +add :: Num a => a -> a -> a +add x y = x + y diff --git a/cabal-testsuite/PackageTests/WithRepl/WithExe/cabal-with-repl-exe.cabal b/cabal-testsuite/PackageTests/WithRepl/WithExe/cabal-with-repl-exe.cabal new file mode 100644 index 00000000000..5354e28791f --- /dev/null +++ b/cabal-testsuite/PackageTests/WithRepl/WithExe/cabal-with-repl-exe.cabal @@ -0,0 +1,14 @@ +cabal-version: 2.4 +name: cabal-with-repl-exe +version: 0.1.0.0 +build-type: Simple + +executable test-exe + main-is: Main.hs + build-depends: base + default-language: Haskell2010 + +executable test-exe2 + main-is: Main2.hs + build-depends: base + default-language: Haskell2010 diff --git a/cabal-testsuite/PackageTests/WithRepl/WithExe/cabal.project b/cabal-testsuite/PackageTests/WithRepl/WithExe/cabal.project new file mode 100644 index 00000000000..e6fdbadb439 --- /dev/null +++ b/cabal-testsuite/PackageTests/WithRepl/WithExe/cabal.project @@ -0,0 +1 @@ +packages: . diff --git a/cabal-testsuite/PackageTests/WithRepl/WithExe/cabal.test.hs b/cabal-testsuite/PackageTests/WithRepl/WithExe/cabal.test.hs new file mode 100644 index 00000000000..fd05e5a0e06 --- /dev/null +++ b/cabal-testsuite/PackageTests/WithRepl/WithExe/cabal.test.hs @@ -0,0 +1,32 @@ +{-# LANGUAGE OverloadedStrings #-} +import Distribution.Simple.Program +import Distribution.Simple.Program.GHC +import Distribution.Simple.Utils +import Test.Cabal.Prelude + +-- On windows this test passed but then fails in CI with +-- +-- D:\a\_temp\cabal-testsuite-12064\cabal-multi.dist\work\.\dist\multi-out-63884\paths\cabal-with-repl-exe-0.1.0.0-inplace-test-exe: removeDirectoryRecursive:removeContentsRecursive:removePathRecursive:removeContentsRecursive:removePathRecursive:DeleteFile "\\\\?\\D:\\a\\_temp\\cabal-testsuite-12064\\cabal-multi.dist\\work\\dist\\multi-out-63884\\paths\\cabal-with-repl-exe-0.1.0.0-inplace-test-exe": permission denied (The process cannot access the file because it is being used by another process.) +-- + +main = do + mkTest "normal-repl" $ \exePath -> do + -- Try using the executable with --with-repl + res <- cabalWithStdin "v2-repl" ["--with-repl=" ++ exePath, "test-exe"] "" + assertOutputContains "My specific executable" res + mkTest "multi-repl" $ \exePath -> do + requireGhcSupportsMultiRepl + res <- cabalWithStdin "v2-repl" ["--enable-multi-repl", "--with-repl=" ++ exePath, "all"] "" + assertOutputContains "My specific executable" res + + +mkTest name act = do + skipIfCIAndWindows 11026 + cabalTest' name $ recordMode DoNotRecord $ do + -- Build the executable + cabal' "v2-build" ["test-exe"] + -- Get the path to the built executable + withPlan $ do + exePath <- planExePath "cabal-with-repl-exe" "test-exe" + act exePath + diff --git a/cabal-testsuite/src/Test/Cabal/Prelude.hs b/cabal-testsuite/src/Test/Cabal/Prelude.hs index a5a76260514..3a12298608c 100644 --- a/cabal-testsuite/src/Test/Cabal/Prelude.hs +++ b/cabal-testsuite/src/Test/Cabal/Prelude.hs @@ -1055,6 +1055,10 @@ skipUnlessJavaScript = skipUnlessIO "needs the JavaScript backend" isJavaScript skipIfJavaScript :: IO () skipIfJavaScript = skipIfIO "incompatible with the JavaScript backend" isJavaScript +requireGhcSupportsMultiRepl :: TestM () +requireGhcSupportsMultiRepl = + skipUnlessGhcVersion ">= 9.4" + isWindows :: Bool isWindows = buildOS == Windows diff --git a/changelog.d/issue-9115.md b/changelog.d/issue-9115.md new file mode 100644 index 00000000000..139f97f8a19 --- /dev/null +++ b/changelog.d/issue-9115.md @@ -0,0 +1,26 @@ +--- +synopsis: Add --with-repl flag to specify alternative REPL program +packages: [cabal-install, Cabal] +prs: [10996] +issues: [9115] +--- + +Added a new `--with-repl` command-line option that allows specifying an alternative +program to use when starting a REPL session, instead of the default GHC. + +This is particularly useful for tools like `doctest` and `hie-bios` that need to +intercept the REPL session to perform their own operations. Previously, these tools +had to use `--with-ghc` which required them to proxy all GHC invocations, including +dependency compilation, making the implementation more complex. + +The `--with-repl` option only affects the final REPL invocation, simplifying the +implementation of such wrapper tools. + +Example usage: +```bash +cabal repl --with-repl=doctest +cabal repl --with-repl=/path/to/custom/ghc +``` + +This change also removes the special handling for response files with `--interactive` +mode, as tools are now expected to handle response files appropriately. diff --git a/doc/cabal-commands.rst b/doc/cabal-commands.rst index 8ed8393df62..b9354db992d 100644 --- a/doc/cabal-commands.rst +++ b/doc/cabal-commands.rst @@ -1112,6 +1112,24 @@ configuration from the 'cabal.project', 'cabal.project.local' and other files. Disables the loading of target modules at startup. +.. option:: --with-repl=PATH + + Specifies an alternative program to use when starting the REPL session, + instead of the default GHC. This is particularly useful for tools like + ``doctest`` and ``hie-bios`` that need to intercept the REPL session to + perform their own operations. + + Unlike ``--with-ghc`` which affects all GHC invocations (including dependency + compilation), ``--with-repl`` only affects the final REPL invocation, making + it much simpler for wrapper tools to implement. + + Examples: + + :: + + $ cabal repl --with-repl=doctest + $ cabal repl --with-repl=/path/to/custom/ghc + .. option:: -b DEPENDENCIES or -bDEPENDENCIES, --build-depends=DEPENDENCIES A way to experiment with libraries without needing to download