@@ -13,6 +13,9 @@ module Distribution.Client.CmdRun (
13
13
selectComponentTarget
14
14
) where
15
15
16
+ import Prelude ()
17
+ import Distribution.Client.Compat.Prelude
18
+
16
19
import Distribution.Client.ProjectOrchestration
17
20
import Distribution.Client.CmdErrorMessages
18
21
@@ -30,11 +33,34 @@ import Distribution.Text
30
33
import Distribution.Verbosity
31
34
( Verbosity , normal )
32
35
import Distribution.Simple.Utils
33
- ( wrapText , die' , ordNub )
36
+ ( wrapText , die' , ordNub , info )
37
+ import Distribution.Types.PackageName
38
+ ( unPackageName )
39
+ import Distribution.Client.ProjectPlanning
40
+ ( ElaboratedConfiguredPackage (.. )
41
+ , ElaboratedInstallPlan , binDirectoryFor )
42
+ import Distribution.Client.InstallPlan
43
+ ( toList , foldPlanPackage )
44
+ import Distribution.Client.ProjectPlanning.Types
45
+ ( ElaboratedPackageOrComponent (.. )
46
+ , ElaboratedComponent (compComponentName ) )
47
+ import Distribution.Types.Executable
48
+ ( Executable (exeName ) )
49
+ import Distribution.Types.UnqualComponentName
50
+ ( UnqualComponentName , unUnqualComponentName )
51
+ import Distribution.Types.PackageDescription
52
+ ( PackageDescription (executables ) )
53
+ import Distribution.Simple.Program.Run
54
+ ( runProgramInvocation , simpleProgramInvocation )
55
+ import Distribution.Types.PackageId
56
+ ( PackageIdentifier (.. ) )
34
57
35
58
import qualified Data.Map as Map
36
59
import qualified Data.Set as Set
37
- import Control.Monad (when )
60
+ import Data.Function
61
+ ( on )
62
+ import System.FilePath
63
+ ( (</>) )
38
64
39
65
40
66
runCommand :: CommandUI (ConfigFlags , ConfigExFlags , InstallFlags , HaddockFlags )
@@ -90,7 +116,8 @@ runAction (configFlags, configExFlags, installFlags, haddockFlags)
90
116
baseCtx <- establishProjectBaseContext verbosity cliConfig
91
117
92
118
targetSelectors <- either (reportTargetSelectorProblems verbosity) return
93
- =<< readTargetSelectors (localPackages baseCtx) targetStrings
119
+ =<< readTargetSelectors (localPackages baseCtx)
120
+ (take 1 targetStrings) -- Drop the exe's args.
94
121
95
122
buildCtx <-
96
123
runProjectPreBuildPhase verbosity baseCtx $ \ elaboratedPlan -> do
@@ -128,12 +155,181 @@ runAction (configFlags, configExFlags, installFlags, haddockFlags)
128
155
129
156
buildOutcomes <- runProjectBuildPhase verbosity baseCtx buildCtx
130
157
runProjectPostBuildPhase verbosity baseCtx buildCtx buildOutcomes
158
+
159
+ -- Get the selectors for the package and component.
160
+ -- These are wrapped in Maybes, because the user
161
+ -- might not specify them.
162
+ (selectedPackage, selectedComponent) <-
163
+ -- This should always match [x] anyway because
164
+ -- we already check for a single target in TargetSelector.hs
165
+ case selectorPackageAndComponent <$> targetSelectors
166
+ of [x] -> return x
167
+ [ ] -> die'
168
+ verbosity
169
+ " No targets given, but the run phase has been reached. This is a bug."
170
+ _ -> die'
171
+ verbosity
172
+ " Multiple targets given, but the run phase has been reached. This is a bug."
173
+
174
+ let elaboratedPlan = elaboratedPlanOriginal buildCtx
175
+ matchingElaboratedConfiguredPackages =
176
+ extractMatchingElaboratedConfiguredPackages
177
+ selectedPackage
178
+ selectedComponent
179
+ elaboratedPlan
180
+
181
+ -- The names to match. Used only for user feedback, as
182
+ -- later on we extract the real ones (whereas these are
183
+ -- wrapped in a Maybe) from the package itself.
184
+ let selectedPackageNameToMatch = getPackageName <$> selectedPackage
185
+ selectedComponentNameToMatch = getExeComponentName =<< selectedComponent
186
+
187
+ -- For each ElaboratedConfiguredPackage in the install plan, we
188
+ -- identify candidate executables. We only keep them if both the
189
+ -- package name and executable name match what the user asked for
190
+ -- (a missing specification matches everything).
191
+ --
192
+ -- In the common case, we expect this to pick out a single
193
+ -- ElaboratedConfiguredPackage that provides a single way of building
194
+ -- an appropriately-named executable. In that case we prune our
195
+ -- install plan to that UnitId and PackageTarget and continue.
196
+ --
197
+ -- However, multiple packages/components could provide that
198
+ -- executable, or it's possible we don't find the executable anywhere
199
+ -- in the build plan. I suppose in principle it's also possible that
200
+ -- a single package provides an executable in two different ways,
201
+ -- though that's probably a bug if. Anyway it's a good lint to report
202
+ -- an error in all of these cases, even if some seem like they
203
+ -- shouldn't happen.
204
+ (pkg,exe) <- case matchingElaboratedConfiguredPackages of
205
+ [] -> die' verbosity $ " Unknown executable"
206
+ ++ case selectedComponentNameToMatch
207
+ of Just x -> " " ++ x
208
+ Nothing -> " "
209
+ ++ case selectedPackageNameToMatch
210
+ of Just x -> " in package " ++ x
211
+ Nothing -> " "
212
+ [(elabPkg,exe)] -> do
213
+ info verbosity $ " Selecting " ++ display (elabUnitId elabPkg)
214
+ ++ case selectedComponentNameToMatch
215
+ of Just x -> " to supply " ++ x
216
+ Nothing -> " "
217
+ return (elabPkg, unUnqualComponentName exe)
218
+ elabPkgs -> die' verbosity
219
+ $ " Multiple matching executables found"
220
+ ++ case selectedComponentNameToMatch
221
+ of Just x -> " matching " ++ x
222
+ Nothing -> " "
223
+ ++ " :\n "
224
+ ++ unlines (fmap (\ (p,_) -> " - in package " ++ display (elabUnitId p)) elabPkgs)
225
+ let exePath = binDirectoryFor (distDirLayout baseCtx)
226
+ (elaboratedShared buildCtx)
227
+ pkg
228
+ exe
229
+ </> exe
230
+ let args = drop 1 targetStrings
231
+ runProgramInvocation
232
+ verbosity
233
+ (simpleProgramInvocation exePath args)
131
234
where
132
235
verbosity = fromFlagOrDefault normal (configVerbosity configFlags)
133
236
cliConfig = commandLineFlagsToProjectConfig
134
237
globalFlags configFlags configExFlags
135
238
installFlags haddockFlags
136
239
240
+ -- Package selection
241
+ ------
242
+
243
+ getPackageName :: PackageIdentifier -> String
244
+ getPackageName (PackageIdentifier packageName _) =
245
+ unPackageName packageName
246
+
247
+ getExeComponentName :: ComponentName -> Maybe String
248
+ getExeComponentName (CExeName unqualComponentName) =
249
+ Just $ unUnqualComponentName unqualComponentName
250
+ getExeComponentName _ = Nothing
251
+
252
+ selectorPackageAndComponent :: TargetSelector PackageId
253
+ -> (Maybe PackageId , Maybe ComponentName )
254
+ selectorPackageAndComponent (TargetPackage _ pkg _) =
255
+ (Just pkg, Nothing )
256
+ selectorPackageAndComponent (TargetAllPackages _) =
257
+ (Nothing , Nothing )
258
+ selectorPackageAndComponent (TargetComponent pkg component _) =
259
+ (Just pkg, Just component)
260
+
261
+ -- | Extract all 'ElaboratedConfiguredPackage's and executable names
262
+ -- that match the user-provided component/package
263
+ -- The component can be either:
264
+ -- * specified by the user (both Just)
265
+ -- * deduced from an user-specified package (the component is unspecified, Nothing)
266
+ -- * deduced from the cwd (both the package and the component are unspecified)
267
+ extractMatchingElaboratedConfiguredPackages
268
+ :: Maybe PackageId -- ^ the package to match
269
+ -> Maybe ComponentName -- ^ the component to match
270
+ -> ElaboratedInstallPlan -- ^ a plan in with to search for matching exes
271
+ -> [(ElaboratedConfiguredPackage , UnqualComponentName )] -- ^ the matching package and the exe name
272
+ extractMatchingElaboratedConfiguredPackages
273
+ pkgId component = nubBy equalPackageIdAndExe
274
+ . catMaybes
275
+ . fmap sequenceA' -- get the Maybe outside the tuple
276
+ . fmap (\ p -> (p, matchingExecutable p))
277
+ . catMaybes
278
+ . fmap (foldPlanPackage
279
+ (const Nothing )
280
+ (\ x -> if match x
281
+ then Just x
282
+ else Nothing ))
283
+ . toList
284
+ where
285
+ -- We need to support ghc 7.6, so we don't have
286
+ -- a sequenceA that works on tuples yet.
287
+ -- Once we drop support for pre-ftp ghc
288
+ -- it's safe to remove this.
289
+ sequenceA' (a, Just b) = Just (a, b)
290
+ sequenceA' _ = Nothing
291
+ match :: ElaboratedConfiguredPackage -> Bool
292
+ match p = matchPackage pkgId p && matchComponent component p
293
+ matchingExecutable p = exactlyOne
294
+ $ filter (\ x -> Just x == componentString
295
+ || isNothing componentString)
296
+ $ executablesOfPackage p
297
+ componentString = componentNameString =<< component
298
+ exactlyOne [x] = Just x
299
+ exactlyOne _ = Nothing
300
+ equalPackageIdAndExe (p,c) (p',c') = c== c' && ((==) `on` elabPkgSourceId) p p'
301
+
302
+ matchPackage :: Maybe PackageId
303
+ -> ElaboratedConfiguredPackage
304
+ -> Bool
305
+ matchPackage pkgId pkg =
306
+ pkgId == Just (elabPkgSourceId pkg)
307
+ || isNothing pkgId -- if the package is unspecified (Nothing), all packages match
308
+
309
+ matchComponent :: Maybe ComponentName
310
+ -> ElaboratedConfiguredPackage
311
+ -> Bool
312
+ matchComponent component pkg =
313
+ componentString `elem` (Just <$> executablesOfPackage pkg)
314
+ || isNothing componentString -- if the component is unspecified (Nothing), all components match
315
+ where componentString = componentNameString =<< component
316
+
317
+ executablesOfPackage :: ElaboratedConfiguredPackage
318
+ -> [UnqualComponentName ]
319
+ executablesOfPackage p =
320
+ case exeFromComponent
321
+ of Just exe -> [exe]
322
+ Nothing -> exesFromPackage
323
+ where
324
+ exeFromComponent =
325
+ case elabPkgOrComp p
326
+ of ElabComponent comp -> case compComponentName comp
327
+ of Just (CExeName exe) -> Just exe
328
+ _ -> Nothing
329
+ _ -> Nothing
330
+ exesFromPackage = fmap exeName $ executables $ elabPkgDescription p
331
+
332
+
137
333
-- | This defines what a 'TargetSelector' means for the @run@ command.
138
334
-- It selects the 'AvailableTarget's that the 'TargetSelector' refers to,
139
335
-- or otherwise classifies the problem.
0 commit comments