@@ -17,33 +17,35 @@ module Distribution.Client.CmdStatus (
17
17
statusCommand , statusAction ,
18
18
) where
19
19
20
+ import Control.Monad
21
+ ( mapM )
20
22
import qualified Data.Map as Map
21
23
22
24
import Prelude ()
23
25
import Distribution.Client.Compat.Prelude
24
26
25
- import Distribution.Client.DistDirLayout
26
27
import Distribution.Client.TargetProblem
27
28
import Distribution.Client.CmdErrorMessages
28
29
import qualified Distribution.Client.InstallPlan as InstallPlan
29
30
import Distribution.Client.NixStyleOptions
30
31
( NixStyleFlags (.. ), nixStyleOptions , defaultNixStyleFlags )
31
32
import Distribution.Client.ProjectOrchestration
32
33
import Distribution.Client.ProjectPlanning
33
- import Distribution.Client.ProjectPlanning.Types
34
34
import Distribution.Client.Setup
35
35
( GlobalFlags , ConfigFlags (.. ), yesNoOpt )
36
+ import Distribution.Client.Types
37
+ ( PackageSpecifier , PackageLocation )
38
+ import Distribution.Client.TargetSelector
39
+ ( TargetSelectorProblem )
36
40
import Distribution.Client.Utils.Json
37
41
( (.=) )
38
42
import qualified Distribution.Client.Utils.Json as Json
39
43
import Distribution.Client.Version
40
44
( cabalInstallVersion )
41
- import Distribution.InstalledPackageInfo
42
- ( InstalledPackageInfo )
43
- import Distribution.Parsec (parsecCommaList , parsecToken )
45
+
46
+ import qualified Distribution.Compat.CharParsing as P
44
47
import Distribution.ReadE
45
48
( ReadE (ReadE ), parsecToReadE )
46
- import Distribution.Simple.BuildPaths (buildInfoPref )
47
49
import Distribution.Simple.Command
48
50
( CommandUI (.. ), option , reqArg , ShowOrParseArgs , OptionField )
49
51
import Distribution.Simple.Compiler
@@ -52,6 +54,8 @@ import Distribution.Simple.Flag
52
54
( Flag (.. ), fromFlagOrDefault )
53
55
import Distribution.Simple.Utils
54
56
( wrapText , die' , withOutputMarker , ordNub )
57
+ import Distribution.Solver.Types.SourcePackage
58
+ import Distribution.Types.UnitId
55
59
import Distribution.Verbosity
56
60
( normal )
57
61
import Distribution.Version
@@ -68,14 +72,14 @@ statusCommand = CommandUI
68
72
" Query for available targets and project information such as project GHC."
69
73
, commandNotes = Just $ \ pname ->
70
74
" Examples:\n "
71
- ++ " " ++ pname ++ " status --output-format=json --compiler-info \n "
75
+ ++ " " ++ pname ++ " status --output-format=json --compiler\n "
72
76
++ " Print the compiler that is used for this project in the json format.\n "
73
- ++ " " ++ pname ++ " status --output-format=json --build-info =./src/Foo.hs\n "
74
- ++ " Print the location of the component \" src/Foo.hs\" belongs to.\n "
75
- ++ " " ++ pname ++ " status --output-format=json --build-info =./src/Foo.hs\n "
76
- ++ " Print both, compiler information and build-info location for the given target.\n "
77
- ++ " " ++ pname ++ " status --output-format=json --build-info =./src/Foo.hs --build-info =./test/Bar.hs\n "
78
- ++ " Print build-info location for multiple targets.\n "
77
+ ++ " " ++ pname ++ " status --output-format=json --target =./src/Foo.hs\n "
78
+ ++ " Print the unit-id of the component \" src/Foo.hs\" belongs to.\n "
79
+ ++ " " ++ pname ++ " status --output-format=json --target =./src/Foo.hs\n "
80
+ ++ " Print both, compiler information and unit-id for the given target.\n "
81
+ ++ " " ++ pname ++ " status --output-format=json --target =./src/Foo.hs --target =./test/Bar.hs\n "
82
+ ++ " Print unit-id location for multiple targets.\n "
79
83
, commandUsage = \ pname ->
80
84
" Usage: " ++ pname ++ " status [FLAGS]\n "
81
85
, commandDefaultFlags = defaultNixStyleFlags defaultStatusFlags
@@ -92,14 +96,14 @@ data StatusOutputFormat
92
96
deriving (Eq , Ord , Show , Read )
93
97
94
98
data StatusFlags = StatusFlags
95
- { statusBuildInfo :: [String ]
99
+ { statusTargets :: [String ]
96
100
, statusCompiler :: Flag Bool
97
101
, statusOutputFormat :: Flag StatusOutputFormat
98
102
} deriving (Eq , Show , Read )
99
103
100
104
defaultStatusFlags :: StatusFlags
101
105
defaultStatusFlags = StatusFlags
102
- { statusBuildInfo = mempty
106
+ { statusTargets = mempty
103
107
, statusCompiler = mempty
104
108
, statusOutputFormat = mempty
105
109
}
@@ -125,23 +129,22 @@ statusOptions showOrParseArgs =
125
129
statusOutputFormatParser
126
130
statusOutputFormatPrinter
127
131
)
128
- , option [] [" build-info " ]
129
- " List all available targets in the project "
130
- statusBuildInfo (\ v flags -> flags { statusBuildInfo = v ++ statusBuildInfo flags})
131
- (reqArg " TARGET" buildInfoTargetReadE ( fmap show ) )
132
- , option [] [" compiler-info " ]
132
+ , option [] [" target " ]
133
+ " Given a target, obtain the unit-id in the build-plan "
134
+ statusTargets (\ v flags -> flags { statusTargets = v ++ statusTargets flags})
135
+ (reqArg " TARGET" buildInfoTargetReadE id )
136
+ , option [] [" compiler" ]
133
137
" Print information of the project compiler"
134
138
statusCompiler (\ v flags -> flags { statusCompiler = v })
135
139
(yesNoOpt showOrParseArgs)
136
140
]
137
141
where
138
142
buildInfoTargetReadE :: ReadE [String ]
139
143
buildInfoTargetReadE =
140
- parsecToReadE
144
+ fmap pure $ parsecToReadE
141
145
-- This error should never be shown
142
146
(" couldn't parse targets: " ++ )
143
- -- TODO: wrong parser, kills filepaths with spaces
144
- (parsecCommaList parsecToken)
147
+ (P. munch1 (const True ))
145
148
146
149
-------------------------------------------------------------------------------
147
150
-- Action
@@ -163,49 +166,112 @@ statusAction flags@NixStyleFlags { extraFlags = statusFlags, ..} cliTargetString
163
166
(projectConfig baseCtx)
164
167
(localPackages baseCtx)
165
168
166
- let initialJson = Json. object
167
- [ " cabal-version" .= jdisplay cabalInstallVersion
168
- ]
169
-
170
- compilerJson <- if not $ fromFlagOrDefault False (statusCompiler statusFlags)
171
- then pure $ Json. object [] -- Neutral element
169
+ compilerInformation <- if not $ fromFlagOrDefault False (statusCompiler statusFlags)
170
+ then pure Nothing
172
171
else do
173
172
let compiler = pkgConfigCompiler elabSharedConfig
174
173
compilerProg <- requireCompilerProg verbosity compiler
175
174
let progDb = pkgConfigCompilerProgs elabSharedConfig
176
175
(configuredCompilerProg, _) <- requireProgram verbosity compilerProg progDb
177
- pure $ mkCompilerInfo configuredCompilerProg compiler
176
+ pure $ Just $ mkCompilerInfo configuredCompilerProg compiler
178
177
179
- buildInfoJson <- if null (statusBuildInfo statusFlags)
180
- then pure $ Json. object [] -- Neutral element
178
+ resolvedTargets <- if null (statusTargets statusFlags)
179
+ then pure Nothing
181
180
else do
182
- let targetStrings = statusBuildInfo statusFlags
183
- targetSelectors <- readTargetSelectors (localPackages baseCtx) Nothing targetStrings >>= \ case
184
- Left err -> reportTargetSelectorProblems verbosity err
185
- Right sels -> pure sels
186
-
181
+ let targetStrings = statusTargets statusFlags
182
+ mtargetSelectors <- mapM (readTargetSelector (localPackages baseCtx) Nothing ) targetStrings
183
+ let (unresolvable, targetSelectors) = partitionEithers
184
+ $ map (\ (mts, str) -> case mts of
185
+ Left _ -> Left str
186
+ Right ts -> Right (ts, str)
187
+ )
188
+ $ zip mtargetSelectors targetStrings
187
189
-- Interpret the targets on the command line as build targets
188
190
-- (as opposed to say repl or haddock targets).
189
191
-- TODO: don't throw on targets that are invalid.
192
+ -- TODO: why might this still fail? should we try to avoid that?
190
193
targets <- either (reportBuildTargetProblems verbosity) return
191
194
$ resolveTargets
192
195
selectPackageTargets
193
196
selectComponentTarget
194
197
elaboratedPlan
195
198
Nothing
196
- targetSelectors
199
+ ( map fst targetSelectors)
197
200
198
- pure $ mkBuildInfoJson (distDirLayout baseCtx) elabSharedConfig
199
- elaboratedPlan targets targetSelectors targetStrings
201
+ pure $ Just $ mkBuildInfoJson elaboratedPlan targets (Map. fromList targetSelectors) unresolvable
200
202
201
- let statusJson = mergeJsonObjects [initialJson, compilerJson, buildInfoJson]
203
+ let si = StatusInformation
204
+ { siCabalVersion = cabalInstallVersion
205
+ , siCompiler = compilerInformation
206
+ , siTargetResolving = resolvedTargets
207
+ }
208
+
209
+ serialisedStatusInformation <- serialise verbosity (statusOutputFormat statusFlags) si
202
210
203
211
-- Final output
204
- putStrLn $ withOutputMarker verbosity $ Json. encodeToString statusJson
212
+ putStrLn $ withOutputMarker verbosity serialisedStatusInformation
205
213
where
206
214
verbosity = fromFlagOrDefault normal (configVerbosity configFlags)
207
215
cliConfig = commandLineFlagsToProjectConfig globalFlags flags mempty
208
216
217
+ -- ----------------------------------------------------------------------------
218
+ -- Big Datatype that can be serialised to different formats
219
+ -- ----------------------------------------------------------------------------
220
+
221
+ data StatusInformation = StatusInformation
222
+ { siCabalVersion :: Version
223
+ , siCompiler :: Maybe CompilerInformation
224
+ , siTargetResolving :: Maybe [ResolvedTarget ]
225
+ }
226
+ deriving (Show , Read , Eq , Ord )
227
+
228
+ data CompilerInformation = CompilerInformation
229
+ { ciFlavour :: CompilerFlavor
230
+ , ciCompilerId :: CompilerId
231
+ , ciPath :: FilePath
232
+ }
233
+ deriving (Show , Read , Eq , Ord )
234
+
235
+ data ResolvedTarget = ResolvedTarget
236
+ { rtOriginalTarget :: String
237
+ -- | UnitId of the resolved target.
238
+ -- If 'Nothing', then the given target can not be resolved
239
+ -- to a target in this project.
240
+ , rtUnitId :: Maybe UnitId
241
+ }
242
+ deriving (Show , Read , Eq , Ord )
243
+
244
+ serialise :: Verbosity -> Flag StatusOutputFormat -> StatusInformation -> IO String
245
+ serialise verbosity NoFlag _ =
246
+ die' verbosity $ " Could not serialise Status information. "
247
+ ++ " The flag '--output-format' is required."
248
+
249
+ serialise _ (Flag JSON ) si = pure $ Json. encodeToString $ Json. object $
250
+ [ " cabal-version" .= jdisplay (siCabalVersion si)
251
+ ]
252
+ ++ prettyCompilerInfo (siCompiler si)
253
+ ++ prettyTargetResolving (siTargetResolving si)
254
+ where
255
+ prettyCompilerInfo Nothing = []
256
+ prettyCompilerInfo (Just ci) =
257
+ [ " compiler" .= Json. object
258
+ [ " flavour" .= jdisplay (ciFlavour ci)
259
+ , " compiler-id" .= jdisplay (ciCompilerId ci)
260
+ , " path" .= Json. String (ciPath ci)
261
+ ]
262
+ ]
263
+
264
+ prettyTargetResolving Nothing = []
265
+ prettyTargetResolving (Just rts) =
266
+ [ " targets" .= Json. Array (fmap prettyResolvedTarget rts)
267
+ ]
268
+ where
269
+ prettyResolvedTarget rt = Json. object
270
+ [ " target" .= Json. String (rtOriginalTarget rt)
271
+ , " unit-id" .= maybe Json. Null jdisplay (rtUnitId rt)
272
+ ]
273
+
274
+
209
275
-- ----------------------------------------------------------------------------
210
276
-- Helpers for determining and serialising compiler information
211
277
-- ----------------------------------------------------------------------------
@@ -219,83 +285,29 @@ requireCompilerProg verbosity compiler =
219
285
" status: Unsupported compiler flavour: "
220
286
<> prettyShow flavour
221
287
222
- mkCompilerInfo :: ConfiguredProgram -> Compiler -> Json. Value
288
+ mkCompilerInfo :: ConfiguredProgram -> Compiler -> CompilerInformation
223
289
mkCompilerInfo compilerProgram compiler =
224
- Json. object
225
- [ " compiler" .= Json. object
226
- [ " flavour" .= Json. String (prettyShow $ compilerFlavor compiler)
227
- , " compiler-id" .= Json. String (showCompilerId compiler)
228
- , " path" .= Json. String (programPath compilerProgram)
229
- ]
230
- ]
290
+ CompilerInformation (compilerFlavor compiler) (compilerId compiler) (programPath compilerProgram)
231
291
232
292
-- ----------------------------------------------------------------------------
233
- -- Helpers for determining and serialising build info
293
+ -- Helpers for determining and serialising the unit-id
234
294
-- ----------------------------------------------------------------------------
235
295
236
- mkBuildInfoJson :: DistDirLayout -> ElaboratedSharedConfig -> ElaboratedInstallPlan -> TargetsMap -> [TargetSelector ] -> [String ] -> Json. Value
237
- mkBuildInfoJson distDirLayout elaboratedSharedConfig elaboratedPlan targetsMap targetSelectors targetStrings = Json. object
238
- [ " build-info" .= Json. Array allTargetsJsons
296
+ mkBuildInfoJson :: ElaboratedInstallPlan -> TargetsMap -> Map TargetSelector String -> [String ] -> [ResolvedTarget ]
297
+ mkBuildInfoJson elaboratedPlan targetsMap tsMap unresolvableTargetStrings =
298
+ [ ResolvedTarget str (Just uid)
299
+ | uid <- Map. keys subsetInstallPlan
300
+ , (_, tss) <- targetsMap Map. ! uid
301
+ , str <- ordNub $ map tsToOriginalTarget $ toList tss
239
302
]
303
+ ++ map mkUnresolvedTarget unresolvableTargetStrings
240
304
where
241
- allTargetsJsons =
242
- [ planPackageToJ elab ts
243
- | (uid, elab) <- Map. assocs subsetInstallPlan
244
- , (_, tss) <- targetsMap Map. ! uid
245
- , ts <- ordNub $ toList tss
246
- ]
247
-
248
305
subsetInstallPlan = Map. restrictKeys (InstallPlan. toMap elaboratedPlan) (Map. keysSet targetsMap)
249
306
250
- targetsTable = Map. fromList $ zip targetSelectors targetStrings
251
-
252
- tsToOriginalTarget ts = targetsTable Map. ! ts
253
-
254
- planPackageToJ :: ElaboratedPlanPackage -> TargetSelector -> Json. Value
255
- planPackageToJ pkg ts =
256
- case pkg of
257
- InstallPlan. PreExisting ipi -> installedPackageInfoToJ ipi
258
- InstallPlan. Configured elab -> elaboratedPackageToJ elab ts
259
- InstallPlan. Installed elab -> elaboratedPackageToJ elab ts
260
- -- Note that the --build-info currently only uses the elaborated plan,
261
- -- not the improved plan. So we will not get the Installed state for
262
- -- that case, but the code supports it in case we want to use this
263
- -- later in some use case where we want the status of the build.
264
-
265
- -- TODO: what should we do if we run in this case?
266
- -- Happens on `--build-info=containers` while we are not in the containers project.
267
- installedPackageInfoToJ :: InstalledPackageInfo -> Json. Value
268
- installedPackageInfoToJ _ipi =
269
- -- Pre-existing packages lack configuration information such as their flag
270
- -- settings or non-lib components. We only get pre-existing packages for
271
- -- the global/core packages however, so this isn't generally a problem.
272
- -- So these packages are never local to the project.
273
- --
274
- Json. object []
275
-
276
- elaboratedPackageToJ :: ElaboratedConfiguredPackage -> TargetSelector -> Json. Value
277
- elaboratedPackageToJ elab ts = Json. object
278
- [ " target" .= Json. String (tsToOriginalTarget ts)
279
- , " path" .= maybe Json. Null Json. String buildInfoFileLocation
280
- ]
281
- where
282
- dist_dir :: FilePath
283
- dist_dir = distBuildDirectory distDirLayout
284
- (elabDistDirParams elaboratedSharedConfig elab)
285
-
286
- -- | Only add build-info file location if the Setup.hs CLI
287
- -- is recent enough to be able to generate build info files.
288
- -- Otherwise, write 'null'.
289
- --
290
- -- Consumers of `status` can use the nullability of this file location
291
- -- to indicate that the given component uses `build-type: Custom`
292
- -- with an old lib:Cabal version.
293
- buildInfoFileLocation :: Maybe FilePath
294
- buildInfoFileLocation
295
- | elabSetupScriptCliVersion elab < mkVersion [3 , 7 , 0 , 0 ]
296
- = Nothing
297
- | otherwise
298
- = Just (buildInfoPref dist_dir)
307
+ tsToOriginalTarget ts = tsMap Map. ! ts
308
+
309
+ mkUnresolvedTarget :: String -> ResolvedTarget
310
+ mkUnresolvedTarget s = ResolvedTarget s Nothing
299
311
300
312
-- ----------------------------------------------------------------------------
301
313
-- Target selectors and helpers
@@ -350,17 +362,20 @@ reportBuildTargetProblems :: Verbosity -> [TargetProblem'] -> IO a
350
362
reportBuildTargetProblems verbosity problems =
351
363
reportTargetProblems verbosity " status" problems
352
364
365
+ readTargetSelector :: [PackageSpecifier (SourcePackage (PackageLocation a ))]
366
+ -> Maybe ComponentKindFilter
367
+ -> String
368
+ -> IO (Either TargetSelectorProblem TargetSelector )
369
+ readTargetSelector pkgs mfilter targetStr =
370
+ readTargetSelectors pkgs mfilter [targetStr] >>= \ case
371
+ Left [problem] -> pure $ Left problem
372
+ Right [ts] -> pure $ Right ts
373
+ _ -> error $ " CmdStatus.readTargetSelector: invariant broken, more than "
374
+ ++ " one target passed *somehow*."
375
+
353
376
-- ----------------------------------------------------------------------------
354
377
-- JSON serialisation helpers
355
378
-- ----------------------------------------------------------------------------
356
379
357
380
jdisplay :: Pretty a => a -> Json. Value
358
381
jdisplay = Json. String . prettyShow
359
-
360
- mergeJsonObjects :: [Json. Value ] -> Json. Value
361
- mergeJsonObjects = Json. object . foldl' go []
362
- where
363
- go acc (Json. Object objs) =
364
- acc <> objs
365
- go _ _ =
366
- error " mergeJsonObjects: Only objects can be merged"
0 commit comments