2 {-# LANGUAGE LambdaCase #-}
3 {-# LANGUAGE NamedFieldPuns #-}
4 {-# LANGUAGE RecordWildCards #-}
6 -----------------------------------------------------------------------------
8 -----------------------------------------------------------------------------
11 -- Module : Distribution.Client.CmdPath
12 -- Maintainer : cabal-devel@haskell.org
13 -- Portability : portable
15 -- Implementation of the 'path' command. Query for project configuration
17 module Distribution
.Client
.CmdPath
22 import Distribution
.Client
.Compat
.Prelude
25 import Distribution
.Client
.CmdInstall
.ClientInstallFlags
28 import Distribution
.Client
.Config
34 import Distribution
.Client
.DistDirLayout
(CabalDirLayout
(..), distProjectRootDirectory
)
35 import Distribution
.Client
.Errors
36 import Distribution
.Client
.GlobalFlags
37 import Distribution
.Client
.NixStyleOptions
39 , defaultNixStyleFlags
42 import Distribution
.Client
.ProjectConfig
.Types
44 , ProjectConfigBuildOnly
(..)
45 , ProjectConfigShared
(..)
47 import Distribution
.Client
.ProjectOrchestration
48 import Distribution
.Client
.ProjectPlanning
49 import Distribution
.Client
.RebuildMonad
(runRebuild
)
50 import Distribution
.Client
.ScriptUtils
51 import Distribution
.Client
.Setup
55 import Distribution
.Client
.Utils
.Json
58 import qualified Distribution
.Client
.Utils
.Json
as Json
59 import Distribution
.Client
.Version
62 import Distribution
.ReadE
65 import Distribution
.Simple
.Command
73 import Distribution
.Simple
.Compiler
74 import Distribution
.Simple
.Flag
79 import Distribution
.Simple
.Program
80 import Distribution
.Simple
.Utils
86 import Distribution
.Verbosity
90 -------------------------------------------------------------------------------
92 -------------------------------------------------------------------------------
94 pathCommand
:: CommandUI
(NixStyleFlags PathFlags
)
97 { commandName
= "path"
98 , commandSynopsis
= "Query for simple project information."
99 , commandDescription
= Just
$ \_
->
101 "Query for configuration and project information such as project GHC.\n"
102 <> "The output order of query keys is implementation defined and should not be relied on.\n"
103 , commandNotes
= Just
$ \pname
->
107 <> " path --store-dir\n"
108 <> " Print the store-dir location of cabal.\n"
111 <> " path --output-format=json --compiler-info\n"
112 <> " Print compiler information in json format.\n"
115 <> " path --output-format=json --installdir --compiler-info\n"
116 <> " Print compiler information and installation directory in json format.\n"
119 <> " path --output-format=key-value --installdir\n"
120 <> " Print the installation directory, taking project information into account.\n"
123 <> " path -z --output-format=key-value --installdir\n"
124 <> " Print the installation directory, without taking project information into account.\n"
125 , commandUsage
= \pname
->
126 "Usage: " <> pname
<> " path [FLAGS]\n"
127 , commandDefaultFlags
= defaultNixStyleFlags defaultPathFlags
128 , commandOptions
= nixStyleOptions pathOptions
131 -------------------------------------------------------------------------------
133 -------------------------------------------------------------------------------
135 data PathOutputFormat
138 deriving (Eq
, Ord
, Show, Read, Enum
, Bounded
)
140 data PathFlags
= PathFlags
141 { pathCompiler
:: Flag
Bool
142 , pathOutputFormat
:: Flag PathOutputFormat
143 , pathDirectories
:: Flag
[ConfigPath
]
147 defaultPathFlags
:: PathFlags
150 { pathCompiler
= mempty
151 , pathOutputFormat
= mempty
152 , pathDirectories
= mempty
155 pathOutputFormatParser
:: ReadE
(Flag PathOutputFormat
)
156 pathOutputFormatParser
= ReadE
$ \case
157 "json" -> Right
$ Flag JSON
158 "key-value" -> Right
$ Flag KeyValue
161 "Cannot parse the status output format '"
165 pathOutputFormatString
:: PathOutputFormat
-> String
166 pathOutputFormatString JSON
= "json"
167 pathOutputFormatString KeyValue
= "key-value"
169 pathOutputFormatPrinter
170 :: Flag PathOutputFormat
-> [String]
171 pathOutputFormatPrinter
= \case
172 (Flag format
) -> [pathOutputFormatString format
]
175 pathOptions
:: ShowOrParseArgs
-> [OptionField PathFlags
]
176 pathOptions showOrParseArgs
=
180 "Output format of the requested path locations"
182 (\v flags
-> flags
{pathOutputFormat
= v
})
184 (intercalate
"|" $ map pathOutputFormatString
[minBound .. maxBound])
185 pathOutputFormatParser
186 pathOutputFormatPrinter
191 "Print information of the project compiler"
193 (\v flags
-> flags
{pathCompiler
= v
})
194 (yesNoOpt showOrParseArgs
)
196 <> map pathOption
[minBound .. maxBound]
202 ("Print cabal's " <> pathName s
)
204 (\v flags
-> flags
{pathDirectories
= Flag
$ concat (flagToList
(pathDirectories flags
) <> flagToList v
)})
207 -- | A path that can be retrieved by the @cabal path@ command.
209 = ConfigPathCacheHome
210 | ConfigPathRemoteRepoCache
213 | ConfigPathConfigFile
214 | ConfigPathInstallDir
215 deriving (Eq
, Ord
, Show, Enum
, Bounded
)
217 -- | The configuration name for this path.
218 pathName
:: ConfigPath
-> String
219 pathName ConfigPathCacheHome
= "cache-home"
220 pathName ConfigPathRemoteRepoCache
= "remote-repo-cache"
221 pathName ConfigPathLogsDir
= "logs-dir"
222 pathName ConfigPathStoreDir
= "store-dir"
223 pathName ConfigPathConfigFile
= "config-file"
224 pathName ConfigPathInstallDir
= "installdir"
226 -------------------------------------------------------------------------------
228 -------------------------------------------------------------------------------
230 -- | Entry point for the 'path' command.
231 pathAction
:: NixStyleFlags PathFlags
-> [String] -> GlobalFlags
-> IO ()
232 pathAction flags
@NixStyleFlags
{extraFlags
= pathFlags
', ..} cliTargetStrings globalFlags
= withContextAndSelectors AcceptNoTargets Nothing flags
[] globalFlags OtherCommand
$ \_ baseCtx _
-> do
234 if pathCompiler pathFlags
' == NoFlag
&& pathDirectories pathFlags
' == NoFlag
235 then -- if not a single key to query is given, query everything!
238 { pathCompiler
= Flag
True
239 , pathDirectories
= Flag
[minBound .. maxBound]
242 when (not $ null cliTargetStrings
) $
243 dieWithException verbosity CmdPathAcceptsNoTargets
244 when (buildSettingDryRun
(buildSettings baseCtx
)) $
245 dieWithException verbosity CmdPathCommandDoesn
'tSupportDryRun
247 compilerPathOutputs
<-
248 if not $ fromFlagOrDefault
False (pathCompiler pathFlags
)
251 (compiler
, _
, progDb
) <- runRebuild
(distProjectRootDirectory
. distDirLayout
$ baseCtx
) $ configureCompiler verbosity
(distDirLayout baseCtx
) (projectConfig baseCtx
)
252 compilerProg
<- requireCompilerProg verbosity compiler
253 (configuredCompilerProg
, _
) <- requireProgram verbosity compilerProg progDb
254 pure
$ Just
$ mkCompilerInfo configuredCompilerProg compiler
256 paths
<- for
(fromFlagOrDefault
[] $ pathDirectories pathFlags
) $ \p
-> do
257 t
<- getPathLocation baseCtx p
262 { pathOutputsCompilerInfo
= compilerPathOutputs
263 , pathOutputsConfigPaths
= paths
266 let output
= case fromFlagOrDefault KeyValue
(pathOutputFormat pathFlags
) of
268 Json
.encodeToString
(showAsJson pathOutputs
) <> "\n"
270 showAsKeyValuePair pathOutputs
272 putStr $ withOutputMarker verbosity output
274 verbosity
= fromFlagOrDefault normal
(configVerbosity configFlags
)
276 -- | Find the FilePath location for common configuration paths.
278 -- TODO: this should come from a common source of truth to avoid code path divergence
279 getPathLocation
:: ProjectBaseContext
-> ConfigPath
-> IO FilePath
280 getPathLocation _ ConfigPathCacheHome
=
282 getPathLocation baseCtx ConfigPathRemoteRepoCache
=
283 pure
$ buildSettingCacheDir
(buildSettings baseCtx
)
284 getPathLocation baseCtx ConfigPathLogsDir
=
285 pure
$ cabalLogsDirectory
(cabalDirLayout baseCtx
)
286 getPathLocation baseCtx ConfigPathStoreDir
=
289 (pure
<$> projectConfigStoreDir
(projectConfigShared
(projectConfig baseCtx
)))
290 getPathLocation baseCtx ConfigPathConfigFile
=
291 getConfigFilePath
(projectConfigConfigFile
(projectConfigShared
(projectConfig baseCtx
)))
292 getPathLocation baseCtx ConfigPathInstallDir
=
295 (pure
<$> cinstInstalldir
(projectConfigClientInstallFlags
$ projectConfigBuildOnly
(projectConfig baseCtx
)))
297 -- ----------------------------------------------------------------------------
298 -- Helpers for determining compiler information
299 -- ----------------------------------------------------------------------------
301 requireCompilerProg
:: Verbosity
-> Compiler
-> IO Program
302 requireCompilerProg verbosity compiler
=
303 case compilerFlavor compiler
of
304 GHC
-> pure ghcProgram
305 GHCJS
-> pure ghcjsProgram
308 "path: Unsupported compiler flavour: "
309 <> prettyShow flavour
311 -- ----------------------------------------------------------------------------
313 -- ----------------------------------------------------------------------------
315 data PathOutputs
= PathOutputs
316 { pathOutputsCompilerInfo
:: Maybe PathCompilerInfo
317 , pathOutputsConfigPaths
:: [(String, FilePath)]
319 deriving (Show, Eq
, Ord
)
321 data PathCompilerInfo
= PathCompilerInfo
322 { pathCompilerInfoFlavour
:: CompilerFlavor
323 , pathCompilerInfoId
:: CompilerId
324 , pathCompilerInfoPath
:: FilePath
326 deriving (Show, Eq
, Ord
)
328 mkCompilerInfo
:: ConfiguredProgram
-> Compiler
-> PathCompilerInfo
329 mkCompilerInfo compilerProgram compiler
=
331 { pathCompilerInfoFlavour
= compilerFlavor compiler
332 , pathCompilerInfoId
= compilerId compiler
333 , pathCompilerInfoPath
= programPath compilerProgram
336 -- ----------------------------------------------------------------------------
338 -- ----------------------------------------------------------------------------
340 showAsJson
:: PathOutputs
-> Json
.Value
341 showAsJson pathOutputs
=
345 [ "cabal-version" .= jdisplay cabalInstallVersion
348 compilerInfoJson
= case pathOutputsCompilerInfo pathOutputs
of
349 Nothing
-> Json
.object
[]
350 Just pci
-> compilerInfoToJson pci
352 pathsJson
= Json
.object
$ map (\(k
, v
) -> k
.= Json
.String v
) (pathOutputsConfigPaths pathOutputs
)
360 jdisplay
:: Pretty a
=> a
-> Json
.Value
361 jdisplay
= Json
.String . prettyShow
363 mergeJsonObjects
:: [Json
.Value
] -> Json
.Value
364 mergeJsonObjects
= Json
.object
. foldl' go
[]
366 go acc
(Json
.Object objs
) =
369 error "mergeJsonObjects: Only objects can be merged"
371 compilerInfoToJson
:: PathCompilerInfo
-> Json
.Value
372 compilerInfoToJson pci
=
376 [ "flavour" .= jdisplay
(pathCompilerInfoFlavour pci
)
377 , "id" .= jdisplay
(pathCompilerInfoId pci
)
378 , "path" .= Json
.String (pathCompilerInfoPath pci
)
382 -- ----------------------------------------------------------------------------
383 -- Key Value Pair outputs
384 -- ----------------------------------------------------------------------------
386 showAsKeyValuePair
:: PathOutputs
-> String
387 showAsKeyValuePair pathOutputs
=
389 cInfo
= case pathOutputsCompilerInfo pathOutputs
of
391 Just pci
-> compilerInfoToKeyValue pci
393 paths
= pathOutputsConfigPaths pathOutputs
395 pairs
= cInfo
<> paths
397 showPair
(k
, v
) = k
<> ": " <> v
401 xs
-> unlines $ map showPair xs
403 compilerInfoToKeyValue
:: PathCompilerInfo
-> [(String, String)]
404 compilerInfoToKeyValue pci
=
405 [ ("compiler-flavour", prettyShow
$ pathCompilerInfoFlavour pci
)
406 , ("compiler-id", prettyShow
$ pathCompilerInfoId pci
)
407 , ("compiler-path", pathCompilerInfoPath pci
)