1 {-# LANGUAGE DuplicateRecordFields #-}
2 {-# LANGUAGE FlexibleContexts #-}
3 {-# LANGUAGE NamedFieldPuns #-}
4 {-# LANGUAGE RankNTypes #-}
6 -----------------------------------------------------------------------------
9 -- Module : Distribution.Simple.Bench
10 -- Copyright : Johan Tibell 2011
13 -- Maintainer : cabal-devel@haskell.org
14 -- Portability : portable
16 -- This is the entry point into running the benchmarks in a built
17 -- package. It performs the \"@.\/setup bench@\" action. It runs
18 -- benchmarks designated in the package description.
19 module Distribution
.Simple
.Bench
23 import Distribution
.Compat
.Prelude
26 import Distribution
.Compat
.Environment
27 import qualified Distribution
.PackageDescription
as PD
28 import Distribution
.Pretty
29 import Distribution
.Simple
.Build
(addInternalBuildTools
)
30 import Distribution
.Simple
.BuildPaths
31 import Distribution
.Simple
.Compiler
32 import Distribution
.Simple
.Errors
33 import Distribution
.Simple
.InstallDirs
34 import qualified Distribution
.Simple
.LocalBuildInfo
as LBI
35 import Distribution
.Simple
.Program
.Db
36 import Distribution
.Simple
.Program
.Find
37 import Distribution
.Simple
.Program
.Run
38 import Distribution
.Simple
.Setup
.Benchmark
39 import Distribution
.Simple
.Setup
.Common
40 import Distribution
.Simple
.UserHooks
41 import Distribution
.Simple
.Utils
42 import Distribution
.System
(Platform
(Platform
))
43 import Distribution
.Types
.Benchmark
(Benchmark
(benchmarkBuildInfo
))
44 import Distribution
.Types
.UnqualComponentName
45 import Distribution
.Utils
.Path
47 import System
.Directory
(doesFileExist)
49 -- | Perform the \"@.\/setup bench@\" action.
52 -- ^ positional command-line arguments
53 -> PD
.PackageDescription
54 -- ^ information from the .cabal file
56 -- ^ information from the configure step
58 -- ^ flags sent to benchmark
60 bench args pkg_descr lbi flags
= do
61 let verbosity
= fromFlag
$ benchmarkVerbosity flags
63 pkgBenchmarks
= PD
.benchmarks pkg_descr
64 enabledBenchmarks
= LBI
.enabledBenchLBIs pkg_descr lbi
65 mbWorkDir
= flagToMaybe
$ benchmarkWorkingDir flags
66 i
= interpretSymbolicPath mbWorkDir
-- See Note [Symbolic paths] in Distribution.Utils.Path
69 doBench
:: (PD
.Benchmark
, LBI
.ComponentLocalBuildInfo
) -> IO ExitCode
70 doBench
(bm
, clbi
) = do
73 { -- Include any build-tool-depends on build tools internal to the current package.
78 (benchmarkBuildInfo bm
)
79 (LBI
.withPrograms lbi
)
81 case PD
.benchmarkInterface bm
of
82 PD
.BenchmarkExeV10 _ _
-> do
83 let cmd
= i
$ LBI
.buildDir lbiForBench
</> makeRelativePathEx
(name
</> name
<.> exeExtension
(LBI
.hostPlatform lbi
))
85 map (benchOption pkg_descr lbiForBench bm
) $
86 benchmarkOptions flags
87 -- Check that the benchmark executable exists.
88 exists
<- doesFileExist cmd
90 dieWithException verbosity
$
91 NoBenchMarkProgram cmd
93 existingEnv
<- getEnvironment
95 -- Compute the appropriate environment for running the benchmark
96 let progDb
= LBI
.withPrograms lbiForBench
97 pathVar
= progSearchPath progDb
98 envOverrides
= progOverrideEnv progDb
99 newPath
<- programSearchPathAsPATHVar pathVar
100 overrideEnv
<- fromMaybe [] <$> getEffectiveEnvironment
([("PATH", Just newPath
)] ++ envOverrides
)
101 let shellEnv
= overrideEnv
++ existingEnv
103 -- Add (DY)LD_LIBRARY_PATH if needed
105 if LBI
.withDynExe lbiForBench
107 let (Platform _ os
) = LBI
.hostPlatform lbiForBench
108 paths
<- LBI
.depLibraryPaths
True False lbiForBench clbi
109 return (addLibraryPath os paths shellEnv
)
112 notice verbosity
$ startMessage name
113 -- This will redirect the child process
114 -- stdout/stderr to the parent process.
115 exitcode
<- rawSystemExitCode verbosity mbWorkDir cmd options
(Just shellEnv
')
116 notice verbosity
$ finishMessage name exitcode
120 "No support for running "
124 ++ prettyShow
(PD
.benchmarkType bm
)
127 name
= unUnqualComponentName
$ PD
.benchmarkName bm
129 unless (PD
.hasBenchmarks pkg_descr
) $ do
130 notice verbosity
"Package has no benchmarks."
133 when (PD
.hasBenchmarks pkg_descr
&& null enabledBenchmarks
) $
134 dieWithException verbosity EnableBenchMark
136 bmsToRun
<- case benchmarkNames
of
137 [] -> return enabledBenchmarks
138 names
-> for names
$ \bmName
->
139 let benchmarkMap
= zip enabledNames enabledBenchmarks
140 enabledNames
= map (PD
.benchmarkName
. fst) enabledBenchmarks
141 allNames
= map PD
.benchmarkName pkgBenchmarks
142 in case lookup (mkUnqualComponentName bmName
) benchmarkMap
of
145 | mkUnqualComponentName bmName `
elem` allNames
->
146 dieWithException verbosity
$ BenchMarkNameDisabled bmName
147 |
otherwise -> dieWithException verbosity
$ NoBenchMark bmName
149 let totalBenchmarks
= length bmsToRun
150 notice verbosity
$ "Running " ++ show totalBenchmarks
++ " benchmarks..."
151 exitcodes
<- traverse doBench bmsToRun
153 let allOk
= totalBenchmarks
== length (filter (== ExitSuccess
) exitcodes
)
154 unless allOk
exitFailure
156 startMessage name
= "Benchmark " ++ name
++ ": RUNNING...\n"
157 finishMessage name exitcode
=
161 ++ ( case exitcode
of
162 ExitSuccess
-> "FINISH"
163 ExitFailure _
-> "ERROR"
166 -- TODO: This is abusing the notion of a 'PathTemplate'. The result isn't
167 -- necessarily a path.
169 :: PD
.PackageDescription
170 -> LBI
.LocalBuildInfo
174 benchOption pkg_descr lbi bm template
=
175 fromPathTemplate
$ substPathTemplate env template
178 initialPathTemplateEnv
179 (PD
.package pkg_descr
)
180 (LBI
.localUnitId lbi
)
181 (compilerInfo
$ LBI
.compiler lbi
)
182 (LBI
.hostPlatform lbi
)
183 ++ [(BenchmarkNameVar
, toPathTemplate
$ unUnqualComponentName
$ PD
.benchmarkName bm
)]