1 {-# LANGUAGE RecordWildCards #-}
3 -- | cabal-install CLI command: bench
5 module Distribution
.Client
.CmdBench
(
6 -- * The @bench@ CLI and action
10 -- * Internals exposed for testing
11 componentNotBenchmarkProblem
,
12 isSubComponentProblem
,
18 import Distribution
.Client
.Compat
.Prelude
21 import Distribution
.Client
.ProjectOrchestration
22 import Distribution
.Client
.CmdErrorMessages
23 ( renderTargetSelector
, showTargetSelector
, renderTargetProblem
,
24 renderTargetProblemNoTargets
, plural
, targetSelectorPluralPkgs
,
25 targetSelectorFilter
)
26 import Distribution
.Client
.TargetProblem
27 ( TargetProblem
(..) )
28 import Distribution
.Client
.NixStyleOptions
29 ( NixStyleFlags
(..), nixStyleOptions
, defaultNixStyleFlags
)
30 import Distribution
.Client
.Setup
31 ( GlobalFlags
, ConfigFlags
(..) )
32 import Distribution
.Simple
.Flag
34 import Distribution
.Simple
.Command
35 ( CommandUI
(..), usageAlternatives
)
36 import Distribution
.Verbosity
38 import Distribution
.Simple
.Utils
41 benchCommand
:: CommandUI
(NixStyleFlags
())
42 benchCommand
= CommandUI
{
43 commandName
= "v2-bench",
44 commandSynopsis
= "Run benchmarks.",
45 commandUsage
= usageAlternatives
"v2-bench" [ "[TARGETS] [FLAGS]" ],
46 commandDescription
= Just
$ \_
-> wrapText
$
47 "Runs the specified benchmarks, first ensuring they are up to "
50 ++ "Any benchmark in any package in the project can be specified. "
51 ++ "A package can be specified in which case all the benchmarks in the "
52 ++ "package are run. The default is to run all the benchmarks in the "
53 ++ "package in the current directory.\n\n"
55 ++ "Dependencies are built or rebuilt as necessary. Additional "
56 ++ "configuration flags can be specified on the command line and these "
57 ++ "extend the project configuration from the 'cabal.project', "
58 ++ "'cabal.project.local' and other files.",
59 commandNotes
= Just
$ \pname
->
61 ++ " " ++ pname
++ " v2-bench\n"
62 ++ " Run all the benchmarks in the package in the current directory\n"
63 ++ " " ++ pname
++ " v2-bench pkgname\n"
64 ++ " Run all the benchmarks in the package named pkgname\n"
65 ++ " " ++ pname
++ " v2-bench cname\n"
66 ++ " Run the benchmark named cname\n"
67 ++ " " ++ pname
++ " v2-bench cname -O2\n"
68 ++ " Run the benchmark built with '-O2' (including local libs used)\n"
70 , commandDefaultFlags
= defaultNixStyleFlags
()
71 , commandOptions
= nixStyleOptions
(const [])
75 -- | The @build@ command does a lot. It brings the install plan up to date,
76 -- selects that part of the plan needed by the given or implicit targets and
77 -- then executes the plan.
79 -- For more details on how this works, see the module
80 -- "Distribution.Client.ProjectOrchestration"
82 benchAction
:: NixStyleFlags
() -> [String] -> GlobalFlags
-> IO ()
83 benchAction flags
@NixStyleFlags
{..} targetStrings globalFlags
= do
85 baseCtx
<- establishProjectBaseContext verbosity cliConfig OtherCommand
87 targetSelectors
<- either (reportTargetSelectorProblems verbosity
) return
88 =<< readTargetSelectors
(localPackages baseCtx
) (Just BenchKind
) targetStrings
91 runProjectPreBuildPhase verbosity baseCtx
$ \elaboratedPlan
-> do
93 when (buildSettingOnlyDeps
(buildSettings baseCtx
)) $
95 "The bench command does not support '--only-dependencies'. "
96 ++ "You may wish to use 'build --only-dependencies' and then "
99 -- Interpret the targets on the command line as bench targets
100 -- (as opposed to say build or haddock targets).
101 targets
<- either (reportTargetProblems verbosity
) return
104 selectComponentTarget
109 let elaboratedPlan
' = pruneInstallPlanToTargets
113 return (elaboratedPlan
', targets
)
115 printPlan verbosity baseCtx buildCtx
117 buildOutcomes
<- runProjectBuildPhase verbosity baseCtx buildCtx
118 runProjectPostBuildPhase verbosity baseCtx buildCtx buildOutcomes
120 verbosity
= fromFlagOrDefault normal
(configVerbosity configFlags
)
121 cliConfig
= commandLineFlagsToProjectConfig globalFlags flags
122 mempty
-- ClientInstallFlags, not needed here
124 -- | This defines what a 'TargetSelector' means for the @bench@ command.
125 -- It selects the 'AvailableTarget's that the 'TargetSelector' refers to,
126 -- or otherwise classifies the problem.
128 -- For the @bench@ command we select all buildable benchmarks,
129 -- or fail if there are no benchmarks or no buildable benchmarks.
131 selectPackageTargets
:: TargetSelector
132 -> [AvailableTarget k
] -> Either BenchTargetProblem
[k
]
133 selectPackageTargets targetSelector targets
135 -- If there are any buildable benchmark targets then we select those
136 |
not (null targetsBenchBuildable
)
137 = Right targetsBenchBuildable
139 -- If there are benchmarks but none are buildable then we report those
140 |
not (null targetsBench
)
141 = Left
(TargetProblemNoneEnabled targetSelector targetsBench
)
143 -- If there are no benchmarks but some other targets then we report that
145 = Left
(noBenchmarksProblem targetSelector
)
147 -- If there are no targets at all then we report that
149 = Left
(TargetProblemNoTargets targetSelector
)
151 targetsBenchBuildable
= selectBuildableTargets
152 . filterTargetsKind BenchKind
155 targetsBench
= forgetTargetsDetail
156 . filterTargetsKind BenchKind
160 -- | For a 'TargetComponent' 'TargetSelector', check if the component can be
163 -- For the @bench@ command we just need to check it is a benchmark, in addition
164 -- to the basic checks on being buildable etc.
166 selectComponentTarget
:: SubComponentTarget
167 -> AvailableTarget k
-> Either BenchTargetProblem k
168 selectComponentTarget subtarget
@WholeComponent t
169 | CBenchName _
<- availableTargetComponentName t
170 = selectComponentTargetBasic subtarget t
172 = Left
(componentNotBenchmarkProblem
173 (availableTargetPackageId t
)
174 (availableTargetComponentName t
))
176 selectComponentTarget subtarget t
177 = Left
(isSubComponentProblem
178 (availableTargetPackageId t
)
179 (availableTargetComponentName t
)
182 -- | The various error conditions that can occur when matching a
183 -- 'TargetSelector' against 'AvailableTarget's for the @bench@ command.
186 -- | The 'TargetSelector' matches targets but no benchmarks
187 TargetProblemNoBenchmarks TargetSelector
189 -- | The 'TargetSelector' refers to a component that is not a benchmark
190 | TargetProblemComponentNotBenchmark PackageId ComponentName
192 -- | Asking to benchmark an individual file or module is not supported
193 | TargetProblemIsSubComponent PackageId ComponentName SubComponentTarget
197 type BenchTargetProblem
= TargetProblem BenchProblem
199 noBenchmarksProblem
:: TargetSelector
-> TargetProblem BenchProblem
200 noBenchmarksProblem
= CustomTargetProblem
. TargetProblemNoBenchmarks
202 componentNotBenchmarkProblem
:: PackageId
-> ComponentName
-> TargetProblem BenchProblem
203 componentNotBenchmarkProblem pkgid name
= CustomTargetProblem
$
204 TargetProblemComponentNotBenchmark pkgid name
206 isSubComponentProblem
209 -> SubComponentTarget
210 -> TargetProblem BenchProblem
211 isSubComponentProblem pkgid name subcomponent
= CustomTargetProblem
$
212 TargetProblemIsSubComponent pkgid name subcomponent
214 reportTargetProblems
:: Verbosity
-> [BenchTargetProblem
] -> IO a
215 reportTargetProblems verbosity
=
216 die
' verbosity
. unlines . map renderBenchTargetProblem
218 renderBenchTargetProblem
:: BenchTargetProblem
-> String
219 renderBenchTargetProblem
(TargetProblemNoTargets targetSelector
) =
220 case targetSelectorFilter targetSelector
of
221 Just kind | kind
/= BenchKind
222 -> "The bench command is for running benchmarks, but the target '"
223 ++ showTargetSelector targetSelector
++ "' refers to "
224 ++ renderTargetSelector targetSelector
++ "."
226 _
-> renderTargetProblemNoTargets
"benchmark" targetSelector
227 renderBenchTargetProblem problem
=
228 renderTargetProblem
"benchmark" renderBenchProblem problem
230 renderBenchProblem
:: BenchProblem
-> String
231 renderBenchProblem
(TargetProblemNoBenchmarks targetSelector
) =
232 "Cannot run benchmarks for the target '" ++ showTargetSelector targetSelector
233 ++ "' which refers to " ++ renderTargetSelector targetSelector
235 ++ plural
(targetSelectorPluralPkgs targetSelector
) "it does" "they do"
236 ++ " not contain any benchmarks."
238 renderBenchProblem
(TargetProblemComponentNotBenchmark pkgid cname
) =
239 "The bench command is for running benchmarks, but the target '"
240 ++ showTargetSelector targetSelector
++ "' refers to "
241 ++ renderTargetSelector targetSelector
++ " from the package "
242 ++ prettyShow pkgid
++ "."
244 targetSelector
= TargetComponent pkgid cname WholeComponent
246 renderBenchProblem
(TargetProblemIsSubComponent pkgid cname subtarget
) =
247 "The bench command can only run benchmarks as a whole, "
248 ++ "not files or modules within them, but the target '"
249 ++ showTargetSelector targetSelector
++ "' refers to "
250 ++ renderTargetSelector targetSelector
++ "."
252 targetSelector
= TargetComponent pkgid cname subtarget