1 -------------------------------------------------------------------------------
2 -------------------------------------------------------------------------------
3 {-# LANGUAGE RecordWildCards #-}
6 -- Module : Distribution.Client.Exec
7 -- Maintainer : cabal-devel@haskell.org
8 -- Portability : portable
10 -- Implementation of the 'v2-exec' command for running an arbitrary executable
11 -- in an environment suited to the part of the store built for a project.
12 module Distribution
.Client
.CmdExec
17 import Distribution
.Client
.DistDirLayout
20 import Distribution
.Client
.InstallPlan
21 ( GenericPlanPackage
(..)
24 import Distribution
.Client
.NixStyleOptions
26 , defaultNixStyleFlags
29 import Distribution
.Client
.ProjectConfig
.Types
30 ( ProjectConfig
(projectConfigShared
)
31 , ProjectConfigShared
(projectConfigProgPathExtra
)
33 import Distribution
.Client
.ProjectFlags
34 ( removeIgnoreProjectOption
36 import Distribution
.Client
.ProjectOrchestration
37 ( BuildTimeSettings
(..)
39 , ProjectBaseContext
(..)
40 , ProjectBuildContext
(..)
41 , commandLineFlagsToProjectConfig
43 , establishProjectBaseContext
44 , runProjectPreBuildPhase
46 import Distribution
.Client
.ProjectPlanOutput
47 ( PostBuildProjectStatus
48 , argsEquivalentOfGhcEnvironmentFile
49 , createPackageEnvironment
50 , updatePostBuildProjectStatus
52 import Distribution
.Client
.ProjectPlanning
53 ( ElaboratedInstallPlan
54 , ElaboratedSharedConfig
(..)
56 import qualified Distribution
.Client
.ProjectPlanning
as Planning
57 import Distribution
.Client
.ProjectPlanning
.Types
58 ( dataDirsEnvironmentForPlan
60 import Distribution
.Client
.Setup
61 ( ConfigFlags
(configCommonFlags
)
64 import Distribution
.Simple
.Command
67 import Distribution
.Simple
.Flag
70 import Distribution
.Simple
.GHC
71 ( GhcImplInfo
(supportsPkgEnvFiles
)
74 import Distribution
.Simple
.Program
81 import Distribution
.Simple
.Program
.Db
83 , prependProgramSearchPath
86 import Distribution
.Simple
.Program
.Run
88 , runProgramInvocation
90 import Distribution
.Simple
.Setup
(CommonSetupFlags
(..))
91 import Distribution
.Simple
.Utils
92 ( createDirectoryIfMissingVerbose
98 import Distribution
.Utils
.NubList
101 import Distribution
.Verbosity
105 import Distribution
.Client
.Compat
.Prelude
108 import qualified Data
.Map
as M
109 import qualified Data
.Set
as S
110 import Distribution
.Client
.Errors
112 execCommand
:: CommandUI
(NixStyleFlags
())
115 { commandName
= "v2-exec"
116 , commandSynopsis
= "Give a command access to the store."
117 , commandUsage
= \pname
->
118 "Usage: " ++ pname
++ " v2-exec [FLAGS] [--] COMMAND [--] [ARGS]\n"
119 , commandDescription
= Just
$ \pname
->
121 "During development it is often useful to run build tasks and perform"
122 ++ " one-off program executions to experiment with the behavior of build"
123 ++ " tools. It is convenient to run these tools in the same way "
125 ++ " itself would. The `"
127 ++ " v2-exec` command provides a way to"
130 ++ "Compiler tools will be configured to see the same subset of the store"
131 ++ " that builds would see. The PATH is modified to make all executables in"
132 ++ " the dependency tree available (provided they have been built already)."
133 ++ " Commands are also rewritten in the way cabal itself would. For"
136 ++ " v2-exec ghc` will consult the configuration"
137 ++ " to choose an appropriate version of ghc and to include any"
138 ++ " ghc-specific flags requested."
139 , commandNotes
= Nothing
141 removeIgnoreProjectOption
142 . nixStyleOptions
(const [])
143 , commandDefaultFlags
= defaultNixStyleFlags
()
146 execAction
:: NixStyleFlags
() -> [String] -> GlobalFlags
-> IO ()
147 execAction flags
@NixStyleFlags
{..} extraArgs globalFlags
= do
148 baseCtx
<- establishProjectBaseContext verbosity cliConfig OtherCommand
150 -- To set up the environment, we'd like to select the libraries in our
151 -- dependency tree that we've already built. So first we set up an install
152 -- plan, but we walk the dependency tree without first executing the plan.
154 runProjectPreBuildPhase
157 (\plan
-> return (plan
, M
.empty))
159 -- We use the build status below to decide what libraries to include in the
160 -- compiler environment, but we don't want to actually build anything. So we
161 -- pass mempty to indicate that nothing happened and we just want the current
164 updatePostBuildProjectStatus
166 (distDirLayout baseCtx
)
167 (elaboratedPlanOriginal buildCtx
)
168 (pkgsBuildStatus buildCtx
)
171 -- Some dependencies may have executables. Let's put those on the PATH.
172 let extraPaths
= pathAdditions baseCtx buildCtx
173 pkgProgs
= pkgConfigCompilerProgs
(elaboratedShared buildCtx
)
175 dataDirsEnvironmentForPlan
176 (distDirLayout baseCtx
)
177 (elaboratedPlanToExecute buildCtx
)
180 prependProgramSearchPath verbosity extraPaths extraEnvVars pkgProgs
182 -- Now that we have the packages, set up the environment. We accomplish this
183 -- by creating an environment file that selects the databases and packages we
184 -- computed in the previous step, and setting an environment variable to
185 -- point at the file.
186 -- In case ghc is too old to support environment files,
187 -- we pass the same info as arguments
188 let compiler
= pkgConfigCompiler
$ elaboratedShared buildCtx
189 envFilesSupported
= supportsPkgEnvFiles
(getImplInfo compiler
)
191 [] -> dieWithException verbosity SpecifyAnExecutable
193 (program
, _
) <- requireProgram verbosity
(simpleProgram exe
) programDb
195 argsEquivalentOfGhcEnvironmentFile
197 (distDirLayout baseCtx
)
198 (elaboratedPlanOriginal buildCtx
)
200 programIsConfiguredCompiler
=
202 (elaboratedShared buildCtx
)
206 ||
not programIsConfiguredCompiler
210 ( if envFilesSupported
211 then withTempEnvFile verbosity baseCtx buildCtx buildStatus
214 $ \envOverrides
-> do
220 invocation
= programInvocation program
' args
222 buildSettingDryRun
(buildSettings baseCtx
)
223 || buildSettingOnlyDownload
(buildSettings baseCtx
)
226 then notice verbosity
"Running of executable suppressed by flag(s)"
227 else runProgramInvocation verbosity invocation
229 verbosity
= fromFlagOrDefault normal
(setupVerbosity
$ configCommonFlags configFlags
)
231 commandLineFlagsToProjectConfig
234 mempty
-- ClientInstallFlags, not needed here
235 withOverrides env args program
=
237 { programOverrideEnv
= programOverrideEnv program
++ env
238 , programDefaultArgs
= programDefaultArgs program
++ args
241 matchCompilerPath
:: ElaboratedSharedConfig
-> ConfiguredProgram
-> Bool
242 matchCompilerPath elaboratedShared program
=
244 `
elem`
(programPath
<$> configuredCompilers
)
246 configuredCompilers
= configuredPrograms
$ pkgConfigCompilerProgs elaboratedShared
248 -- | Execute an action with a temporary .ghc.environment file reflecting the
249 -- current environment. The action takes an environment containing the env
250 -- variable which points ghc to the file.
253 -> ProjectBaseContext
254 -> ProjectBuildContext
255 -> PostBuildProjectStatus
256 -> ([(String, Maybe String)] -> IO a
)
258 withTempEnvFile verbosity baseCtx buildCtx buildStatus action
= do
259 let tmpDirTemplate
= distTempDirectory
(distDirLayout baseCtx
)
260 createDirectoryIfMissingVerbose verbosity
True tmpDirTemplate
267 createPackageEnvironment
270 (elaboratedPlanToExecute buildCtx
)
271 (elaboratedShared buildCtx
)
276 -- | Get paths to all dependency executables to be included in PATH.
277 pathAdditions
:: ProjectBaseContext
-> ProjectBuildContext
-> [FilePath]
278 pathAdditions ProjectBaseContext
{..} ProjectBuildContext
{..} =
279 paths
++ cabalConfigPaths
283 . projectConfigProgPathExtra
284 . projectConfigShared
288 binDirectories distDirLayout elaboratedShared elaboratedPlanToExecute
290 -- | Get paths to all dependency executables to be included in PATH.
293 -> ElaboratedSharedConfig
294 -> ElaboratedInstallPlan
296 binDirectories layout config
= fromElaboratedInstallPlan
298 fromElaboratedInstallPlan
= fromGraph
. toGraph
299 fromGraph
= foldMap fromPlan
300 fromSrcPkg
= S
.fromList
. Planning
.binDirectories layout config
302 fromPlan
(PreExisting _
) = mempty
303 fromPlan
(Configured pkg
) = fromSrcPkg pkg
304 fromPlan
(Installed pkg
) = fromSrcPkg pkg