Skip to content

Set exe as default component for cabal run and cabal list-bin #7408

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Dec 1, 2021
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
43 changes: 30 additions & 13 deletions cabal-install/src/Distribution/Client/CmdListBin.hs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@
module Distribution.Client.CmdListBin (
listbinCommand,
listbinAction,

-- * Internals exposed for testing
selectPackageTargets,
selectComponentTarget,
noComponentsProblem,
matchesMultipleProblem,
multipleTargetsProblem,
componentNotRightKindProblem
) where

import Distribution.Client.Compat.Prelude
Expand Down Expand Up @@ -210,17 +218,21 @@ selectPackageTargets :: TargetSelector
-> [AvailableTarget k] -> Either ListBinTargetProblem [k]
selectPackageTargets targetSelector targets

-- If there is exactly one buildable executable then we select that
-- If there is a single executable component, select that. See #7403
| [target] <- targetsExesBuildable
= Right [target]

-- Otherwise, if there is a single executable-like component left, select that.
| [target] <- targetsExeLikesBuildable
= Right [target]

-- but fail if there are multiple buildable executables.
| not (null targetsExesBuildable)
= Left (matchesMultipleProblem targetSelector targetsExesBuildable')
| not (null targetsExeLikesBuildable)
= Left (matchesMultipleProblem targetSelector targetsExeLikesBuildable')

-- If there are executables but none are buildable then we report those
| not (null targetsExes)
= Left (TargetProblemNoneEnabled targetSelector targetsExes)
| not (null targetsExeLikes')
= Left (TargetProblemNoneEnabled targetSelector targetsExeLikes')

-- If there are no executables but some other targets then we report that
| not (null targets)
Expand All @@ -230,14 +242,19 @@ selectPackageTargets targetSelector targets
| otherwise
= Left (TargetProblemNoTargets targetSelector)
where
-- Targets that can be executed
targetsExecutableLike =
concatMap (\kind -> filterTargetsKind kind targets)
[ExeKind, TestKind, BenchKind]
(targetsExesBuildable,
targetsExesBuildable') = selectBuildableTargets' targetsExecutableLike

targetsExes = forgetTargetsDetail targetsExecutableLike
-- Targets that are precisely executables
targetsExes = filterTargetsKind ExeKind targets
targetsExesBuildable = selectBuildableTargets targetsExes

-- Any target that could be executed
targetsExeLikes = targetsExes
++ filterTargetsKind TestKind targets
++ filterTargetsKind BenchKind targets

(targetsExeLikesBuildable,
targetsExeLikesBuildable') = selectBuildableTargets' targetsExeLikes

targetsExeLikes' = forgetTargetsDetail targetsExeLikes


-- | For a 'TargetComponent' 'TargetSelector', check if the component can be
Expand Down
42 changes: 26 additions & 16 deletions cabal-install/src/Distribution/Client/CmdRun.hs
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,9 @@ runCommand = CommandUI

++ "Any executable-like component in any package in the project can be "
++ "specified. A package can be specified if contains just one "
++ "executable-like. The default is to use the package in the current "
++ "directory if it contains just one executable-like.\n\n"
++ "executable-like, preferring a single executable. The default is to "
++ "use the package in the current directory if it contains just one "
++ "executable-like.\n\n"

++ "Extra arguments can be passed to the program, but use '--' to "
++ "separate arguments for the program from arguments for " ++ pname
Expand Down Expand Up @@ -480,17 +481,21 @@ selectPackageTargets :: TargetSelector
-> [AvailableTarget k] -> Either RunTargetProblem [k]
selectPackageTargets targetSelector targets

-- If there is exactly one buildable executable then we select that
-- If there is a single executable component, select that. See #7403
| [target] <- targetsExesBuildable
= Right [target]

-- but fail if there are multiple buildable executables.
| not (null targetsExesBuildable)
= Left (matchesMultipleProblem targetSelector targetsExesBuildable')
-- Otherwise, if there is a single executable-like component left, select that.
| [target] <- targetsExeLikesBuildable
= Right [target]

-- but fail if there are multiple buildable executables.
| not (null targetsExeLikesBuildable)
= Left (matchesMultipleProblem targetSelector targetsExeLikesBuildable')

-- If there are executables but none are buildable then we report those
| not (null targetsExes)
= Left (TargetProblemNoneEnabled targetSelector targetsExes)
| not (null targetsExeLikes')
= Left (TargetProblemNoneEnabled targetSelector targetsExeLikes')

-- If there are no executables but some other targets then we report that
| not (null targets)
Expand All @@ -500,14 +505,19 @@ selectPackageTargets targetSelector targets
| otherwise
= Left (TargetProblemNoTargets targetSelector)
where
-- Targets that can be executed
targetsExecutableLike =
concatMap (\kind -> filterTargetsKind kind targets)
[ExeKind, TestKind, BenchKind]
(targetsExesBuildable,
targetsExesBuildable') = selectBuildableTargets' targetsExecutableLike

targetsExes = forgetTargetsDetail targetsExecutableLike
-- Targets that are precisely executables
targetsExes = filterTargetsKind ExeKind targets
targetsExesBuildable = selectBuildableTargets targetsExes

-- Any target that could be executed
targetsExeLikes = targetsExes
++ filterTargetsKind TestKind targets
++ filterTargetsKind BenchKind targets

(targetsExeLikesBuildable,
targetsExeLikesBuildable') = selectBuildableTargets' targetsExeLikes

targetsExeLikes' = forgetTargetsDetail targetsExeLikes


-- | For a 'TargetComponent' 'TargetSelector', check if the component can be
Expand Down
78 changes: 78 additions & 0 deletions cabal-install/tests/IntegrationTests2.hs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import qualified Distribution.Client.CmdRun as CmdRun
import qualified Distribution.Client.CmdTest as CmdTest
import qualified Distribution.Client.CmdBench as CmdBench
import qualified Distribution.Client.CmdHaddock as CmdHaddock
import qualified Distribution.Client.CmdListBin as CmdListBin

import Distribution.Package
import Distribution.PackageDescription
Expand Down Expand Up @@ -109,6 +110,7 @@ tests config =
, testCaseSteps "problems (build)" (testTargetProblemsBuild config)
, testCaseSteps "problems (repl)" (testTargetProblemsRepl config)
, testCaseSteps "problems (run)" (testTargetProblemsRun config)
, testCaseSteps "problems (list-bin)" (testTargetProblemsListBin config)
, testCaseSteps "problems (test)" (testTargetProblemsTest config)
, testCaseSteps "problems (bench)" (testTargetProblemsBench config)
, testCaseSteps "problems (haddock)" (testTargetProblemsHaddock config)
Expand Down Expand Up @@ -861,9 +863,85 @@ testTargetProblemsRepl config reportSubCase = do
[ TargetPackage TargetExplicitNamed ["p-0.1"] (Just BenchKind) ]
[ ("p-0.1-inplace-a-benchmark", CBenchName "a-benchmark") ]

testTargetProblemsListBin :: ProjectConfig -> (String -> IO ()) -> Assertion
testTargetProblemsListBin config reportSubCase = do
reportSubCase "one-of-each"
do (_,elaboratedPlan,_) <- planProject "targets/one-of-each" config
assertProjectDistinctTargets
elaboratedPlan
CmdListBin.selectPackageTargets
CmdListBin.selectComponentTarget
[ TargetPackage TargetExplicitNamed ["p-0.1"] Nothing
]
[ ("p-0.1-inplace-p1", CExeName "p1")
]

reportSubCase "multiple-exes"
assertProjectTargetProblems
"targets/multiple-exes" config
CmdListBin.selectPackageTargets
CmdListBin.selectComponentTarget
[ ( flip CmdListBin.matchesMultipleProblem
[ AvailableTarget "p-0.1" (CExeName "p2")
(TargetBuildable () TargetRequestedByDefault) True
, AvailableTarget "p-0.1" (CExeName "p1")
(TargetBuildable () TargetRequestedByDefault) True
]
, mkTargetPackage "p-0.1" )
]

reportSubCase "multiple targets"
do (_,elaboratedPlan,_) <- planProject "targets/multiple-exes" config
assertProjectDistinctTargets
elaboratedPlan
CmdListBin.selectPackageTargets
CmdListBin.selectComponentTarget
[ mkTargetComponent "p-0.1" (CExeName "p1")
, mkTargetComponent "p-0.1" (CExeName "p2")
]
[ ("p-0.1-inplace-p1", CExeName "p1")
, ("p-0.1-inplace-p2", CExeName "p2")
]

reportSubCase "exes-disabled"
assertProjectTargetProblems
"targets/exes-disabled" config
CmdListBin.selectPackageTargets
CmdListBin.selectComponentTarget
[ ( flip TargetProblemNoneEnabled
[ AvailableTarget "p-0.1" (CExeName "p") TargetNotBuildable True
]
, mkTargetPackage "p-0.1" )
]

reportSubCase "empty-pkg"
assertProjectTargetProblems
"targets/empty-pkg" config
CmdListBin.selectPackageTargets
CmdListBin.selectComponentTarget
[ ( TargetProblemNoTargets, mkTargetPackage "p-0.1" )
]

reportSubCase "lib-only"
assertProjectTargetProblems
"targets/lib-only" config
CmdListBin.selectPackageTargets
CmdListBin.selectComponentTarget
[ (CmdListBin.noComponentsProblem, mkTargetPackage "p-0.1" )
]

testTargetProblemsRun :: ProjectConfig -> (String -> IO ()) -> Assertion
testTargetProblemsRun config reportSubCase = do
reportSubCase "one-of-each"
do (_,elaboratedPlan,_) <- planProject "targets/one-of-each" config
assertProjectDistinctTargets
elaboratedPlan
CmdRun.selectPackageTargets
CmdRun.selectComponentTarget
[ TargetPackage TargetExplicitNamed ["p-0.1"] Nothing
]
[ ("p-0.1-inplace-p1", CExeName "p1")
]

reportSubCase "multiple-exes"
assertProjectTargetProblems
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
packages: ./
26 changes: 26 additions & 0 deletions cabal-install/tests/IntegrationTests2/targets/one-of-each/p.cabal
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: p
version: 0.1
build-type: Simple
cabal-version: >= 1.10

executable p1
main-is: P1.hs
build-depends: base

benchmark p2
type: exitcode-stdio-1.0
main-is: P2.hs
build-depends: base

test-suite p3
type: exitcode-stdio-1.0
main-is: P3.hs
build-depends: base

library p4
exposed-modules: P4
build-depends: base

foreign-library libp
type: native-shared
other-modules: FLib
13 changes: 13 additions & 0 deletions doc/cabal-commands.rst
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,19 @@ Tests and benchmarks are also treated as executables.

See `the v2-build section <#cabal-v2-build>`__ for the target syntax.

When ``TARGET`` is one of the following:

- A component target: execute the specified executable, benchmark or test suite

- A package target:
1. If the package has exactly one executable component, it will be selected.
2. If the package has multiple executable components, an error is raised.
3. If the package has exactly one test or benchmark component, it will be selected.
4. Otherwise an issue is raised

- 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.

Expand Down