Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 59 additions & 4 deletions cabal-install/src/Distribution/Client/CmdRun.hs
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,9 @@ runCommand =
-- For more details on how this works, see the module
-- "Distribution.Client.ProjectOrchestration"
runAction :: NixStyleFlags () -> [String] -> GlobalFlags -> IO ()
runAction flags targetAndArgs globalFlags =
runAction flags targetAndArgs globalFlags = do
fullArgs <- getFullArgs
let (targetStr, args) = splitTargetAndArgs fullArgs targetAndArgs
withContextAndSelectors (cfgVerbosity normal flags) RejectNoTargets (Just ExeKind) flags targetStr globalFlags OtherCommand $ \targetCtx ctx targetSelectors -> do
(baseCtx, defaultVerbosity) <- case targetCtx of
ProjectContext -> return (ctx, normal)
Expand All @@ -213,7 +215,6 @@ runAction flags targetAndArgs globalFlags =
when (buildSettingOnlyDeps (buildSettings baseCtx)) $
dieWithException verbosity NoSupportForRunCommand

fullArgs <- getFullArgs
when (occursOnlyOrBefore fullArgs "+RTS" "--") $
warn verbosity $
giveRTSWarning "run"
Expand Down Expand Up @@ -353,8 +354,62 @@ runAction flags targetAndArgs globalFlags =
(distDirLayout baseCtx)
elaboratedPlan
}
where
(targetStr, args) = splitAt 1 targetAndArgs

-- | Split @cabal run@ arguments (@exe cmd@ arguments in the examples) into
-- target selectors and target executable arguments.
--
-- When a target is given it appears in both lists:
--
-- >>> splitTargetAndArgs ["exe", "cmd", "target"] ["target"]
-- (["target"],[])
--
-- The @+RTS@ argument is passed to the executable so only appears in the first
-- list:
--
-- >>> splitTargetAndArgs ["exe", "cmd", "target", "+RTS"] ["target"]
-- (["target"],[])
--
-- The @--@ follows the @+RTS@ argument, so @+RTS@ is passed to the executable
-- and only appears in the first list:
--
-- >>> splitTargetAndArgs ["exe", "cmd", "target", "+RTS", "--"] ["target"]
-- (["target"],[])
--
-- The @--@ precedes the @+RTS@ argument, so @+RTS@ is included in the
-- 'targetAndArgs' list as well:
--
-- >>> splitTargetAndArgs ["exe", "cmd", "target", "--", "+RTS"] ["target", "+RTS"]
-- (["target"],["+RTS"])
--
-- Same examples as above but when no target is given:
--
-- >>> splitTargetAndArgs ["exe", "cmd"] []
-- ([],[])
-- >>> splitTargetAndArgs ["exe", "cmd", "+RTS"] []
-- ([],[])
-- >>> splitTargetAndArgs ["exe", "cmd", "+RTS", "--"] []
-- ([],[])
-- >>> splitTargetAndArgs ["exe", "cmd", "--", "+RTS"] ["+RTS"]
-- ([],["+RTS"])
splitTargetAndArgs
:: [String]
-- ^ Full command line arguments, the original command line from
-- 'getFullArgs', which is only used to detect whether a @--@ separator was
-- present so that @cabal run -- ...@ keeps the target empty.
-> [String]
-- ^ The second argument is the parser-produced list that combines targets and
-- their arguments. These arguments do not include those passed to @cabal@
-- such as @+RTS@ preceding the @--@ separator.
-> ([String], [String])
splitTargetAndArgs fullArgs targetAndArgs = case dropWhile (/= "--") fullArgs of
("--" : exeArgs) ->
-- targetAndArgs contains targets (>=0) and args; exeArgs contains only args; so
-- the difference (>=0) is the number of targets
let numTargets = length targetAndArgs - length exeArgs
in splitAt numTargets targetAndArgs
_ ->
-- No '--': first element (if any) is the target.
splitAt 1 targetAndArgs

-- | Used by the main CLI parser as heuristic to decide whether @cabal@ was
-- invoked as a script interpreter, i.e. via
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# cabal run
Resolving dependencies...
Warning: Your RTS options are applied to cabal, not the executable. Use '--' to separate cabal options from your executable options. For example, use 'cabal run -- +RTS -N to pass the '-N' RTS option to your executable.
Build profile: -w ghc-<GHCVER> -O1
In order, the following will be built:
- WarningRTS-1.0 (exe:foo) (first run)
Configuring executable 'foo' for WarningRTS-1.0...
Preprocessing executable 'foo' for WarningRTS-1.0...
Building executable 'foo' for WarningRTS-1.0...
# cabal run
Warning: Your RTS options are applied to cabal, not the executable. Use '--' to separate cabal options from your executable options. For example, use 'cabal run -- +RTS -N to pass the '-N' RTS option to your executable.
# cabal run
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Test.Cabal.Prelude

main = cabalTest $ do
main = do
cabalTest $ do
res <- cabal' "run" ["foo", "+RTS"]
assertOutputContains "Warning: Your RTS options" res

Expand All @@ -9,3 +10,16 @@ main = cabalTest $ do

res <- cabal' "run" ["foo", "--", "+RTS"]
assertOutputDoesNotContain "Warning: Your RTS options" res

-- Regression tests for https://github.com/haskell/cabal/issues/10487:
-- 'cabal run -- +RTS' should not fail with "Unrecognised target '+RTS'"
cabalTest' "no-target" $ do
res <- cabal' "run" ["+RTS"]
assertOutputContains "Warning: Your RTS options" res

res <- cabal' "run" ["+RTS", "--"]
assertOutputContains "Warning: Your RTS options" res

res <- cabal' "run" ["--", "+RTS"]
assertOutputDoesNotContain "Warning: Your RTS options" res
assertOutputDoesNotContain "Unrecognised target" res
18 changes: 18 additions & 0 deletions changelog.d/pr-10487.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
synopsis: Fix `cabal run` handling of `--` with empty targets
packages: [cabal-install]
prs: 10487
issues: 10487
---

Previously, when using `--` to separate cabal options from executable
arguments, without an explicit target, the first argument after `--` was
incorrectly treated as a target selector.

For example, the following now works as expected while it failed before
thinking that `+RTS` is a target:

```
$ cabal run -- +RTS -s
$ cabal run -w /path/to/ghc -- +RTS -s
```
9 changes: 7 additions & 2 deletions doc/cabal-commands.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1212,8 +1212,9 @@ When ``TARGET`` is one of the following:
- Empty target: Same as package target, implicitly using the package from the current
working directory.

Except in the case of the empty target, the strings after it will be
passed to the executable as arguments.
With a non-empty target, the strings after it are passed to the
executable as arguments. With an empty target you must use ``--`` to
separate executable arguments from cabal flags.

If one of the arguments starts with ``-`` it will be interpreted as
a cabal flag, so if you need to pass flags to the executable you
Expand All @@ -1222,6 +1223,10 @@ have to separate them with ``--``.
::

$ cabal run target -- -a -bcd --argument
$ cabal run -- +RTS -s -RTS

The second form (empty target with ``--``) runs the single executable
in the current package and passes the RTS options to it.

``run`` supports running script files that use a certain format.
Scripts look like:
Expand Down
Loading