1 {-# LANGUAGE OverloadedStrings #-}
3 -----------------------------------------------------------------------------
5 -----------------------------------------------------------------------------
8 -- Module : Distribution.PackageDescription.PrettyPrint
9 -- Copyright : Jürgen Nicklisch-Franken 2010
12 -- Maintainer : cabal-devel@haskell.org
13 -- Stability : provisional
14 -- Portability : portable
16 -- Pretty printing for cabal files
17 module Distribution
.PackageDescription
.PrettyPrint
18 ( -- * Generic package descriptions
19 writeGenericPackageDescription
20 , showGenericPackageDescription
21 , ppGenericPackageDescription
23 -- * Package descriptions
24 , writePackageDescription
25 , showPackageDescription
27 -- ** Supplementary build information
28 , writeHookedBuildInfo
32 import Distribution
.Compat
.Prelude
35 import Distribution
.CabalSpecVersion
36 import Distribution
.Compat
.Lens
37 import Distribution
.FieldGrammar
(PrettyFieldGrammar
', prettyFieldGrammar
)
38 import Distribution
.Fields
.Pretty
39 import Distribution
.PackageDescription
40 import Distribution
.PackageDescription
.Configuration
(transformAllBuildInfos
)
41 import Distribution
.PackageDescription
.FieldGrammar
42 ( benchmarkFieldGrammar
43 , buildInfoFieldGrammar
44 , executableFieldGrammar
46 , foreignLibFieldGrammar
48 , packageDescriptionFieldGrammar
49 , setupBInfoFieldGrammar
50 , sourceRepoFieldGrammar
51 , testSuiteFieldGrammar
53 import Distribution
.Pretty
54 import Distribution
.Utils
.Generic
(writeFileAtomic
, writeUTF8File
)
56 import qualified Distribution
.PackageDescription
.FieldGrammar
as FG
57 import qualified Distribution
.Types
.BuildInfo
.Lens
as L
58 import qualified Distribution
.Types
.SetupBuildInfo
.Lens
as L
60 import Text
.PrettyPrint
(Doc
, char
, hsep
, parens
, text
)
62 import qualified Data
.ByteString
.Lazy
.Char8
as BS
.Char8
63 import qualified Distribution
.Compat
.NonEmptySet
as NES
65 -- | Writes a .cabal file from a generic package description
66 writeGenericPackageDescription
:: FilePath -> GenericPackageDescription
-> IO ()
67 writeGenericPackageDescription fpath pkg
= writeUTF8File fpath
(showGenericPackageDescription pkg
)
69 -- | Writes a generic package description to a string
70 showGenericPackageDescription
:: GenericPackageDescription
-> String
71 showGenericPackageDescription gpd
= showFields
(const NoComment
) $ ppGenericPackageDescription v gpd
73 v
= specVersion
$ packageDescription gpd
75 -- | Convert a generic package description to 'PrettyField's.
76 ppGenericPackageDescription
:: CabalSpecVersion
-> GenericPackageDescription
-> [PrettyField
()]
77 ppGenericPackageDescription v gpd0
=
79 [ ppPackageDescription v
(packageDescription gpd
)
80 , ppSetupBInfo v
(setupBuildInfo
(packageDescription gpd
))
81 , ppGenPackageFlags v
(genPackageFlags gpd
)
82 , ppCondLibrary v
(condLibrary gpd
)
83 , ppCondSubLibraries v
(condSubLibraries gpd
)
84 , ppCondForeignLibs v
(condForeignLibs gpd
)
85 , ppCondExecutables v
(condExecutables gpd
)
86 , ppCondTestSuites v
(condTestSuites gpd
)
87 , ppCondBenchmarks v
(condBenchmarks gpd
)
90 gpd
= preProcessInternalDeps
(specVersion
(packageDescription gpd0
)) gpd0
92 ppPackageDescription
:: CabalSpecVersion
-> PackageDescription
-> [PrettyField
()]
93 ppPackageDescription v pd
=
94 prettyFieldGrammar v packageDescriptionFieldGrammar pd
95 ++ ppSourceRepos v
(sourceRepos pd
)
97 ppSourceRepos
:: CabalSpecVersion
-> [SourceRepo
] -> [PrettyField
()]
98 ppSourceRepos
= map . ppSourceRepo
100 ppSourceRepo
:: CabalSpecVersion
-> SourceRepo
-> PrettyField
()
101 ppSourceRepo v repo
=
102 PrettySection
() "source-repository" [pretty kind
] $
103 prettyFieldGrammar v
(sourceRepoFieldGrammar kind
) repo
107 ppSetupBInfo
:: CabalSpecVersion
-> Maybe SetupBuildInfo
-> [PrettyField
()]
108 ppSetupBInfo _ Nothing
= mempty
109 ppSetupBInfo v
(Just sbi
)
110 | defaultSetupDepends sbi
= mempty
113 PrettySection
() "custom-setup" [] $
114 prettyFieldGrammar v
(setupBInfoFieldGrammar
False) sbi
116 ppGenPackageFlags
:: CabalSpecVersion
-> [PackageFlag
] -> [PrettyField
()]
117 ppGenPackageFlags
= map . ppFlag
119 ppFlag
:: CabalSpecVersion
-> PackageFlag
-> PrettyField
()
120 ppFlag v flag
@(MkPackageFlag name _ _ _
) =
121 PrettySection
() "flag" [ppFlagName name
] $
122 prettyFieldGrammar v
(flagFieldGrammar name
) flag
124 ppCondTree2
:: CabalSpecVersion
-> PrettyFieldGrammar
' s
-> CondTree ConfVar
[Dependency
] s
-> [PrettyField
()]
125 ppCondTree2 v grammar
= go
127 -- TODO: recognise elif opportunities
128 go
(CondNode it _ ifs
) =
129 prettyFieldGrammar v grammar it
130 ++ concatMap ppIf ifs
132 ppIf
(CondBranch c thenTree Nothing
)
133 -- | isEmpty thenDoc = mempty
134 |
otherwise = [ppIfCondition c thenDoc
]
136 thenDoc
= go thenTree
137 ppIf
(CondBranch c thenTree
(Just elseTree
)) =
139 [ ppIfCondition c
(go thenTree
)
140 , PrettySection
() "else" [] (go elseTree
)
143 ppCondLibrary
:: CabalSpecVersion
-> Maybe (CondTree ConfVar
[Dependency
] Library
) -> [PrettyField
()]
144 ppCondLibrary _ Nothing
= mempty
145 ppCondLibrary v
(Just condTree
) =
147 PrettySection
() "library" [] $
148 ppCondTree2 v
(libraryFieldGrammar LMainLibName
) condTree
150 ppCondSubLibraries
:: CabalSpecVersion
-> [(UnqualComponentName
, CondTree ConfVar
[Dependency
] Library
)] -> [PrettyField
()]
151 ppCondSubLibraries v libs
=
152 [ PrettySection
() "library" [pretty n
] $
153 ppCondTree2 v
(libraryFieldGrammar
$ LSubLibName n
) condTree
154 |
(n
, condTree
) <- libs
157 ppCondForeignLibs
:: CabalSpecVersion
-> [(UnqualComponentName
, CondTree ConfVar
[Dependency
] ForeignLib
)] -> [PrettyField
()]
158 ppCondForeignLibs v flibs
=
159 [ PrettySection
() "foreign-library" [pretty n
] $
160 ppCondTree2 v
(foreignLibFieldGrammar n
) condTree
161 |
(n
, condTree
) <- flibs
164 ppCondExecutables
:: CabalSpecVersion
-> [(UnqualComponentName
, CondTree ConfVar
[Dependency
] Executable
)] -> [PrettyField
()]
165 ppCondExecutables v exes
=
166 [ PrettySection
() "executable" [pretty n
] $
167 ppCondTree2 v
(executableFieldGrammar n
) condTree
168 |
(n
, condTree
) <- exes
171 ppCondTestSuites
:: CabalSpecVersion
-> [(UnqualComponentName
, CondTree ConfVar
[Dependency
] TestSuite
)] -> [PrettyField
()]
172 ppCondTestSuites v suites
=
173 [ PrettySection
() "test-suite" [pretty n
] $
174 ppCondTree2 v testSuiteFieldGrammar
(fmap FG
.unvalidateTestSuite condTree
)
175 |
(n
, condTree
) <- suites
178 ppCondBenchmarks
:: CabalSpecVersion
-> [(UnqualComponentName
, CondTree ConfVar
[Dependency
] Benchmark
)] -> [PrettyField
()]
179 ppCondBenchmarks v suites
=
180 [ PrettySection
() "benchmark" [pretty n
] $
181 ppCondTree2 v benchmarkFieldGrammar
(fmap FG
.unvalidateBenchmark condTree
)
182 |
(n
, condTree
) <- suites
185 ppCondition
:: Condition ConfVar
-> Doc
186 ppCondition
(Var x
) = ppConfVar x
187 ppCondition
(Lit b
) = text
(show b
)
188 ppCondition
(CNot c
) = char
'!' <<>> (ppCondition c
)
189 ppCondition
(COr c1 c2
) =
197 ppCondition
(CAnd c1 c2
) =
205 ppConfVar
:: ConfVar
-> Doc
206 ppConfVar
(OS os
) = text
"os" <<>> parens
(pretty os
)
207 ppConfVar
(Arch arch
) = text
"arch" <<>> parens
(pretty arch
)
208 ppConfVar
(PackageFlag name
) = text
"flag" <<>> parens
(ppFlagName name
)
209 ppConfVar
(Impl c v
) = text
"impl" <<>> parens
(pretty c
<+> pretty v
)
211 ppFlagName
:: FlagName
-> Doc
212 ppFlagName
= text
. unFlagName
214 ppIfCondition
:: Condition ConfVar
-> [PrettyField
()] -> PrettyField
()
215 ppIfCondition c
= PrettySection
() "if" [ppCondition c
]
218 writePackageDescription
:: FilePath -> PackageDescription
-> IO ()
219 writePackageDescription fpath pkg
= writeUTF8File fpath
(showPackageDescription pkg
)
221 -- TODO: make this use section syntax
222 -- add equivalent for GenericPackageDescription
225 showPackageDescription
:: PackageDescription
-> String
226 showPackageDescription
= showGenericPackageDescription
. pdToGpd
228 pdToGpd
:: PackageDescription
-> GenericPackageDescription
230 GenericPackageDescription
231 { packageDescription
= pd
232 , gpdScannedVersion
= Nothing
233 , genPackageFlags
= []
234 , condLibrary
= mkCondTree
<$> library pd
235 , condSubLibraries
= mkCondTreeL
<$> subLibraries pd
236 , condForeignLibs
= mkCondTree
' foreignLibName
<$> foreignLibs pd
237 , condExecutables
= mkCondTree
' exeName
<$> executables pd
238 , condTestSuites
= mkCondTree
' testName
<$> testSuites pd
239 , condBenchmarks
= mkCondTree
' benchmarkName
<$> benchmarks pd
242 -- We set CondTree's [Dependency] to an empty list, as it
243 -- is not pretty printed anyway.
244 mkCondTree x
= CondNode x
[] []
245 mkCondTreeL l
= (fromMaybe (mkUnqualComponentName
"") (libraryNameString
(libName l
)), CondNode l
[] [])
248 :: (a
-> UnqualComponentName
)
250 -> (UnqualComponentName
, CondTree ConfVar
[Dependency
] a
)
251 mkCondTree
' f x
= (f x
, CondNode x
[] [])
253 -------------------------------------------------------------------------------
255 -------------------------------------------------------------------------------
257 -- See Note [Dependencies on sublibraries] in Distribution.PackageDescription.Parsec
259 preProcessInternalDeps
:: CabalSpecVersion
-> GenericPackageDescription
-> GenericPackageDescription
260 preProcessInternalDeps specVer gpd
261 | specVer
>= CabalSpecV3_4
= gpd
262 |
otherwise = transformAllBuildInfos transformBI transformSBI gpd
264 transformBI
:: BuildInfo
-> BuildInfo
266 over L
.targetBuildDepends
(concatMap transformD
)
267 . over L
.mixins
(map transformM
)
269 transformSBI
:: SetupBuildInfo
-> SetupBuildInfo
270 transformSBI
= over L
.setupDepends
(concatMap transformD
)
272 transformD
:: Dependency
-> [Dependency
]
273 transformD
(Dependency pn vr ln
)
275 if LMainLibName `NES
.member` ln
276 then Dependency thisPn vr mainLibSet
: sublibs
280 [ Dependency
(unqualComponentNameToPackageName uqn
) vr mainLibSet
281 | LSubLibName uqn
<- NES
.toList ln
285 transformM
:: Mixin
-> Mixin
286 transformM
(Mixin pn
(LSubLibName uqn
) inc
)
288 mkMixin
(unqualComponentNameToPackageName uqn
) LMainLibName inc
291 thisPn
:: PackageName
292 thisPn
= pkgName
(package
(packageDescription gpd
))
294 -------------------------------------------------------------------------------
296 -------------------------------------------------------------------------------
299 writeHookedBuildInfo
:: FilePath -> HookedBuildInfo
-> IO ()
300 writeHookedBuildInfo fpath
=
301 writeFileAtomic fpath
303 . showHookedBuildInfo
306 showHookedBuildInfo
:: HookedBuildInfo
-> String
307 showHookedBuildInfo
(mb_lib_bi
, ex_bis
) =
308 showFields
(const NoComment
) $
309 maybe mempty
(prettyFieldGrammar cabalSpecLatest buildInfoFieldGrammar
) mb_lib_bi
310 ++ [ PrettySection
() "executable:" [pretty name
] $
311 prettyFieldGrammar cabalSpecLatest buildInfoFieldGrammar bi
312 |
(name
, bi
) <- ex_bis