Skip to content
Open
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
1 change: 1 addition & 0 deletions cabal-install/cabal-install.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,7 @@ test-suite unit-tests
other-modules:
UnitTests.Distribution.Client.ArbitraryInstances
UnitTests.Distribution.Client.BuildReport
UnitTests.Distribution.Client.CmdRun
UnitTests.Distribution.Client.Configure
UnitTests.Distribution.Client.FetchUtils
UnitTests.Distribution.Client.Get
Expand Down
28 changes: 28 additions & 0 deletions cabal-install/src/Distribution/Client/CmdRun.hs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ module Distribution.Client.CmdRun
, noExesProblem
, selectPackageTargets
, selectComponentTarget
, RunProblem (..)
, renderRunProblem
) where

import Distribution.Client.Compat.Prelude hiding (toList)
Expand Down Expand Up @@ -78,6 +80,10 @@ import Distribution.Client.Utils
, occursOnlyOrBefore
)

import Distribution.Package
( packageName
)

import Distribution.Simple.BuildToolDepends
( getAllInternalToolDependencies
)
Expand Down Expand Up @@ -612,6 +618,7 @@ renderRunProblem (TargetProblemMatchesMultiple targetSelector targets) =
(map (componentNameRaw . availableTargetComponentName) . (`filterTargetsKind` targets) <$> [ExeKind, TestKind, BenchKind])
)
)
++ renderRunProblemMatchesMultipleHint targets
renderRunProblem (TargetProblemMultipleTargets selectorMap) =
"The run command is for running a single executable at once. The targets "
++ renderListCommaAnd
Expand Down Expand Up @@ -646,3 +653,24 @@ renderRunProblem (TargetProblemNoExes targetSelector) =
++ " because "
++ plural (targetSelectorPluralPkgs targetSelector) "it does" "they do"
++ " not contain any executables."

-- | Render a hint suggesting how to disambiguate which executable to run when
-- a target matches multiple executable-like components.
renderRunProblemMatchesMultipleHint :: [AvailableTarget ()] -> String
renderRunProblemMatchesMultipleHint targets =
case exes of
[] -> ""
_ ->
"\nYou need to specify which executable cabal should use. Try one of those:\n"
++ unlines
[ "- cabal run " ++ prettyShow pkgname ++ ":" ++ exe
| (pkgname, exe) <- exes
]
where
exes =
sortNub
[ ( packageName (availableTargetPackageId target)
, componentNameRaw (availableTargetComponentName target)
)
| target <- filterTargetsKind ExeKind targets
]
4 changes: 4 additions & 0 deletions cabal-install/tests/UnitTests.hs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module Main (main) where
import Test.Tasty

import qualified UnitTests.Distribution.Client.BuildReport
import qualified UnitTests.Distribution.Client.CmdRun
import qualified UnitTests.Distribution.Client.Configure
import qualified UnitTests.Distribution.Client.FetchUtils
import qualified UnitTests.Distribution.Client.GZipUtils
Expand Down Expand Up @@ -36,6 +37,9 @@ main = do
[ testGroup
"UnitTests.Distribution.Client.BuildReport"
UnitTests.Distribution.Client.BuildReport.tests
, testGroup
"UnitTests.Distribution.Client.CmdRun"
UnitTests.Distribution.Client.CmdRun.tests
, testGroup
"UnitTests.Distribution.Client.Configure"
UnitTests.Distribution.Client.Configure.tests
Expand Down
109 changes: 109 additions & 0 deletions cabal-install/tests/UnitTests/Distribution/Client/CmdRun.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
module UnitTests.Distribution.Client.CmdRun
( tests
) where

import Distribution.Client.CmdRun
( RunProblem (..)
, renderRunProblem
)
import Distribution.Client.ProjectPlanning
( AvailableTarget (..)
, AvailableTargetStatus (..)
, TargetRequested (..)
)
import Distribution.Client.TargetSelector
( TargetSelector (..)
)

import Distribution.Package (PackageIdentifier (..), PackageName, mkPackageName)
import Distribution.Types.ComponentName (ComponentName (..))
import Distribution.Types.UnqualComponentName (mkUnqualComponentName)
import Distribution.Version (mkVersion)

import Data.List (isInfixOf)

import Test.Tasty
import Test.Tasty.HUnit

tests :: [TestTree]
tests =
[ testCase "hints at each executable when there are multiple" testMultipleExecutablesHint
, testCase "hint lists only executables" testHintListsOnlyExecutables
, testCase "no hint when there are no executables" testNoHintWithoutExecutables
]

pkgname :: PackageName
pkgname = mkPackageName "agent-cli"

pkgid :: PackageIdentifier
pkgid = PackageIdentifier pkgname (mkVersion [0, 1, 0, 0])

mkTarget :: ComponentName -> AvailableTarget ()
mkTarget cname =
AvailableTarget
{ availableTargetPackageId = pkgid
, availableTargetComponentName = cname
, availableTargetStatus = TargetBuildable () TargetRequestedByDefault
, availableTargetLocalToProject = True
}

exe :: String -> AvailableTarget ()
exe = mkTarget . CExeName . mkUnqualComponentName

test :: String -> AvailableTarget ()
test = mkTarget . CTestName . mkUnqualComponentName

bench :: String -> AvailableTarget ()
bench = mkTarget . CBenchName . mkUnqualComponentName

selector :: TargetSelector
selector = TargetPackageNamed pkgname Nothing

testMultipleExecutablesHint :: Assertion
testMultipleExecutablesHint = do
let rendered =
renderRunProblem $
TargetProblemMatchesMultiple
selector
[ exe "agent-cli"
, exe "agent-telegram"
, exe "eval-ghci-vs-bash"
]
assertBool "hint header is present" $
"You need to specify which executable cabal should use. Try one of those:"
`isInfixOf` rendered
mapM_ (assertSuggestion rendered) ["agent-cli", "agent-telegram", "eval-ghci-vs-bash"]

testHintListsOnlyExecutables :: Assertion
testHintListsOnlyExecutables = do
let rendered =
renderRunProblem $
TargetProblemMatchesMultiple
selector
[ exe "agent-cli"
, exe "agent-telegram"
, test "agent-cli-test"
, bench "image-preview-latency-bench"
]
assertBool "test suite is not suggested" $
not ("- cabal run agent-cli:agent-cli-test" `isInfixOf` rendered)
assertBool "benchmark is not suggested" $
not ("- cabal run agent-cli:image-preview-latency-bench" `isInfixOf` rendered)
mapM_ (assertSuggestion rendered) ["agent-cli", "agent-telegram"]

testNoHintWithoutExecutables :: Assertion
testNoHintWithoutExecutables = do
let rendered =
renderRunProblem $
TargetProblemMatchesMultiple
selector
[ test "agent-cli-test"
, bench "image-preview-latency-bench"
]
assertBool "no hint when there are no executables" $
not ("You need to specify which executable" `isInfixOf` rendered)

assertSuggestion :: String -> String -> Assertion
assertSuggestion rendered exeName =
assertBool ("suggestion for executable " ++ exeName) $
("- cabal run agent-cli:" ++ exeName) `isInfixOf` rendered
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,16 @@ Error: [Cabal-7070]
The run command is for running a single executable at once. The target '' refers to the package MultipleExes-1.0 which includes
- executables: bar and foo

You need to specify which executable cabal should use. Try one of those:
- cabal run MultipleExes:bar
- cabal run MultipleExes:foo

# cabal v2-run
Error: [Cabal-7070]
The run command is for running a single executable at once. The target 'MultipleExes' refers to the package MultipleExes-1.0 which includes
- executables: bar and foo

You need to specify which executable cabal should use. Try one of those:
- cabal run MultipleExes:bar
- cabal run MultipleExes:foo

Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ Error: [Cabal-7070]
The run command is for running a single executable at once. The target 'bar' refers to the package bar-1.0 which includes
- executables: bar-exe and foo-exe

You need to specify which executable cabal should use. Try one of those:
- cabal run bar:bar-exe
- cabal run bar:foo-exe

# cabal v2-run
Error: [Cabal-7132]
Ambiguous target 'foo-exe'. It could be:
Expand Down
25 changes: 25 additions & 0 deletions changelog.d/12266.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
synopsis: Suggest a concrete target when `cabal run` matches multiple executables
packages: [cabal-install]
prs: 12266
issues: 12262
---

The `cabal run` command previously failed with an error that listed the
components of a package but did not explain how to pick one:

```diff
Error: [Cabal-7070]
The run command is for running a single executable at once. The target 'agent-cli' refers to the package agent-cli-0.1.0.0 which includes
- executables: agent-cli, agent-telegram and eval-ghci-vs-bash
- test-suites: agent-cli-test
- benchmarks: image-preview-latency-bench and subagent-retention-bench
+
+ You need to specify which executable cabal should use. Try one of those:
+ - cabal run agent-cli:agent-cli
+ - cabal run agent-cli:agent-telegram
+ - cabal run agent-cli:eval-ghci-vs-bash
```

The hint lists the executables of the target package and is only shown when
there is more than one executable to choose from.
Loading