1 {-# LANGUAGE LambdaCase #-}
2 {-# LANGUAGE NamedFieldPuns #-}
3 {-# LANGUAGE OverloadedStrings #-}
4 {-# LANGUAGE RecordWildCards #-}
5 {-# LANGUAGE TupleSections #-}
7 -- | cabal-install CLI command: run
8 module Distribution
.Client
.CmdRun
9 ( -- * The @run@ CLI and action
15 -- * Internals exposed for testing
16 , matchesMultipleProblem
18 , selectPackageTargets
19 , selectComponentTarget
22 import Distribution
.Client
.Compat
.Prelude
hiding (toList
)
25 import Data
.List
(group)
26 import qualified Data
.Set
as Set
27 import Distribution
.Client
.CmdErrorMessages
32 , renderTargetProblemNoTargets
33 , renderTargetSelector
35 , targetSelectorFilter
36 , targetSelectorPluralPkgs
38 import Distribution
.Client
.Errors
39 import Distribution
.Client
.GlobalFlags
42 import Distribution
.Client
.InstallPlan
46 import Distribution
.Client
.NixStyleOptions
48 , defaultNixStyleFlags
51 import Distribution
.Client
.ProjectOrchestration
52 import Distribution
.Client
.ProjectPlanning
53 ( ElaboratedConfiguredPackage
(..)
54 , ElaboratedInstallPlan
57 import Distribution
.Client
.ProjectPlanning
.Types
58 ( dataDirsEnvironmentForPlan
60 import Distribution
.Client
.ScriptUtils
61 ( AcceptNoTargets
(..)
64 , updateContextAndWriteProjectFile
65 , withContextAndSelectors
67 import Distribution
.Client
.Setup
71 import Distribution
.Client
.TargetProblem
74 import Distribution
.Client
.Utils
78 import Distribution
.Simple
.Command
82 import Distribution
.Simple
.Flag
85 import Distribution
.Simple
.Program
.Run
86 ( ProgramInvocation
(..)
87 , emptyProgramInvocation
88 , runProgramInvocation
90 import Distribution
.Simple
.Utils
98 import Distribution
.Types
.ComponentName
101 import Distribution
.Types
.UnitId
104 import Distribution
.Types
.UnqualComponentName
105 ( UnqualComponentName
106 , unUnqualComponentName
108 import Distribution
.Verbosity
112 import GHC
.Environment
115 import System
.Directory
118 import System
.FilePath
124 runCommand
:: CommandUI
(NixStyleFlags
())
127 { commandName
= "v2-run"
128 , commandSynopsis
= "Run an executable."
132 ["[TARGET] [FLAGS] [-- EXECUTABLE_FLAGS]"]
133 , commandDescription
= Just
$ \pname
->
135 "Runs the specified executable-like component (an executable, a test, "
136 ++ "or a benchmark), first ensuring it is up to date.\n\n"
137 ++ "Any executable-like component in any package in the project can be "
138 ++ "specified. A package can be specified if contains just one "
139 ++ "executable-like, preferring a single executable. The default is to "
140 ++ "use the package in the current directory if it contains just one "
141 ++ "executable-like.\n\n"
142 ++ "Extra arguments can be passed to the program, but use '--' to "
143 ++ "separate arguments for the program from arguments for "
145 ++ ". The executable is run in an environment where it can find its "
146 ++ "data files inplace in the build tree.\n\n"
147 ++ "Dependencies are built or rebuilt as necessary. Additional "
148 ++ "configuration flags can be specified on the command line and these "
149 ++ "extend the project configuration from the 'cabal.project', "
150 ++ "'cabal.project.local' and other files."
151 , commandNotes
= Just
$ \pname
->
156 ++ " Run the executable-like in the package in the current directory\n"
159 ++ " v2-run foo-tool\n"
160 ++ " Run the named executable-like (in any package in the project)\n"
163 ++ " v2-run pkgfoo:foo-tool\n"
164 ++ " Run the executable-like 'foo-tool' in the package 'pkgfoo'\n"
167 ++ " v2-run foo -O2 -- dothing --fooflag\n"
168 ++ " Build with '-O2' and run the program, passing it extra arguments.\n"
169 , commandDefaultFlags
= defaultNixStyleFlags
()
170 , commandOptions
= nixStyleOptions
(const [])
173 -- | The @run@ command runs a specified executable-like component, building it
174 -- first if necessary. The component can be either an executable, a test,
175 -- or a benchmark. This is particularly useful for passing arguments to
176 -- exes/tests/benchs by simply appending them after a @--@.
178 -- For more details on how this works, see the module
179 -- "Distribution.Client.ProjectOrchestration"
180 runAction
:: NixStyleFlags
() -> [String] -> GlobalFlags
-> IO ()
181 runAction flags
@NixStyleFlags
{..} targetAndArgs globalFlags
=
182 withContextAndSelectors RejectNoTargets
(Just ExeKind
) flags targetStr globalFlags OtherCommand
$ \targetCtx ctx targetSelectors
-> do
183 (baseCtx
, defaultVerbosity
) <- case targetCtx
of
184 ProjectContext
-> return (ctx
, normal
)
185 GlobalContext
-> return (ctx
, normal
)
186 ScriptContext path exemeta
-> (,silent
) <$> updateContextAndWriteProjectFile ctx path exemeta
188 let verbosity
= fromFlagOrDefault defaultVerbosity
(configVerbosity configFlags
)
191 runProjectPreBuildPhase verbosity baseCtx
$ \elaboratedPlan
-> do
192 when (buildSettingOnlyDeps
(buildSettings baseCtx
)) $
193 dieWithException verbosity NoSupportForRunCommand
195 fullArgs
<- getFullArgs
196 when (occursOnlyOrBefore fullArgs
"+RTS" "--") $
200 -- Interpret the targets on the command line as build targets
201 -- (as opposed to say repl or haddock targets).
203 either (reportTargetProblems verbosity
) return $
206 selectComponentTarget
211 -- Reject multiple targets, or at least targets in different
212 -- components. It is ok to have two module/file targets in the
213 -- same component, but not two that live in different components.
215 -- Note that we discard the target and return the whole 'TargetsMap',
216 -- so this check will be repeated (and must succeed) after
217 -- the 'runProjectPreBuildPhase'. Keep it in mind when modifying this.
220 ( reportTargetProblems
222 [multipleTargetsProblem targets
]
226 let elaboratedPlan
' =
227 pruneInstallPlanToTargets
231 return (elaboratedPlan
', targets
)
233 (selectedUnitId
, selectedComponent
) <-
234 -- Slight duplication with 'runProjectPreBuildPhase'.
236 ( dieWithException verbosity RunPhaseReached
238 $ targetsMap buildCtx
240 printPlan verbosity baseCtx buildCtx
242 buildOutcomes
<- runProjectBuildPhase verbosity baseCtx buildCtx
243 runProjectPostBuildPhase verbosity baseCtx buildCtx buildOutcomes
245 let elaboratedPlan
= elaboratedPlanToExecute buildCtx
246 matchingElaboratedConfiguredPackages
=
247 matchingPackagesByUnitId
251 let exeName
= unUnqualComponentName selectedComponent
253 -- In the common case, we expect @matchingElaboratedConfiguredPackages@
254 -- to consist of a single element that provides a single way of building
255 -- an appropriately-named executable. In that case we take that
256 -- package and continue.
258 -- However, multiple packages/components could provide that
259 -- executable, or it's possible we don't find the executable anywhere
260 -- in the build plan. I suppose in principle it's also possible that
261 -- a single package provides an executable in two different ways,
262 -- though that's probably a bug if. Anyway it's a good lint to report
263 -- an error in all of these cases, even if some seem like they
265 pkg
<- case matchingElaboratedConfiguredPackages
of
266 [] -> dieWithException verbosity
$ UnknownExecutable exeName selectedUnitId
270 ++ prettyShow selectedUnitId
275 dieWithException verbosity
$
276 MultipleMatchingExecutables exeName
(fmap (\p
-> " - in package " ++ prettyShow
(elabUnitId p
)) elabPkgs
)
280 (distDirLayout baseCtx
)
281 (elaboratedShared buildCtx
)
285 exePath
= fromMaybe defaultExePath
(movedExePath selectedComponent
(distDirLayout baseCtx
) (elaboratedShared buildCtx
) pkg
)
288 buildSettingDryRun
(buildSettings baseCtx
)
289 || buildSettingOnlyDownload
(buildSettings baseCtx
)
292 then notice verbosity
"Running of executable suppressed by flag(s)"
296 emptyProgramInvocation
297 { progInvokePath
= exePath
298 , progInvokeArgs
= args
300 dataDirsEnvironmentForPlan
301 (distDirLayout baseCtx
)
305 (targetStr
, args
) = splitAt 1 targetAndArgs
307 -- | Used by the main CLI parser as heuristic to decide whether @cabal@ was
308 -- invoked as a script interpreter, i.e. via
310 -- > #! /usr/bin/env cabal
314 -- > #! /usr/bin/cabal
316 -- As the first argument passed to `cabal` will be a filepath to the
317 -- script to be interpreted.
319 -- See also 'handleShebang'
320 validScript
:: String -> IO Bool
322 | isValid script
&& any isPathSeparator script
= doesFileExist script
323 |
otherwise = return False
325 -- | Handle @cabal@ invoked as script interpreter, see also 'validScript'
327 -- First argument is the 'FilePath' to the script to be executed; second
328 -- argument is a list of arguments to be passed to the script.
329 handleShebang
:: FilePath -> [String] -> IO ()
330 handleShebang script args
=
331 runAction
(commandDefaultFlags runCommand
) (script
: args
) defaultGlobalFlags
333 singleExeOrElse
:: IO (UnitId
, UnqualComponentName
) -> TargetsMap
-> IO (UnitId
, UnqualComponentName
)
334 singleExeOrElse action targetsMap
=
335 case Set
.toList
. distinctTargetComponents
$ targetsMap
of
336 [(unitId
, CExeName component
)] -> return (unitId
, component
)
337 [(unitId
, CTestName component
)] -> return (unitId
, component
)
338 [(unitId
, CBenchName component
)] -> return (unitId
, component
)
341 -- | Filter the 'ElaboratedInstallPlan' keeping only the
342 -- 'ElaboratedConfiguredPackage's that match the specified
344 matchingPackagesByUnitId
346 -> ElaboratedInstallPlan
347 -> [ElaboratedConfiguredPackage
]
348 matchingPackagesByUnitId uid
=
354 if elabUnitId x
== uid
361 -- | This defines what a 'TargetSelector' means for the @run@ command.
362 -- It selects the 'AvailableTarget's that the 'TargetSelector' refers to,
363 -- or otherwise classifies the problem.
365 -- For the @run@ command we select the exe if there is only one and it's
366 -- buildable. Fail if there are no or multiple buildable exe components.
369 -> [AvailableTarget k
]
370 -> Either RunTargetProblem
[k
]
371 selectPackageTargets targetSelector targets
372 -- If there is a single executable component, select that. See #7403
373 |
[target
] <- targetsExesBuildable
=
375 -- Otherwise, if there is a single executable-like component left, select that.
376 |
[target
] <- targetsExeLikesBuildable
=
378 -- but fail if there are multiple buildable executables.
379 |
not (null targetsExeLikesBuildable
) =
380 Left
(matchesMultipleProblem targetSelector targetsExeLikesBuildable
')
381 -- If there are executables but none are buildable then we report those
382 |
not (null targetsExeLikes
') =
383 Left
(TargetProblemNoneEnabled targetSelector targetsExeLikes
')
384 -- If there are no executables but some other targets then we report that
385 |
not (null targets
) =
386 Left
(noExesProblem targetSelector
)
387 -- If there are no targets at all then we report that
389 Left
(TargetProblemNoTargets targetSelector
)
391 -- Targets that are precisely executables
392 targetsExes
= filterTargetsKind ExeKind targets
393 targetsExesBuildable
= selectBuildableTargets targetsExes
395 -- Any target that could be executed
398 ++ filterTargetsKind TestKind targets
399 ++ filterTargetsKind BenchKind targets
401 ( targetsExeLikesBuildable
402 , targetsExeLikesBuildable
'
403 ) = selectBuildableTargets
' targetsExeLikes
405 targetsExeLikes
' = forgetTargetsDetail targetsExeLikes
407 -- | For a 'TargetComponent' 'TargetSelector', check if the component can be
410 -- For the @run@ command we just need to check it is a executable-like
411 -- (an executable, a test, or a benchmark), in addition
412 -- to the basic checks on being buildable etc.
413 selectComponentTarget
414 :: SubComponentTarget
416 -> Either RunTargetProblem k
417 selectComponentTarget subtarget
@WholeComponent t
=
418 case availableTargetComponentName t
of
419 CExeName _
-> component
420 CTestName _
-> component
421 CBenchName _
-> component
422 _
-> Left
(componentNotExeProblem pkgid cname
)
424 pkgid
= availableTargetPackageId t
425 cname
= availableTargetComponentName t
426 component
= selectComponentTargetBasic subtarget t
427 selectComponentTarget subtarget t
=
429 ( isSubComponentProblem
430 (availableTargetPackageId t
)
431 (availableTargetComponentName t
)
435 -- | The various error conditions that can occur when matching a
436 -- 'TargetSelector' against 'AvailableTarget's for the @run@ command.
438 = -- | The 'TargetSelector' matches targets but no executables
439 TargetProblemNoExes TargetSelector
440 |
-- | A single 'TargetSelector' matches multiple targets
441 TargetProblemMatchesMultiple TargetSelector
[AvailableTarget
()]
442 |
-- | Multiple 'TargetSelector's match multiple targets
443 TargetProblemMultipleTargets TargetsMap
444 |
-- | The 'TargetSelector' refers to a component that is not an executable
445 TargetProblemComponentNotExe PackageId ComponentName
446 |
-- | Asking to run an individual file or module is not supported
447 TargetProblemIsSubComponent PackageId ComponentName SubComponentTarget
450 type RunTargetProblem
= TargetProblem RunProblem
452 noExesProblem
:: TargetSelector
-> RunTargetProblem
453 noExesProblem
= CustomTargetProblem
. TargetProblemNoExes
455 matchesMultipleProblem
:: TargetSelector
-> [AvailableTarget
()] -> RunTargetProblem
456 matchesMultipleProblem selector targets
=
457 CustomTargetProblem
$
458 TargetProblemMatchesMultiple selector targets
460 multipleTargetsProblem
:: TargetsMap
-> TargetProblem RunProblem
461 multipleTargetsProblem
= CustomTargetProblem
. TargetProblemMultipleTargets
463 componentNotExeProblem
:: PackageId
-> ComponentName
-> TargetProblem RunProblem
464 componentNotExeProblem pkgid name
=
465 CustomTargetProblem
$
466 TargetProblemComponentNotExe pkgid name
468 isSubComponentProblem
471 -> SubComponentTarget
472 -> TargetProblem RunProblem
473 isSubComponentProblem pkgid name subcomponent
=
474 CustomTargetProblem
$
475 TargetProblemIsSubComponent pkgid name subcomponent
477 reportTargetProblems
:: Verbosity
-> [RunTargetProblem
] -> IO a
478 reportTargetProblems verbosity
=
479 dieWithException verbosity
. CmdRunReportTargetProblems
. unlines . map renderRunTargetProblem
481 renderRunTargetProblem
:: RunTargetProblem
-> String
482 renderRunTargetProblem
(TargetProblemNoTargets targetSelector
) =
483 case targetSelectorFilter targetSelector
of
486 "The run command is for running executables, but the target '"
487 ++ showTargetSelector targetSelector
489 ++ renderTargetSelector targetSelector
491 _
-> renderTargetProblemNoTargets
"run" targetSelector
492 renderRunTargetProblem problem
=
493 renderTargetProblem
"run" renderRunProblem problem
495 renderRunProblem
:: RunProblem
-> String
496 renderRunProblem
(TargetProblemMatchesMultiple targetSelector targets
) =
497 "The run command is for running a single executable at once. The target '"
498 ++ showTargetSelector targetSelector
500 ++ renderTargetSelector targetSelector
501 ++ " which includes \n"
503 ( (\(label
, xs
) -> "- " ++ label
++ ": " ++ renderListPretty xs
)
505 ["executables", "test-suites", "benchmarks"]
506 ( filter (not . null) . map removeDuplicates
$
507 map (componentNameRaw
. availableTargetComponentName
)
508 <$> (flip filterTargetsKind
$ targets
)
509 <$> [ExeKind
, TestKind
, BenchKind
]
513 removeDuplicates
= catMaybes . map safeHead
. group . sort
514 renderRunProblem
(TargetProblemMultipleTargets selectorMap
) =
515 "The run command is for running a single executable at once. The targets "
516 ++ renderListCommaAnd
517 [ "'" ++ showTargetSelector ts
++ "'"
518 | ts
<- uniqueTargetSelectors selectorMap
520 ++ " refer to different executables."
521 renderRunProblem
(TargetProblemComponentNotExe pkgid cname
) =
522 "The run command is for running executables, but the target '"
523 ++ showTargetSelector targetSelector
525 ++ renderTargetSelector targetSelector
526 ++ " from the package "
530 targetSelector
= TargetComponent pkgid cname WholeComponent
531 renderRunProblem
(TargetProblemIsSubComponent pkgid cname subtarget
) =
532 "The run command can only run an executable as a whole, "
533 ++ "not files or modules within them, but the target '"
534 ++ showTargetSelector targetSelector
536 ++ renderTargetSelector targetSelector
539 targetSelector
= TargetComponent pkgid cname subtarget
540 renderRunProblem
(TargetProblemNoExes targetSelector
) =
541 "Cannot run the target '"
542 ++ showTargetSelector targetSelector
543 ++ "' which refers to "
544 ++ renderTargetSelector targetSelector
546 ++ plural
(targetSelectorPluralPkgs targetSelector
) "it does" "they do"
547 ++ " not contain any executables."