Skip to content

Commit

Permalink
Add '--repl-no-load' option to skip module load in REPL (#7578)
Browse files Browse the repository at this point in the history
* Add '--repl-no-load' option to skip module load in REPL

* Command docs

* Add tests

* update changelog

Co-authored-by: Stuart Popejoy <[email protected]>
  • Loading branch information
sirlensalot and Stuart Popejoy authored Aug 29, 2021
1 parent 7d4ce47 commit 785ea62
Show file tree
Hide file tree
Showing 19 changed files with 155 additions and 34 deletions.
12 changes: 6 additions & 6 deletions Cabal/src/Distribution/Simple/Build.hs
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ addExtraAsmSources bi extras = bi { asmSources = new }
exs = Set.fromList extras


replComponent :: [String]
replComponent :: ReplOptions
-> Verbosity
-> PackageDescription
-> LocalBuildInfo
Expand Down Expand Up @@ -645,27 +645,27 @@ buildExe verbosity numJobs pkg_descr lbi exe clbi =
UHC -> UHC.buildExe verbosity pkg_descr lbi exe clbi
_ -> die' verbosity "Building is not supported with this compiler."

replLib :: [String] -> Verbosity -> PackageDescription
replLib :: ReplOptions -> Verbosity -> PackageDescription
-> LocalBuildInfo -> Library -> ComponentLocalBuildInfo
-> IO ()
replLib replFlags verbosity pkg_descr lbi lib clbi =
case compilerFlavor (compiler lbi) of
-- 'cabal repl' doesn't need to support 'ghc --make -j', so we just pass
-- NoFlag as the numJobs parameter.
GHC -> GHC.replLib replFlags verbosity NoFlag pkg_descr lbi lib clbi
GHCJS -> GHCJS.replLib replFlags verbosity NoFlag pkg_descr lbi lib clbi
GHCJS -> GHCJS.replLib (replOptionsFlags replFlags) verbosity NoFlag pkg_descr lbi lib clbi
_ -> die' verbosity "A REPL is not supported for this compiler."

replExe :: [String] -> Verbosity -> PackageDescription
replExe :: ReplOptions -> Verbosity -> PackageDescription
-> LocalBuildInfo -> Executable -> ComponentLocalBuildInfo
-> IO ()
replExe replFlags verbosity pkg_descr lbi exe clbi =
case compilerFlavor (compiler lbi) of
GHC -> GHC.replExe replFlags verbosity NoFlag pkg_descr lbi exe clbi
GHCJS -> GHCJS.replExe replFlags verbosity NoFlag pkg_descr lbi exe clbi
GHCJS -> GHCJS.replExe (replOptionsFlags replFlags) verbosity NoFlag pkg_descr lbi exe clbi
_ -> die' verbosity "A REPL is not supported for this compiler."

replFLib :: [String] -> Verbosity -> PackageDescription
replFLib :: ReplOptions -> Verbosity -> PackageDescription
-> LocalBuildInfo -> ForeignLib -> ComponentLocalBuildInfo
-> IO ()
replFLib replFlags verbosity pkg_descr lbi exe clbi =
Expand Down
26 changes: 17 additions & 9 deletions Cabal/src/Distribution/Simple/GHC.hs
Original file line number Diff line number Diff line change
Expand Up @@ -493,13 +493,13 @@ buildLib :: Verbosity -> Cabal.Flag (Maybe Int)
-> Library -> ComponentLocalBuildInfo -> IO ()
buildLib = buildOrReplLib Nothing

replLib :: [String] -> Verbosity
replLib :: ReplOptions -> Verbosity
-> Cabal.Flag (Maybe Int) -> PackageDescription
-> LocalBuildInfo -> Library
-> ComponentLocalBuildInfo -> IO ()
replLib = buildOrReplLib . Just

buildOrReplLib :: Maybe [String] -> Verbosity
buildOrReplLib :: Maybe ReplOptions -> Verbosity
-> Cabal.Flag (Maybe Int) -> PackageDescription
-> LocalBuildInfo -> Library
-> ComponentLocalBuildInfo -> IO ()
Expand Down Expand Up @@ -609,8 +609,9 @@ buildOrReplLib mReplFlags verbosity numJobs pkg_descr lbi lib clbi = do
replOpts = vanillaOpts {
ghcOptExtra = Internal.filterGhciFlags
(ghcOptExtra vanillaOpts)
<> replFlags,
ghcOptNumJobs = mempty
<> replOptionsFlags replFlags,
ghcOptNumJobs = mempty,
ghcOptInputModules = replNoLoad replFlags (ghcOptInputModules vanillaOpts)
}
`mappend` linkerOpts
`mappend` mempty {
Expand Down Expand Up @@ -969,7 +970,7 @@ buildFLib
buildFLib v njobs pkg lbi = gbuild v njobs pkg lbi . GBuildFLib

replFLib
:: [String] -> Verbosity
:: ReplOptions -> Verbosity
-> Cabal.Flag (Maybe Int) -> PackageDescription
-> LocalBuildInfo -> ForeignLib
-> ComponentLocalBuildInfo -> IO ()
Expand All @@ -985,7 +986,7 @@ buildExe
buildExe v njobs pkg lbi = gbuild v njobs pkg lbi . GBuildExe

replExe
:: [String] -> Verbosity
:: ReplOptions -> Verbosity
-> Cabal.Flag (Maybe Int) -> PackageDescription
-> LocalBuildInfo -> Executable
-> ComponentLocalBuildInfo -> IO ()
Expand All @@ -997,9 +998,9 @@ replExe replFlags v njobs pkg lbi =
-- 'GBuildMode' distinguishes between the various kinds of operation.
data GBuildMode =
GBuildExe Executable
| GReplExe [String] Executable
| GReplExe ReplOptions Executable
| GBuildFLib ForeignLib
| GReplFLib [String] ForeignLib
| GReplFLib ReplOptions ForeignLib

gbuildInfo :: GBuildMode -> BuildInfo
gbuildInfo (GBuildExe exe) = buildInfo exe
Expand Down Expand Up @@ -1265,6 +1266,11 @@ gbuildSources verbosity specVer tmpDir bm =
isCxx :: FilePath -> Bool
isCxx fp = elem (takeExtension fp) [".cpp", ".cxx", ".c++"]

replNoLoad :: Ord a => ReplOptions -> NubListR a -> NubListR a
replNoLoad replFlags l
| replOptionsNoLoad replFlags == Flag True = mempty
| otherwise = l

-- | Generic build function. See comment for 'GBuildMode'.
gbuild :: Verbosity -> Cabal.Flag (Maybe Int)
-> PackageDescription -> LocalBuildInfo
Expand Down Expand Up @@ -1383,7 +1389,9 @@ gbuild verbosity numJobs pkg_descr lbi bm clbi = do
replOpts = baseOpts {
ghcOptExtra = Internal.filterGhciFlags
(ghcOptExtra baseOpts)
<> replFlags
<> replOptionsFlags replFlags,
ghcOptInputModules = replNoLoad replFlags (ghcOptInputModules baseOpts),
ghcOptInputFiles = replNoLoad replFlags (ghcOptInputFiles baseOpts)
}
-- For a normal compile we do separate invocations of ghc for
-- compiling as for linking. But for repl we have to do just
Expand Down
36 changes: 31 additions & 5 deletions Cabal/src/Distribution/Simple/Setup.hs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ module Distribution.Simple.Setup (
BuildFlags(..), emptyBuildFlags, defaultBuildFlags, buildCommand,
ShowBuildInfoFlags(..), defaultShowBuildFlags, showBuildInfoCommand,
ReplFlags(..), defaultReplFlags, replCommand,
ReplOptions(..),
CleanFlags(..), emptyCleanFlags, defaultCleanFlags, cleanCommand,
RegisterFlags(..), emptyRegisterFlags, defaultRegisterFlags, registerCommand,
unregisterCommand,
Expand Down Expand Up @@ -1665,13 +1666,30 @@ instance Semigroup BuildFlags where
-- * REPL Flags
-- ------------------------------------------------------------

data ReplOptions = ReplOptions {
replOptionsFlags :: [String],
replOptionsNoLoad :: Flag Bool
}
deriving (Show, Generic, Typeable)

instance Binary ReplOptions
instance Structured ReplOptions


instance Monoid ReplOptions where
mempty = ReplOptions mempty (Flag False)
mappend = (<>)

instance Semigroup ReplOptions where
(<>) = gmappend

data ReplFlags = ReplFlags {
replProgramPaths :: [(String, FilePath)],
replProgramArgs :: [(String, [String])],
replDistPref :: Flag FilePath,
replVerbosity :: Flag Verbosity,
replReload :: Flag Bool,
replReplOptions :: [String]
replReplOptions :: ReplOptions
}
deriving (Show, Generic, Typeable)

Expand All @@ -1682,7 +1700,7 @@ defaultReplFlags = ReplFlags {
replDistPref = NoFlag,
replVerbosity = Flag normal,
replReload = Flag False,
replReplOptions = []
replReplOptions = mempty
}

instance Monoid ReplFlags where
Expand Down Expand Up @@ -1763,9 +1781,17 @@ replCommand progDb = CommandUI
where
liftReplOption = liftOption replReplOptions (\v flags -> flags { replReplOptions = v })

replOptions :: ShowOrParseArgs -> [OptionField [String]]
replOptions _ = [ option [] ["repl-options"] "use this option for the repl" id
const (reqArg "FLAG" (succeedReadE (:[])) id) ]
replOptions :: ShowOrParseArgs -> [OptionField ReplOptions]
replOptions _ =
[ option [] ["repl-no-load"]
"Disable loading of project modules at REPL startup."
replOptionsNoLoad (\p flags -> flags { replOptionsNoLoad = p })
trueArg
, option [] ["repl-options"]
"use this option for the repl"
replOptionsFlags (\p flags -> flags { replOptionsFlags = p ++ replOptionsFlags flags })
(reqArg "FLAG" (succeedReadE (:[])) id)
]

-- ------------------------------------------------------------
-- * Test flags
Expand Down
19 changes: 9 additions & 10 deletions cabal-install/src/Distribution/Client/CmdRepl.hs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ import qualified Distribution.Client.Setup as Client
import Distribution.Client.Types
( PackageLocation(..), PackageSpecifier(..), UnresolvedSourcePackage )
import Distribution.Simple.Setup
( fromFlagOrDefault, replOptions
( fromFlagOrDefault, ReplOptions(..), replOptions
, Flag(..), toFlag, falseArg )
import Distribution.Simple.Command
( CommandUI(..), liftOptionL, usageAlternatives, option
Expand Down Expand Up @@ -111,8 +111,6 @@ import System.Directory
import System.FilePath
( (</>) )

type ReplFlags = [String]

data EnvFlags = EnvFlags
{ envPackages :: [Dependency]
, envIncludeTransitive :: Flag Bool
Expand Down Expand Up @@ -142,7 +140,7 @@ envOptions _ =
("couldn't parse dependencies: " ++)
(parsecCommaList parsec)

replCommand :: CommandUI (NixStyleFlags (ReplFlags, EnvFlags))
replCommand :: CommandUI (NixStyleFlags (ReplOptions, EnvFlags))
replCommand = Client.installCommand {
commandName = "v2-repl",
commandSynopsis = "Open an interactive session for the given component.",
Expand Down Expand Up @@ -179,7 +177,7 @@ replCommand = Client.installCommand {
++ " add a version (constrained between 4.15 and 4.18) of the library 'lens' "
++ "to the default component (or no component if there is no project present)\n",

commandDefaultFlags = defaultNixStyleFlags ([], defaultEnvFlags),
commandDefaultFlags = defaultNixStyleFlags (mempty, defaultEnvFlags),
commandOptions = nixStyleOptions $ \showOrParseArgs ->
map (liftOptionL _1) (replOptions showOrParseArgs) ++
map (liftOptionL _2) (envOptions showOrParseArgs)
Expand All @@ -196,7 +194,7 @@ replCommand = Client.installCommand {
-- For more details on how this works, see the module
-- "Distribution.Client.ProjectOrchestration"
--
replAction :: NixStyleFlags (ReplFlags, EnvFlags) -> [String] -> GlobalFlags -> IO ()
replAction :: NixStyleFlags (ReplOptions, EnvFlags) -> [String] -> GlobalFlags -> IO ()
replAction flags@NixStyleFlags { extraFlags = (replFlags, envFlags), ..} targetStrings globalFlags = do
let
with = withProject cliConfig verbosity targetStrings
Expand Down Expand Up @@ -286,8 +284,9 @@ replAction flags@NixStyleFlags { extraFlags = (replFlags, envFlags), ..} targetS

let buildCtx' = buildCtx
{ elaboratedShared = (elaboratedShared buildCtx)
{ pkgConfigReplOptions = replFlags ++ replFlags'' }
}
{ pkgConfigReplOptions = replFlags
{ replOptionsFlags = (replOptionsFlags replFlags) ++ replFlags''
} } }
printPlan verbosity baseCtx' buildCtx'

buildOutcomes <- runProjectBuildPhase verbosity baseCtx' buildCtx'
Expand Down Expand Up @@ -415,7 +414,7 @@ addDepsToProjectTarget deps pkgId ctx =
}
addDeps spec = spec

generateReplFlags :: Bool -> ElaboratedInstallPlan -> OriginalComponentInfo -> ReplFlags
generateReplFlags :: Bool -> ElaboratedInstallPlan -> OriginalComponentInfo -> [String]
generateReplFlags includeTransitive elaboratedPlan OriginalComponentInfo{..} = flags
where
exeDeps :: [UnitId]
Expand All @@ -425,7 +424,7 @@ generateReplFlags includeTransitive elaboratedPlan OriginalComponentInfo{..} = f
(InstallPlan.dependencyClosure elaboratedPlan [ociUnitId])

deps, deps', trans, trans' :: [UnitId]
flags :: ReplFlags
flags :: [String]
deps = installedUnitId <$> InstallPlan.directDeps elaboratedPlan ociUnitId
deps' = deps \\ ociOriginalDeps
trans = installedUnitId <$> InstallPlan.dependencyClosure elaboratedPlan deps'
Expand Down
2 changes: 1 addition & 1 deletion cabal-install/src/Distribution/Client/ProjectPlanning.hs
Original file line number Diff line number Diff line change
Expand Up @@ -1283,7 +1283,7 @@ elaborateInstallPlan verbosity platform compiler compilerprogdb pkgConfigDB
pkgConfigPlatform = platform,
pkgConfigCompiler = compiler,
pkgConfigCompilerProgs = compilerprogdb,
pkgConfigReplOptions = []
pkgConfigReplOptions = mempty
}

preexistingInstantiatedPkgs =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ import Distribution.Simple.LocalBuildInfo
( ComponentName(..), LibraryName(..) )
import qualified Distribution.Simple.InstallDirs as InstallDirs
import Distribution.Simple.InstallDirs (PathTemplate)
import Distribution.Simple.Setup (HaddockTarget, TestShowDetails)
import Distribution.Simple.Setup (HaddockTarget, TestShowDetails, ReplOptions)
import Distribution.Version

import qualified Distribution.Solver.Types.ComponentDeps as CD
Expand Down Expand Up @@ -147,7 +147,7 @@ data ElaboratedSharedConfig
-- ghc & ghc-pkg). Once constructed, only the 'configuredPrograms' are
-- used.
pkgConfigCompilerProgs :: ProgramDb,
pkgConfigReplOptions :: [String]
pkgConfigReplOptions :: ReplOptions
}
deriving (Show, Generic, Typeable)
--TODO: [code cleanup] no Eq instance
Expand Down
4 changes: 4 additions & 0 deletions cabal-testsuite/PackageTests/ReplNoLoad/ModuleA.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module ModuleA where

a :: Int
a = 42
6 changes: 6 additions & 0 deletions cabal-testsuite/PackageTests/ReplNoLoad/ModuleB.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module Main where

import ModuleC

main :: IO ()
main = print c
4 changes: 4 additions & 0 deletions cabal-testsuite/PackageTests/ReplNoLoad/ModuleC.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module ModuleC where

c :: Int
c = 42
15 changes: 15 additions & 0 deletions cabal-testsuite/PackageTests/ReplNoLoad/cabal-repl-no-load.cabal
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
name: cabal-repl-no-load
version: 0.1
build-type: Simple
cabal-version: >= 1.10

library
exposed-modules: ModuleA
build-depends: base
default-language: Haskell2010

executable exec
main-is: ModuleB.hs
other-modules: ModuleC
build-depends: base
default-language: Haskell2010
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# cabal clean
# cabal repl
Resolving dependencies...
Build profile: -w ghc-<GHCVER> -O1
In order, the following will be built:
- cabal-repl-no-load-0.1 (lib) (first run)
Configuring library for cabal-repl-no-load-0.1..
Preprocessing library for cabal-repl-no-load-0.1..
8 changes: 8 additions & 0 deletions cabal-testsuite/PackageTests/ReplNoLoad/cabal.exec-normal.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# cabal clean
# cabal repl
Resolving dependencies...
Build profile: -w ghc-<GHCVER> -O1
In order, the following will be built:
- cabal-repl-no-load-0.1 (exe:exec) (first run)
Configuring executable 'exec' for cabal-repl-no-load-0.1..
Preprocessing executable 'exec' for cabal-repl-no-load-0.1..
8 changes: 8 additions & 0 deletions cabal-testsuite/PackageTests/ReplNoLoad/cabal.lib-no-load.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# cabal clean
# cabal repl
Resolving dependencies...
Build profile: -w ghc-<GHCVER> -O1
In order, the following will be built:
- cabal-repl-no-load-0.1 (lib) (first run)
Configuring library for cabal-repl-no-load-0.1..
Preprocessing library for cabal-repl-no-load-0.1..
8 changes: 8 additions & 0 deletions cabal-testsuite/PackageTests/ReplNoLoad/cabal.lib-normal.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# cabal clean
# cabal repl
Resolving dependencies...
Build profile: -w ghc-<GHCVER> -O1
In order, the following will be built:
- cabal-repl-no-load-0.1 (lib) (first run)
Configuring library for cabal-repl-no-load-0.1..
Preprocessing library for cabal-repl-no-load-0.1..
1 change: 1 addition & 0 deletions cabal-testsuite/PackageTests/ReplNoLoad/cabal.project
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
packages: .
19 changes: 19 additions & 0 deletions cabal-testsuite/PackageTests/ReplNoLoad/cabal.test.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import Test.Cabal.Prelude

main = do
cabalTest' "lib-normal" $ do
cabal' "clean" []
res <- cabalWithStdin "repl" [] ":show modules"
assertOutputContains "Ok, one module loaded." res
cabalTest' "lib-no-load" $ do
cabal' "clean" []
res <- cabalWithStdin "repl" ["--repl-no-load"] ":show modules"
assertOutputDoesNotContain "Ok, one module loaded." res
cabalTest' "exec-normal" $ do
cabal' "clean" []
res <- cabalWithStdin "repl" ["exec"] ":show modules"
assertOutputContains "Ok, two modules loaded." res
cabalTest' "exec-no-load" $ do
cabal' "clean" []
res <- cabalWithStdin "repl" ["--repl-no-load"] ":show modules"
assertOutputDoesNotContain "Ok, two modules loaded." res
1 change: 1 addition & 0 deletions cabal-testsuite/src/Test/Cabal/Monad.hs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ module Test.Cabal.Monad (
setupAndCabalTest,
setupTest,
cabalTest,
cabalTest',
-- * The monad
TestM,
runTestM,
Expand Down
4 changes: 4 additions & 0 deletions changelog.d/issue-7541
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
synopsis: '--repl-no-load' option skips startup modules load in REPL
issues: #7541
packages: Cabal cabal-install
prs: #7578
Loading

0 comments on commit 785ea62

Please sign in to comment.