From 5e269325886758ac5cfc874d12c9b488d91737d3 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Sat, 28 Dec 2024 07:15:13 -0500 Subject: [PATCH] Fix two issues when starting the repl Issue 1: In 3.14 there was a poor error when starting a repl in a project context without any targets. In 3.16, this error regressed, so cabal just exited cleanly. Issue 2: The repl was broken when started from a global context. Issue 1 is fixed by checking to see if there are any user targets, and issuing a proper error if there are none. Issue 2 is fixed by specifying the correct fake target which is constructed when starting the repl in the global context. Both are reported in #11107 and fixed in this patch. Co-authored-by: Matthew Pickering --- .../src/Distribution/Client/CmdRepl.hs | 138 +++++++++++++----- .../PackageTests/ReplDashB/File.hs | 1 + .../PackageTests/ReplDashB/cabal.out | 8 + .../PackageTests/ReplDashB/cabal.test.hs | 8 + .../PackageTests/ReplOptions/alt.project | 1 + .../PackageTests/ReplOptions/alt/ModuleA.hs | 4 + .../PackageTests/ReplOptions/alt/ModuleC.hs | 4 + .../PackageTests/ReplOptions/alt/alt.cabal | 10 ++ .../cabal.alt-multiple-repl-options.out | 5 + .../cabal.alt-single-repl-options.out | 5 + .../PackageTests/ReplOptions/cabal.test.hs | 23 ++- .../ReplProjectNoneTarget/cabal.out | 18 +++ .../ReplProjectNoneTarget/cabal.test.hs | 22 +++ .../pkg-one/pkg-one.cabal | 9 ++ .../pkg-two/pkg-one.cabal | 9 ++ .../ReplProjectTargetOnePkg/cabal.out | 26 ++++ .../ReplProjectTargetOnePkg/cabal.project | 1 + .../ReplProjectTargetOnePkg/cabal.test.hs | 27 ++++ .../ReplProjectTargetOnePkg/pkg-one/Foo.hs | 4 + .../pkg-one/pkg-one.cabal | 9 ++ .../ReplProjectTargetTwoPkgs/all-repl.txt | 3 + .../ReplProjectTargetTwoPkgs/cabal.out | 46 ++++++ .../ReplProjectTargetTwoPkgs/cabal.project | 1 + .../ReplProjectTargetTwoPkgs/cabal.test.hs | 52 +++++++ .../ReplProjectTargetTwoPkgs/default-repl.txt | 3 + .../ReplProjectTargetTwoPkgs/empty.project | 0 .../ReplProjectTargetTwoPkgs/pkg-one/Foo.hs | 4 + .../pkg-one/pkg-one.cabal | 9 ++ .../ReplProjectTargetTwoPkgs/pkg-two/Bar.hs | 4 + .../pkg-two/pkg-two.cabal | 9 ++ .../ReplProjectTargetTwoPkgs/reverse-repl.txt | 3 + .../ReplProjectTargetTwoPkgs/reverse.project | 1 + .../ReplProjectTargetTwoPkgs/some-repl.txt | 4 + .../ReplProjectTargetTwoPkgs/some.project | 1 + changelog.d/pr-10684.md | 64 ++++++++ changelog.d/pr-11237 | 13 ++ 36 files changed, 511 insertions(+), 38 deletions(-) create mode 100644 cabal-testsuite/PackageTests/ReplDashB/File.hs create mode 100644 cabal-testsuite/PackageTests/ReplDashB/cabal.out create mode 100644 cabal-testsuite/PackageTests/ReplDashB/cabal.test.hs create mode 100644 cabal-testsuite/PackageTests/ReplOptions/alt.project create mode 100644 cabal-testsuite/PackageTests/ReplOptions/alt/ModuleA.hs create mode 100644 cabal-testsuite/PackageTests/ReplOptions/alt/ModuleC.hs create mode 100644 cabal-testsuite/PackageTests/ReplOptions/alt/alt.cabal create mode 100644 cabal-testsuite/PackageTests/ReplOptions/cabal.alt-multiple-repl-options.out create mode 100644 cabal-testsuite/PackageTests/ReplOptions/cabal.alt-single-repl-options.out create mode 100644 cabal-testsuite/PackageTests/ReplProjectNoneTarget/cabal.out create mode 100644 cabal-testsuite/PackageTests/ReplProjectNoneTarget/cabal.test.hs create mode 100644 cabal-testsuite/PackageTests/ReplProjectNoneTarget/pkg-one/pkg-one.cabal create mode 100644 cabal-testsuite/PackageTests/ReplProjectNoneTarget/pkg-two/pkg-one.cabal create mode 100644 cabal-testsuite/PackageTests/ReplProjectTargetOnePkg/cabal.out create mode 100644 cabal-testsuite/PackageTests/ReplProjectTargetOnePkg/cabal.project create mode 100644 cabal-testsuite/PackageTests/ReplProjectTargetOnePkg/cabal.test.hs create mode 100644 cabal-testsuite/PackageTests/ReplProjectTargetOnePkg/pkg-one/Foo.hs create mode 100644 cabal-testsuite/PackageTests/ReplProjectTargetOnePkg/pkg-one/pkg-one.cabal create mode 100644 cabal-testsuite/PackageTests/ReplProjectTargetTwoPkgs/all-repl.txt create mode 100644 cabal-testsuite/PackageTests/ReplProjectTargetTwoPkgs/cabal.out create mode 100644 cabal-testsuite/PackageTests/ReplProjectTargetTwoPkgs/cabal.project create mode 100644 cabal-testsuite/PackageTests/ReplProjectTargetTwoPkgs/cabal.test.hs create mode 100644 cabal-testsuite/PackageTests/ReplProjectTargetTwoPkgs/default-repl.txt create mode 100644 cabal-testsuite/PackageTests/ReplProjectTargetTwoPkgs/empty.project create mode 100644 cabal-testsuite/PackageTests/ReplProjectTargetTwoPkgs/pkg-one/Foo.hs create mode 100644 cabal-testsuite/PackageTests/ReplProjectTargetTwoPkgs/pkg-one/pkg-one.cabal create mode 100644 cabal-testsuite/PackageTests/ReplProjectTargetTwoPkgs/pkg-two/Bar.hs create mode 100644 cabal-testsuite/PackageTests/ReplProjectTargetTwoPkgs/pkg-two/pkg-two.cabal create mode 100644 cabal-testsuite/PackageTests/ReplProjectTargetTwoPkgs/reverse-repl.txt create mode 100644 cabal-testsuite/PackageTests/ReplProjectTargetTwoPkgs/reverse.project create mode 100644 cabal-testsuite/PackageTests/ReplProjectTargetTwoPkgs/some-repl.txt create mode 100644 cabal-testsuite/PackageTests/ReplProjectTargetTwoPkgs/some.project create mode 100644 changelog.d/pr-10684.md create mode 100644 changelog.d/pr-11237 diff --git a/cabal-install/src/Distribution/Client/CmdRepl.hs b/cabal-install/src/Distribution/Client/CmdRepl.hs index 8e4b814d67e..c78f6f4416d 100644 --- a/cabal-install/src/Distribution/Client/CmdRepl.hs +++ b/cabal-install/src/Distribution/Client/CmdRepl.hs @@ -106,7 +106,8 @@ import Distribution.Simple.Compiler ) import Distribution.Simple.Program.GHC import Distribution.Simple.Setup - ( ReplOptions (..) + ( Flag + , ReplOptions (..) , commonSetupTempFileOptions ) import Distribution.Simple.Utils @@ -170,8 +171,8 @@ import Data.List import qualified Data.Map as Map import qualified Data.Set as Set import Distribution.Client.ProjectConfig - ( ProjectConfig (projectConfigShared) - , ProjectConfigShared (projectConfigConstraints, projectConfigMultiRepl) + ( ProjectConfig (..) + , ProjectConfigShared (..) ) import Distribution.Client.ReplFlags ( EnvFlags (envIncludeTransitive, envPackages) @@ -184,6 +185,7 @@ 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 +import Distribution.Types.PackageName.Magic (fakePackageId) import System.Directory ( doesFileExist , getCurrentDirectory @@ -195,6 +197,7 @@ import System.FilePath , splitSearchPath , () ) +import Text.PrettyPrint hiding ((<>)) replCommand :: CommandUI (NixStyleFlags ReplFlags) replCommand = @@ -281,17 +284,30 @@ multiReplDecision ctx compiler flags = -- For more details on how this works, see the module -- "Distribution.Client.ProjectOrchestration" replAction :: NixStyleFlags ReplFlags -> [String] -> GlobalFlags -> IO () -replAction flags@NixStyleFlags{extraFlags = r@ReplFlags{..}, ..} targetStrings globalFlags = - withContextAndSelectors verbosity AcceptNoTargets (Just LibKind) flags targetStrings globalFlags ReplCommand $ \targetCtx ctx targetSelectors -> do +replAction flags@NixStyleFlags{extraFlags = replFlags@ReplFlags{..}, configFlags} targetStrings globalFlags = do + withCtx verbosity targetStrings $ \targetCtx ctx userTargetSelectors -> do when (buildSettingOnlyDeps (buildSettings ctx)) $ dieWithException verbosity ReplCommandDoesn'tSupport let projectRoot = distProjectRootDirectory $ distDirLayout ctx distDir = distDirectory $ distDirLayout ctx - baseCtx <- case targetCtx of - ProjectContext -> return ctx + -- After ther user selectors have been resolved, and it's decided what context + -- we're in, implement repl-specific behaviour. + (baseCtx, targetSelectors) <- case targetCtx of + -- If in the project context, and no selectors are provided + -- then produce an error. + ProjectContext -> do + let projectFile = projectConfigProjectFile . projectConfigShared $ projectConfig ctx + let pkgs = projectPackages $ projectConfig ctx + case userTargetSelectors of + [] -> + dieWithException verbosity $ + RenderReplTargetProblem [render (reportProjectNoTarget projectFile pkgs)] + _ -> return (ctx, userTargetSelectors) + -- In the global context, construct a fake package which can be used to start + -- a repl with extra arguments if `-b` is given. GlobalContext -> do - unless (null targetStrings) $ + unless (null userTargetSelectors) $ dieWithException verbosity $ ReplTakesNoArguments targetStrings let @@ -303,12 +319,18 @@ replAction flags@NixStyleFlags{extraFlags = r@ReplFlags{..}, ..} targetStrings g library = emptyLibrary{libBuildInfo = lBuildInfo} lBuildInfo = emptyBuildInfo - { targetBuildDepends = [baseDep] + { targetBuildDepends = [baseDep] ++ envPackages replEnvFlags , defaultLanguage = Just Haskell2010 } baseDep = Dependency "base" anyVersion mainLibSet - updateContextAndWriteProjectFile' ctx sourcePackage + -- Write the fake package + updatedCtx <- updateContextAndWriteProjectFile' ctx sourcePackage + -- Specify the selector for this package + let fakeSelector = TargetPackage TargetExplicitNamed [fakePackageId] Nothing + return (updatedCtx, [fakeSelector]) + + -- For the script context, no special behaviour. ScriptContext scriptPath scriptExecutable -> do unless (length targetStrings == 1) $ dieWithException verbosity $ @@ -318,7 +340,8 @@ replAction flags@NixStyleFlags{extraFlags = r@ReplFlags{..}, ..} targetStrings g dieWithException verbosity $ ReplTakesSingleArgument targetStrings - updateContextAndWriteProjectFile ctx scriptPath scriptExecutable + updatedCtx <- updateContextAndWriteProjectFile ctx scriptPath scriptExecutable + return (updatedCtx, userTargetSelectors) -- If multi-repl is used, we need a Cabal recent enough to handle it. -- We need to do this before solving, but the compiler version is only known @@ -361,7 +384,7 @@ replAction flags@NixStyleFlags{extraFlags = r@ReplFlags{..}, ..} targetStrings g -- especially in the no-project case. withInstallPlan (lessVerbose verbosity) baseCtx' $ \elaboratedPlan sharedConfig -> do -- targets should be non-empty map, but there's no NonEmptyMap yet. - targets <- validatedTargets (projectConfigShared (projectConfig ctx)) (pkgConfigCompiler sharedConfig) elaboratedPlan targetSelectors + targets <- validatedTargets' (projectConfigShared (projectConfig ctx)) (pkgConfigCompiler sharedConfig) elaboratedPlan targetSelectors let (unitId, _) = fromMaybe (error "panic: targets should be non-empty") $ safeHead $ Map.toList targets @@ -385,7 +408,7 @@ replAction flags@NixStyleFlags{extraFlags = r@ReplFlags{..}, ..} targetStrings g let ProjectBaseContext{..} = baseCtx'' -- Recalculate with updated project. - targets <- validatedTargets (projectConfigShared projectConfig) (pkgConfigCompiler elaboratedShared') elaboratedPlan targetSelectors + targets <- validatedTargets' (projectConfigShared projectConfig) (pkgConfigCompiler elaboratedShared') elaboratedPlan targetSelectors let elaboratedPlan' = @@ -518,31 +541,13 @@ replAction flags@NixStyleFlags{extraFlags = r@ReplFlags{..}, ..} targetStrings g go m ("PATH", Just s) = foldl' (\m' f -> Map.insertWith (+) f 1 m') m (splitSearchPath s) go m _ = m + withCtx ctxVerbosity strings = + withContextAndSelectors ctxVerbosity AcceptNoTargets (Just LibKind) flags strings globalFlags ReplCommand + verbosity = cfgVerbosity normal flags tempFileOptions = commonSetupTempFileOptions $ configCommonFlags configFlags - validatedTargets ctx compiler elaboratedPlan targetSelectors = do - let multi_repl_enabled = multiReplDecision ctx compiler r - -- Interpret the targets on the command line as repl targets - -- (as opposed to say build or haddock targets). - targets <- - either (reportTargetProblems verbosity) return $ - resolveTargetsFromSolver - (selectPackageTargets multi_repl_enabled) - selectComponentTarget - elaboratedPlan - Nothing - targetSelectors - - -- Reject multiple targets, or at least targets in different - -- components. It is ok to have two module/file targets in the - -- same component, but not two that live in different components. - when (Set.size (distinctTargetComponents targets) > 1 && not (useMultiRepl multi_repl_enabled)) $ - reportTargetProblems - verbosity - [multipleTargetsProblem multi_repl_enabled targets] - - return targets + validatedTargets' = validatedTargets verbosity replFlags -- | Create a constraint which requires a later version of Cabal. -- This is used for commands which require a specific feature from the Cabal library @@ -555,6 +560,69 @@ requireCabal version source = , source ) +reportProjectNoTarget :: Flag FilePath -> [String] -> Doc +reportProjectNoTarget projectFile pkgs = + case (null pkgs, projectName) of + (True, Just project) -> + text "There are no packages in" + <+> (project <> char '.') + <+> text "Please add a package to the project and" + <+> pickComponent + (True, Nothing) -> + text "Please add a package to the project and" <+> pickComponent + (False, Just project) -> + text "Please" + <+> pickComponent + <+> text "The packages in" + <+> project + <+> (text "from which to select a component target are" <> colon) + $+$ nest 1 (vcat [text "-" <+> text pkg | pkg <- sort pkgs]) + (False, Nothing) -> + text "Please" + <+> pickComponent + <+> (text "The packages from which to select a component in 'cabal.project'" <> comma) + <+> (text "the implicit default as if `--project-file=cabal.project` was added as a command option" <> comma) + <+> (text "are" <> colon) + $+$ nest 1 (vcat [text "-" <+> text pkg | pkg <- sort pkgs]) + where + projectName = case projectFile of + Flag "" -> Nothing + Flag n -> Just $ quotes (text n) + _ -> Nothing + pickComponent = text "pick a single [package:][ctype:]component (or all) as target for the REPL command." + +-- | Invariant: validatedTargets returns at least one target for the REPL. +validatedTargets + :: Verbosity + -> ReplFlags + -> ProjectConfigShared + -> Compiler + -> ElaboratedInstallPlan + -> [TargetSelector] + -> IO TargetsMap +validatedTargets verbosity replFlags ctx compiler elaboratedPlan targetSelectors = do + let multi_repl_enabled = multiReplDecision ctx compiler replFlags + -- Interpret the targets on the command line as repl targets (as opposed to + -- say build or haddock targets). + targets <- + either (reportTargetProblems verbosity) return $ + resolveTargetsFromSolver + (selectPackageTargets multi_repl_enabled) + selectComponentTarget + elaboratedPlan + Nothing + targetSelectors + + -- Reject multiple targets, or at least targets in different components. It is + -- ok to have two module/file targets in the same component, but not two that + -- live in different components. + when (Set.size (distinctTargetComponents targets) > 1 && not (useMultiRepl multi_repl_enabled)) $ + reportTargetProblems + verbosity + [multipleTargetsProblem multi_repl_enabled targets] + + return targets + -- | First version of GHC which supports multiple home packages minMultipleHomeUnitsVersion :: Version minMultipleHomeUnitsVersion = mkVersion [9, 4] diff --git a/cabal-testsuite/PackageTests/ReplDashB/File.hs b/cabal-testsuite/PackageTests/ReplDashB/File.hs new file mode 100644 index 00000000000..1a4f18e5a15 --- /dev/null +++ b/cabal-testsuite/PackageTests/ReplDashB/File.hs @@ -0,0 +1 @@ +module File where diff --git a/cabal-testsuite/PackageTests/ReplDashB/cabal.out b/cabal-testsuite/PackageTests/ReplDashB/cabal.out new file mode 100644 index 00000000000..46fee55f1a9 --- /dev/null +++ b/cabal-testsuite/PackageTests/ReplDashB/cabal.out @@ -0,0 +1,8 @@ +# cabal clean +# cabal v2-repl +Resolving dependencies... +Build profile: -w ghc- -O1 +In order, the following will be built: + - fake-package-0 (interactive) (lib) (first run) +Configuring library for fake-package-0... +Warning: No exposed modules diff --git a/cabal-testsuite/PackageTests/ReplDashB/cabal.test.hs b/cabal-testsuite/PackageTests/ReplDashB/cabal.test.hs new file mode 100644 index 00000000000..1798d813b51 --- /dev/null +++ b/cabal-testsuite/PackageTests/ReplDashB/cabal.test.hs @@ -0,0 +1,8 @@ +import Test.Cabal.Prelude + +main = do + cabalTest $ do + cabal' "clean" [] + res <- cabalWithStdin "v2-repl" ["-b", "containers"] ":m +Data.Map\n:t fromList" + assertOutputContains "fromList :: Ord k => [(k, a)] -> Map k a" res + diff --git a/cabal-testsuite/PackageTests/ReplOptions/alt.project b/cabal-testsuite/PackageTests/ReplOptions/alt.project new file mode 100644 index 00000000000..42e62e64527 --- /dev/null +++ b/cabal-testsuite/PackageTests/ReplOptions/alt.project @@ -0,0 +1 @@ +packages: alt diff --git a/cabal-testsuite/PackageTests/ReplOptions/alt/ModuleA.hs b/cabal-testsuite/PackageTests/ReplOptions/alt/ModuleA.hs new file mode 100644 index 00000000000..c449728173e --- /dev/null +++ b/cabal-testsuite/PackageTests/ReplOptions/alt/ModuleA.hs @@ -0,0 +1,4 @@ +module ModuleA where + +a :: Int +a = 42 diff --git a/cabal-testsuite/PackageTests/ReplOptions/alt/ModuleC.hs b/cabal-testsuite/PackageTests/ReplOptions/alt/ModuleC.hs new file mode 100644 index 00000000000..45bcb062103 --- /dev/null +++ b/cabal-testsuite/PackageTests/ReplOptions/alt/ModuleC.hs @@ -0,0 +1,4 @@ +module ModuleC where + +c :: Int +c = 42 diff --git a/cabal-testsuite/PackageTests/ReplOptions/alt/alt.cabal b/cabal-testsuite/PackageTests/ReplOptions/alt/alt.cabal new file mode 100644 index 00000000000..57dcd758380 --- /dev/null +++ b/cabal-testsuite/PackageTests/ReplOptions/alt/alt.cabal @@ -0,0 +1,10 @@ +name: alt +version: 0.1 +build-type: Simple +cabal-version: >= 1.10 + +library + exposed-modules: ModuleA, ModuleC + build-depends: base + default-language: Haskell2010 + diff --git a/cabal-testsuite/PackageTests/ReplOptions/cabal.alt-multiple-repl-options.out b/cabal-testsuite/PackageTests/ReplOptions/cabal.alt-multiple-repl-options.out new file mode 100644 index 00000000000..ce6b9f493be --- /dev/null +++ b/cabal-testsuite/PackageTests/ReplOptions/cabal.alt-multiple-repl-options.out @@ -0,0 +1,5 @@ +# cabal clean +# cabal v2-repl +Error: [Cabal-7076] +Please pick a single [package:][ctype:]component (or all) as target for the REPL command. The packages in 'alt.project' from which to select a component target are: + - alt diff --git a/cabal-testsuite/PackageTests/ReplOptions/cabal.alt-single-repl-options.out b/cabal-testsuite/PackageTests/ReplOptions/cabal.alt-single-repl-options.out new file mode 100644 index 00000000000..ce6b9f493be --- /dev/null +++ b/cabal-testsuite/PackageTests/ReplOptions/cabal.alt-single-repl-options.out @@ -0,0 +1,5 @@ +# cabal clean +# cabal v2-repl +Error: [Cabal-7076] +Please pick a single [package:][ctype:]component (or all) as target for the REPL command. The packages in 'alt.project' from which to select a component target are: + - alt diff --git a/cabal-testsuite/PackageTests/ReplOptions/cabal.test.hs b/cabal-testsuite/PackageTests/ReplOptions/cabal.test.hs index 6c72166ed5f..f1fe06ab80a 100644 --- a/cabal-testsuite/PackageTests/ReplOptions/cabal.test.hs +++ b/cabal-testsuite/PackageTests/ReplOptions/cabal.test.hs @@ -1,29 +1,46 @@ import Test.Cabal.Prelude +singleOpts = ["--repl-options=-fwrite-interface"] +multiOpts = "--repl-options=-fdefer-typed-holes" : singleOpts +altProject = ("--project-file=alt.project" :) + main = do cabalTest' "single-repl-options" $ do cabal' "clean" [] - res <- cabalWithStdin "v2-repl" ["--repl-options=-fwrite-interface"] ":set" + res <- cabalWithStdin "v2-repl" singleOpts ":set" assertOutputContains "Ok, two modules loaded." res - assertOutputContains " -fwrite-interface" res + + cabalTest' "alt-single-repl-options" $ do + cabal' "clean" [] + -- We can't 'cabal repl' without a target when the project has a single package. + void $ fails $ cabalWithStdin "v2-repl" (altProject singleOpts) ":set" + cabalTest' "multiple-repl-options" $ do cabal' "clean" [] - res <- cabalWithStdin "v2-repl" ["--repl-options=-fwrite-interface", "--repl-options=-fdefer-typed-holes"] ":set" + res <- cabalWithStdin "v2-repl" multiOpts ":set" assertOutputContains "Ok, two modules loaded." res assertOutputContains " -fwrite-interface" res assertOutputContains " -fdefer-typed-holes" res + + cabalTest' "alt-multiple-repl-options" $ do + cabal' "clean" [] + -- We can't 'cabal repl' without a target when the project has a single package. + void $ fails $ cabalWithStdin "v2-repl" (altProject multiOpts) ":set" + cabalTest' "single-repl-options-multiple-flags" $ do cabal' "clean" [] res <- cabalWithStdin "v2-repl" ["--repl-options=-fdefer-typed-holes -fwrite-interface"] ":set" assertOutputContains "Ok, two modules loaded." res assertOutputContains " -fwrite-interface" res assertOutputContains " -fdefer-typed-holes" res + cabalTest' "single-repl-options-multiple-flags-negative" $ do cabal' "clean" [] res <- fails $ cabalWithStdin "v2-repl" ["--repl-options=-fwrite-interface -fdiagnostics-show-baret"] ":set" assertOutputDoesNotContain "Ok, two modules loaded." res assertOutputContains "unrecognised flag: -fdiagnostics-show-baret" res assertOutputContains "did you mean one of:" res + cabalTest' "multiple-repl-options-multiple-flags" $ do cabal' "clean" [] res <- cabalWithStdin "v2-repl" [ diff --git a/cabal-testsuite/PackageTests/ReplProjectNoneTarget/cabal.out b/cabal-testsuite/PackageTests/ReplProjectNoneTarget/cabal.out new file mode 100644 index 00000000000..17f03a432f3 --- /dev/null +++ b/cabal-testsuite/PackageTests/ReplProjectNoneTarget/cabal.out @@ -0,0 +1,18 @@ +# checking repl command with no project and --ignore-project +# cabal repl +Resolving dependencies... +Build profile: -w ghc- -O1 +In order, the following will be built: + - fake-package-0 (interactive) (lib) (first run) +Configuring library for fake-package-0... +Warning: No exposed modules +# checking repl command with no project and no project options +# cabal repl +Resolving dependencies... +Build profile: -w ghc- -O1 +In order, the following will be built: + - fake-package-0 (interactive) (lib) (configuration changed) +Configuring library for fake-package-0... +Warning: No exposed modules +# checking repl command with a missing project +# cabal repl diff --git a/cabal-testsuite/PackageTests/ReplProjectNoneTarget/cabal.test.hs b/cabal-testsuite/PackageTests/ReplProjectNoneTarget/cabal.test.hs new file mode 100644 index 00000000000..dd8f319b577 --- /dev/null +++ b/cabal-testsuite/PackageTests/ReplProjectNoneTarget/cabal.test.hs @@ -0,0 +1,22 @@ +import Test.Cabal.Prelude + +main = cabalTest . recordMode RecordMarked $ do + let log = recordHeader . pure + + -- The following output is not what we want but is the current behaviour that + -- refers to the fake package. + log "checking repl command with no project and --ignore-project" + ignored <- cabalWithStdin "repl" ["--ignore-project"] "" + assertOutputContains "fake-package-0 (interactive) (lib) (first run)" ignored + + -- The following output is not what we want but is the current behaviour that + -- refers to the fake package. + log "checking repl command with no project and no project options" + noOptions <- cabalWithStdin "repl" [] "" + assertOutputContains "fake-package-0 (interactive) (lib) (configuration changed)" noOptions + + log "checking repl command with a missing project" + missing <- fails $ cabalWithStdin "repl" [ "--project-file=missing.project" ] "" + assertOutputContains "The given project file 'missing.project' does not exist." missing + + return () diff --git a/cabal-testsuite/PackageTests/ReplProjectNoneTarget/pkg-one/pkg-one.cabal b/cabal-testsuite/PackageTests/ReplProjectNoneTarget/pkg-one/pkg-one.cabal new file mode 100644 index 00000000000..11d300a05a6 --- /dev/null +++ b/cabal-testsuite/PackageTests/ReplProjectNoneTarget/pkg-one/pkg-one.cabal @@ -0,0 +1,9 @@ +name: pkg-one +version: 0.1 +license: BSD3 +cabal-version: >= 1.2 +build-type: Simple + +library + exposed-modules: Foo + build-depends: base diff --git a/cabal-testsuite/PackageTests/ReplProjectNoneTarget/pkg-two/pkg-one.cabal b/cabal-testsuite/PackageTests/ReplProjectNoneTarget/pkg-two/pkg-one.cabal new file mode 100644 index 00000000000..11d300a05a6 --- /dev/null +++ b/cabal-testsuite/PackageTests/ReplProjectNoneTarget/pkg-two/pkg-one.cabal @@ -0,0 +1,9 @@ +name: pkg-one +version: 0.1 +license: BSD3 +cabal-version: >= 1.2 +build-type: Simple + +library + exposed-modules: Foo + build-depends: base diff --git a/cabal-testsuite/PackageTests/ReplProjectTargetOnePkg/cabal.out b/cabal-testsuite/PackageTests/ReplProjectTargetOnePkg/cabal.out new file mode 100644 index 00000000000..f070f04cb23 --- /dev/null +++ b/cabal-testsuite/PackageTests/ReplProjectTargetOnePkg/cabal.out @@ -0,0 +1,26 @@ +# checking repl command with a 'cabal.project' and --ignore-project +# cabal repl +Resolving dependencies... +Build profile: -w ghc- -O1 +In order, the following will be built: + - fake-package-0 (interactive) (lib) (first run) +Configuring library for fake-package-0... +Warning: No exposed modules +# checking repl command with a 'cabal.project' and no project options +# cabal repl +Error: [Cabal-7076] +Please pick a single [package:][ctype:]component (or all) as target for the REPL command. The packages from which to select a component in 'cabal.project', the implicit default as if `--project-file=cabal.project` was added as a command option, are: + - pkg-one +# checking repl command with a single package in 'cabal.project' +# cabal repl +Error: [Cabal-7076] +Please pick a single [package:][ctype:]component (or all) as target for the REPL command. The packages in 'cabal.project' from which to select a component target are: + - pkg-one +# checking repl command with the 'all' target +# cabal repl +Resolving dependencies... +Build profile: -w ghc- -O1 +In order, the following will be built: + - pkg-one-0.1 (interactive) (first run) +Configuring pkg-one-0.1... +Preprocessing library for pkg-one-0.1... diff --git a/cabal-testsuite/PackageTests/ReplProjectTargetOnePkg/cabal.project b/cabal-testsuite/PackageTests/ReplProjectTargetOnePkg/cabal.project new file mode 100644 index 00000000000..46b734d0ad3 --- /dev/null +++ b/cabal-testsuite/PackageTests/ReplProjectTargetOnePkg/cabal.project @@ -0,0 +1 @@ +packages: pkg-one diff --git a/cabal-testsuite/PackageTests/ReplProjectTargetOnePkg/cabal.test.hs b/cabal-testsuite/PackageTests/ReplProjectTargetOnePkg/cabal.test.hs new file mode 100644 index 00000000000..774f3fecf5e --- /dev/null +++ b/cabal-testsuite/PackageTests/ReplProjectTargetOnePkg/cabal.test.hs @@ -0,0 +1,27 @@ +import Test.Cabal.Prelude +import Data.List (isInfixOf) + +main = cabalTest . recordMode RecordMarked $ do + liftIO $ skipIfWindows "I'm seeing extra newlines in the output on Windows" + let log = recordHeader . pure + + log "checking repl command with a 'cabal.project' and --ignore-project" + ignored <- cabalWithStdin "repl" ["--ignore-project"] "" + assertOutputContains "fake-package-0 (interactive) (lib) (first run)" ignored + + log "checking repl command with a 'cabal.project' and no project options" + defaultProject <- fails $ cabalWithStdin "repl" [] "" +-- assertOutputContains "the following will be built" defaultProject +-- assertOutputContains "pkg-one-0.1" defaultProject + + log "checking repl command with a single package in 'cabal.project'" + defaultProject <- fails $ cabalWithStdin "repl" [ "--project-file=cabal.project" ] "" +-- assertOutputContains "the following will be built" defaultProject +-- assertOutputContains "pkg-one-0.1" defaultProject + + log "checking repl command with the 'all' target" + allTarget <- cabalWithStdin "repl" ["all"] "" + assertOutputContains "the following will be built" allTarget + assertOutputContains "pkg-one-0.1" allTarget + + return () diff --git a/cabal-testsuite/PackageTests/ReplProjectTargetOnePkg/pkg-one/Foo.hs b/cabal-testsuite/PackageTests/ReplProjectTargetOnePkg/pkg-one/Foo.hs new file mode 100644 index 00000000000..8a39fe134cf --- /dev/null +++ b/cabal-testsuite/PackageTests/ReplProjectTargetOnePkg/pkg-one/Foo.hs @@ -0,0 +1,4 @@ +module Foo where + +a :: Int +a = 42 diff --git a/cabal-testsuite/PackageTests/ReplProjectTargetOnePkg/pkg-one/pkg-one.cabal b/cabal-testsuite/PackageTests/ReplProjectTargetOnePkg/pkg-one/pkg-one.cabal new file mode 100644 index 00000000000..3e17e5074d7 --- /dev/null +++ b/cabal-testsuite/PackageTests/ReplProjectTargetOnePkg/pkg-one/pkg-one.cabal @@ -0,0 +1,9 @@ +name: pkg-one +version: 0.1 +license: BSD3 +cabal-version: >=1.2 +build-type: Simple + +library + exposed-modules: Foo + build-depends: base diff --git a/cabal-testsuite/PackageTests/ReplProjectTargetTwoPkgs/all-repl.txt b/cabal-testsuite/PackageTests/ReplProjectTargetTwoPkgs/all-repl.txt new file mode 100644 index 00000000000..f2322b5e3e9 --- /dev/null +++ b/cabal-testsuite/PackageTests/ReplProjectTargetTwoPkgs/all-repl.txt @@ -0,0 +1,3 @@ +In order, the following will be built: + - pkg-one-0.1 (interactive) (first run) + - pkg-two-0.1 (interactive) (first run) diff --git a/cabal-testsuite/PackageTests/ReplProjectTargetTwoPkgs/cabal.out b/cabal-testsuite/PackageTests/ReplProjectTargetTwoPkgs/cabal.out new file mode 100644 index 00000000000..d1663d89d60 --- /dev/null +++ b/cabal-testsuite/PackageTests/ReplProjectTargetTwoPkgs/cabal.out @@ -0,0 +1,46 @@ +# checking repl command with a 'cabal.project' and --ignore-project +# cabal repl +Resolving dependencies... +Build profile: -w ghc- -O1 +In order, the following will be built: + - fake-package-0 (interactive) (lib) (first run) +Configuring library for fake-package-0... +Warning: No exposed modules +# checking repl command with a 'cabal.project' and no project options +# cabal repl +Error: [Cabal-7076] +Please pick a single [package:][ctype:]component (or all) as target for the REPL command. The packages from which to select a component in 'cabal.project', the implicit default as if `--project-file=cabal.project` was added as a command option, are: + - pkg-one + - pkg-two +# checking repl command using an explicit 'some.project' +# cabal repl +Error: [Cabal-7076] +Please pick a single [package:][ctype:]component (or all) as target for the REPL command. The packages in 'some.project' from which to select a component target are: + - pkg-one + - pkg-two +# checking repl command using an explicit 'reverse.project', listing packages in reverse order +# cabal repl +Error: [Cabal-7076] +Please pick a single [package:][ctype:]component (or all) as target for the REPL command. The packages in 'reverse.project' from which to select a component target are: + - pkg-one + - pkg-two +# checking repl command with an 'empty.project' with no packages +# cabal repl +Warning: There are no packages or optional-packages in the project +Error: [Cabal-7076] +There are no packages in 'empty.project'. Please add a package to the project and pick a single [package:][ctype:]component (or all) as target for the REPL command. +# checking repl command with a missing 'missing.project' +# cabal repl +# checking repl command with a missing 'missing.project' +# cabal repl +# checking repl command with the 'all' target +# cabal repl +Resolving dependencies... +Build profile: -w ghc- -O1 +In order, the following will be built: + - pkg-one-0.1 (interactive) (first run) + - pkg-two-0.1 (interactive) (first run) +Configuring pkg-one-0.1... +Preprocessing library for pkg-one-0.1... +Configuring pkg-two-0.1... +Preprocessing library for pkg-two-0.1... diff --git a/cabal-testsuite/PackageTests/ReplProjectTargetTwoPkgs/cabal.project b/cabal-testsuite/PackageTests/ReplProjectTargetTwoPkgs/cabal.project new file mode 100644 index 00000000000..80c5979562a --- /dev/null +++ b/cabal-testsuite/PackageTests/ReplProjectTargetTwoPkgs/cabal.project @@ -0,0 +1 @@ +packages: pkg-one pkg-two diff --git a/cabal-testsuite/PackageTests/ReplProjectTargetTwoPkgs/cabal.test.hs b/cabal-testsuite/PackageTests/ReplProjectTargetTwoPkgs/cabal.test.hs new file mode 100644 index 00000000000..cb95a819f29 --- /dev/null +++ b/cabal-testsuite/PackageTests/ReplProjectTargetTwoPkgs/cabal.test.hs @@ -0,0 +1,52 @@ +import Test.Cabal.Prelude +import Data.List (isInfixOf) + +main = cabalTest . recordMode RecordMarked $ do + liftIO $ skipIfWindows "I'm seeing extra newlines in the output on Windows" + let log = recordHeader . pure + + log "checking repl command with a 'cabal.project' and --ignore-project" + ignored <- cabalWithStdin "repl" ["--ignore-project"] "" + assertOutputContains "fake-package-0 (interactive) (lib) (first run)" ignored + + log "checking repl command with a 'cabal.project' and no project options" + defaultProject <- fails $ cabalWithStdin "repl" [] "" + + readFileVerbatim "default-repl.txt" + >>= flip (assertOn isInfixOf multilineNeedleHaystack) defaultProject . normalizePathSeparators + + log "checking repl command using an explicit 'some.project'" + someProject <- fails $ cabalWithStdin "repl" [ "--project-file=some.project" ] "" + + readFileVerbatim "some-repl.txt" + >>= flip (assertOn isInfixOf multilineNeedleHaystack) someProject . normalizePathSeparators + + log "checking repl command using an explicit 'reverse.project', listing packages in reverse order" + reverseProject <- fails $ cabalWithStdin "repl" [ "--project-file=reverse.project" ] "" + + readFileVerbatim "reverse-repl.txt" + >>= flip (assertOn isInfixOf multilineNeedleHaystack) reverseProject . normalizePathSeparators + + log "checking repl command with an 'empty.project' with no packages" + emptyProject <- fails $ cabalWithStdin "repl" [ "--project-file=empty.project" ] "" + + log "checking repl command with a missing 'missing.project'" + missing <- fails $ cabalWithStdin "repl" [ "--project-file=missing.project" ] "" + assertOutputContains "The given project file 'missing.project' does not exist." missing + + log "checking repl command with a missing 'missing.project'" + dotMissing <- fails $ cabalWithStdin "repl" [ "--project-dir=.", "--project-file=missing.project" ] "" + assertOutputContains "The given project directory/file combination './missing.project' does not exist." dotMissing + + -- "all" target only works if tested GHC is >= 9.4.1 + -- If we skip here, at least we will get a failure from the other parts of the test + -- before we reach the skip. + skipUnlessGhcVersion ">= 9.4.1" + + log "checking repl command with the 'all' target" + allTarget <- cabalWithStdin "repl" ["all", "--enable-multi-repl"] "" + + readFileVerbatim "all-repl.txt" + >>= flip (assertOn isInfixOf multilineNeedleHaystack) allTarget . normalizePathSeparators + + return () diff --git a/cabal-testsuite/PackageTests/ReplProjectTargetTwoPkgs/default-repl.txt b/cabal-testsuite/PackageTests/ReplProjectTargetTwoPkgs/default-repl.txt new file mode 100644 index 00000000000..47f752a74f6 --- /dev/null +++ b/cabal-testsuite/PackageTests/ReplProjectTargetTwoPkgs/default-repl.txt @@ -0,0 +1,3 @@ +Please pick a single [package:][ctype:]component (or all) as target for the REPL command. The packages from which to select a component in 'cabal.project', the implicit default as if `--project-file=cabal.project` was added as a command option, are: + - pkg-one + - pkg-two diff --git a/cabal-testsuite/PackageTests/ReplProjectTargetTwoPkgs/empty.project b/cabal-testsuite/PackageTests/ReplProjectTargetTwoPkgs/empty.project new file mode 100644 index 00000000000..e69de29bb2d diff --git a/cabal-testsuite/PackageTests/ReplProjectTargetTwoPkgs/pkg-one/Foo.hs b/cabal-testsuite/PackageTests/ReplProjectTargetTwoPkgs/pkg-one/Foo.hs new file mode 100644 index 00000000000..8a39fe134cf --- /dev/null +++ b/cabal-testsuite/PackageTests/ReplProjectTargetTwoPkgs/pkg-one/Foo.hs @@ -0,0 +1,4 @@ +module Foo where + +a :: Int +a = 42 diff --git a/cabal-testsuite/PackageTests/ReplProjectTargetTwoPkgs/pkg-one/pkg-one.cabal b/cabal-testsuite/PackageTests/ReplProjectTargetTwoPkgs/pkg-one/pkg-one.cabal new file mode 100644 index 00000000000..3e17e5074d7 --- /dev/null +++ b/cabal-testsuite/PackageTests/ReplProjectTargetTwoPkgs/pkg-one/pkg-one.cabal @@ -0,0 +1,9 @@ +name: pkg-one +version: 0.1 +license: BSD3 +cabal-version: >=1.2 +build-type: Simple + +library + exposed-modules: Foo + build-depends: base diff --git a/cabal-testsuite/PackageTests/ReplProjectTargetTwoPkgs/pkg-two/Bar.hs b/cabal-testsuite/PackageTests/ReplProjectTargetTwoPkgs/pkg-two/Bar.hs new file mode 100644 index 00000000000..870176c2b2d --- /dev/null +++ b/cabal-testsuite/PackageTests/ReplProjectTargetTwoPkgs/pkg-two/Bar.hs @@ -0,0 +1,4 @@ +module Bar where + +a :: Int +a = 42 diff --git a/cabal-testsuite/PackageTests/ReplProjectTargetTwoPkgs/pkg-two/pkg-two.cabal b/cabal-testsuite/PackageTests/ReplProjectTargetTwoPkgs/pkg-two/pkg-two.cabal new file mode 100644 index 00000000000..4fec067adb0 --- /dev/null +++ b/cabal-testsuite/PackageTests/ReplProjectTargetTwoPkgs/pkg-two/pkg-two.cabal @@ -0,0 +1,9 @@ +name: pkg-two +version: 0.1 +license: BSD3 +cabal-version: >= 1.2 +build-type: Simple + +library + exposed-modules: Bar + build-depends: base diff --git a/cabal-testsuite/PackageTests/ReplProjectTargetTwoPkgs/reverse-repl.txt b/cabal-testsuite/PackageTests/ReplProjectTargetTwoPkgs/reverse-repl.txt new file mode 100644 index 00000000000..24d01c795ac --- /dev/null +++ b/cabal-testsuite/PackageTests/ReplProjectTargetTwoPkgs/reverse-repl.txt @@ -0,0 +1,3 @@ +Please pick a single [package:][ctype:]component (or all) as target for the REPL command. The packages in 'reverse.project' from which to select a component target are: + - pkg-one + - pkg-two diff --git a/cabal-testsuite/PackageTests/ReplProjectTargetTwoPkgs/reverse.project b/cabal-testsuite/PackageTests/ReplProjectTargetTwoPkgs/reverse.project new file mode 100644 index 00000000000..0224f32119b --- /dev/null +++ b/cabal-testsuite/PackageTests/ReplProjectTargetTwoPkgs/reverse.project @@ -0,0 +1 @@ +packages: pkg-two pkg-one diff --git a/cabal-testsuite/PackageTests/ReplProjectTargetTwoPkgs/some-repl.txt b/cabal-testsuite/PackageTests/ReplProjectTargetTwoPkgs/some-repl.txt new file mode 100644 index 00000000000..5488ccf7693 --- /dev/null +++ b/cabal-testsuite/PackageTests/ReplProjectTargetTwoPkgs/some-repl.txt @@ -0,0 +1,4 @@ +Please pick a single [package:][ctype:]component (or all) as target for the REPL command. The packages in 'some.project' from which to select a component target are: + - pkg-one + - pkg-two + diff --git a/cabal-testsuite/PackageTests/ReplProjectTargetTwoPkgs/some.project b/cabal-testsuite/PackageTests/ReplProjectTargetTwoPkgs/some.project new file mode 100644 index 00000000000..80c5979562a --- /dev/null +++ b/cabal-testsuite/PackageTests/ReplProjectTargetTwoPkgs/some.project @@ -0,0 +1 @@ +packages: pkg-one pkg-two diff --git a/changelog.d/pr-10684.md b/changelog.d/pr-10684.md new file mode 100644 index 00000000000..24cc0beefca --- /dev/null +++ b/changelog.d/pr-10684.md @@ -0,0 +1,64 @@ +--- +synopsis: "Better error messages when repl command is missing a target" +packages: [cabal-install] +prs: 10684 +issues: 10527 +--- + +With a project, the REPL command requires a target. If one is not given then a +message is shown explaining this and naming the project if the `--project-file` +option was given (but not when the default 'cabal.project' project name is used +implicitly). We're not yet able to list project targets so in the meantime, the +messages lists the packages of the project. + +* When the implicit default `cabal.project` is used: + +``` +$ cat cabal.project +packages: pkg-one pkg-two + +$ cabal repl +Error: [Cabal-7076] +With a project, the REPL command requires a single target. The packages in this project are: + - pkg-one + - pkg-two +``` + +* When the `--project-file` option is used, the file name is included: + +``` +$ cat some.project +packages: pkg-one pkg-two + +$ cabal repl --project-file=some.project +... +Error: [Cabal-7076] +With a project, the REPL command requires a single target. The packages in this project, 'some.project', are: + - pkg-one + - pkg-two +``` + +* When the project has no packages, this is mentioned in the message: + +``` +$ cat empty.project + +$ cabal repl --project-file=empty.project +... +Error: [Cabal-7076] +With a project, the REPL command requires a single target but there are no +packages in this project, 'empty.project', to choose a package (library) or +other component from as the target for this command. +``` + +* Before the fix the message mentioned a `fake-package-0`. This was confusing: + +``` +$ ~/.ghcup/bin/cabal-3.12.1.0 repl --project-file=some.project +... +Error: [Cabal-7076] +Internal error when trying to open a repl for the package fake-package-0. The +package is not in the set of available targets for the project plan, which would +suggest an inconsistency between readTargetSelectors and resolveTargets. +``` + diff --git a/changelog.d/pr-11237 b/changelog.d/pr-11237 new file mode 100644 index 00000000000..05fef4b432a --- /dev/null +++ b/changelog.d/pr-11237 @@ -0,0 +1,13 @@ +--- +synopsis: "Fix regression when -b option is used in a non-project context" +packages: [cabal-install] +prs: 11237 +issues: 11107 +--- + +The `repl -b` option can be used to create a repl which contains certain specific +packages. + +`cabal repl -b vector`, starts a GHCi session with the `vector` package available. + +The flag was broken in the 3.16.0.0 release, and now fixed.