-
Notifications
You must be signed in to change notification settings - Fork 698
/
CmdHaddockProject.hs
444 lines (410 loc) · 16 KB
/
CmdHaddockProject.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
module Distribution.Client.CmdHaddockProject
( haddockProjectCommand
, haddockProjectAction
) where
import Control.Monad (mapM)
import Distribution.Client.Compat.Prelude hiding (get)
import Prelude ()
import qualified Distribution.Client.CmdBuild as CmdBuild
import qualified Distribution.Client.CmdHaddock as CmdHaddock
import Distribution.Client.DistDirLayout
( CabalDirLayout (..)
, StoreDirLayout (..)
, distBuildDirectory
)
import Distribution.Client.InstallPlan (foldPlanPackage)
import qualified Distribution.Client.InstallPlan as InstallPlan
import qualified Distribution.Client.NixStyleOptions as NixStyleOptions
import Distribution.Client.ProjectOrchestration
( AvailableTarget (..)
, AvailableTargetStatus (..)
, CurrentCommand (..)
, ProjectBaseContext (..)
, ProjectBuildContext (..)
, TargetSelector (..)
, pruneInstallPlanToTargets
, resolveTargets
, runProjectPreBuildPhase
, selectComponentTargetBasic
)
import Distribution.Client.ProjectPlanning
( ElaboratedConfiguredPackage (..)
, ElaboratedInstallPlan
, ElaboratedSharedConfig (..)
, TargetAction (..)
)
import Distribution.Client.ProjectPlanning.Types
( elabDistDirParams
)
import Distribution.Client.ScriptUtils
( AcceptNoTargets (..)
, TargetContext (..)
, updateContextAndWriteProjectFile
, withContextAndSelectors
)
import Distribution.Client.Setup
( CommonSetupFlags (setupVerbosity)
, ConfigFlags (..)
, GlobalFlags (..)
)
import Distribution.Client.TargetProblem (TargetProblem (..))
import Distribution.Simple.BuildPaths
( haddockBenchmarkDirPath
, haddockDirName
, haddockLibraryDirPath
, haddockLibraryPath
, haddockPath
, haddockTestDirPath
)
import Distribution.Simple.Command
( CommandUI (..)
)
import Distribution.Simple.Flag
( Flag (..)
, fromFlag
, fromFlagOrDefault
)
import Distribution.Simple.Haddock (createHaddockIndex)
import Distribution.Simple.InstallDirs
( toPathTemplate
)
import Distribution.Simple.Program.Builtin
( haddockProgram
)
import Distribution.Simple.Program.Db
( addKnownProgram
, reconfigurePrograms
, requireProgramVersion
)
import Distribution.Simple.Setup
( HaddockFlags (..)
, HaddockProjectFlags (..)
, HaddockTarget (..)
, Visibility (..)
, defaultHaddockFlags
, haddockProjectCommand
)
import Distribution.Simple.Utils
( copyDirectoryRecursive
, createDirectoryIfMissingVerbose
, dieWithException
, info
, warn
)
import Distribution.Types.InstalledPackageInfo (InstalledPackageInfo (..))
import Distribution.Types.PackageDescription (PackageDescription (benchmarks, subLibraries, testSuites))
import Distribution.Types.PackageId (pkgName)
import Distribution.Types.PackageName (unPackageName)
import Distribution.Types.UnitId (unUnitId)
import Distribution.Types.Version (mkVersion)
import Distribution.Types.VersionRange (orLaterVersion)
import Distribution.Verbosity as Verbosity
( normal
)
import Distribution.Client.Errors
import System.Directory (doesDirectoryExist, doesFileExist)
import System.FilePath (normalise, takeDirectory, (</>))
haddockProjectAction :: HaddockProjectFlags -> [String] -> GlobalFlags -> IO ()
haddockProjectAction flags _extraArgs globalFlags = do
-- create destination directory if it does not exist
let outputDir = normalise $ fromFlag (haddockProjectDir flags)
createDirectoryIfMissingVerbose verbosity True outputDir
warn verbosity "haddock-project command is experimental, it might break in the future"
--
-- Construct the build plan and infer the list of packages which haddocks
-- we need.
--
withContextAndSelectors
RejectNoTargets
Nothing
(commandDefaultFlags CmdBuild.buildCommand)
["all"]
globalFlags
HaddockCommand
$ \targetCtx ctx targetSelectors -> do
baseCtx <- case targetCtx of
ProjectContext -> return ctx
GlobalContext -> return ctx
ScriptContext path exemeta -> updateContextAndWriteProjectFile ctx path exemeta
let distLayout = distDirLayout baseCtx
cabalLayout = cabalDirLayout baseCtx
buildCtx <-
runProjectPreBuildPhase verbosity baseCtx $ \elaboratedPlan -> do
-- Interpret the targets on the command line as build targets
-- (as opposed to say repl or haddock targets).
targets <-
either reportTargetProblems return $
resolveTargets
selectPackageTargets
selectComponentTargetBasic
elaboratedPlan
Nothing
targetSelectors
let elaboratedPlan' =
pruneInstallPlanToTargets
TargetActionBuild
targets
elaboratedPlan
return (elaboratedPlan', targets)
let elaboratedPlan :: ElaboratedInstallPlan
elaboratedPlan = elaboratedPlanOriginal buildCtx
sharedConfig :: ElaboratedSharedConfig
sharedConfig = elaboratedShared buildCtx
pkgs :: [Either InstalledPackageInfo ElaboratedConfiguredPackage]
pkgs = matchingPackages elaboratedPlan
progs <-
reconfigurePrograms
verbosity
(haddockProjectProgramPaths flags)
(haddockProjectProgramArgs flags)
-- we need to insert 'haddockProgram' before we reconfigure it,
-- otherwise 'set
. addKnownProgram haddockProgram
. pkgConfigCompilerProgs
$ sharedConfig
let sharedConfig' = sharedConfig{pkgConfigCompilerProgs = progs}
_ <-
requireProgramVersion
verbosity
haddockProgram
(orLaterVersion (mkVersion [2, 26, 1]))
progs
--
-- Build project; we need to build dependencies.
-- Issue #8958.
--
when localStyle $
CmdBuild.buildAction
(commandDefaultFlags CmdBuild.buildCommand)
["all"]
globalFlags
--
-- Build haddocks of each components
--
CmdHaddock.haddockAction
nixFlags
["all"]
globalFlags
--
-- Copy haddocks to the destination folder
--
packageInfos <- fmap (nub . concat) $ for pkgs $ \pkg ->
case pkg of
Left package | localStyle -> do
let packageName = unPackageName (pkgName $ sourcePackageId package)
destDir = outputDir </> packageName
fmap catMaybes $ for (haddockInterfaces package) $ \interfacePath -> do
let docDir = takeDirectory interfacePath
a <- doesFileExist interfacePath
case a of
True -> do
copyDirectoryRecursive verbosity docDir destDir
return $ Just $ Right (packageName, interfacePath, Hidden)
False -> return Nothing
Left _ -> return []
Right package ->
case elabLocalToProject package of
True -> do
let distDirParams = elabDistDirParams sharedConfig' package
pkg_descr = elabPkgDescription package
packageName = pkgName $ elabPkgSourceId package
unitId = elabUnitId package
packageDir = haddockDirName ForDevelopment pkg_descr
destDir = outputDir </> packageDir
interfacePath = destDir </> haddockPath pkg_descr
buildDir = distBuildDirectory distLayout distDirParams
docDir =
buildDir
</> "doc"
</> "html"
</> packageDir
a <- doesDirectoryExist docDir
if a
then do
copyDirectoryRecursive verbosity docDir destDir
let infos :: [(String, FilePath, Visibility)]
infos =
(unPackageName packageName, interfacePath, Visible)
: [ (sublibDirPath, sublibInterfacePath, Visible)
| lib <- subLibraries pkg_descr
, let sublibDirPath = haddockLibraryDirPath ForDevelopment pkg_descr lib
sublibInterfacePath =
outputDir
</> sublibDirPath
</> haddockLibraryPath pkg_descr lib
]
++ [ (testPath, testInterfacePath, Visible)
| test <- testSuites pkg_descr
, let testPath = haddockTestDirPath ForDevelopment pkg_descr test
testInterfacePath =
outputDir
</> testPath
</> haddockPath pkg_descr
]
++ [ (benchPath, benchInterfacePath, Visible)
| bench <- benchmarks pkg_descr
, let benchPath = haddockBenchmarkDirPath ForDevelopment pkg_descr bench
benchInterfacePath =
outputDir
</> benchPath
</> haddockPath pkg_descr
]
infos' <-
mapM
( \x@(_, path, _) -> do
e <- doesFileExist path
return $
if e
then Right x
else Left path
)
infos
return infos'
else do
warn
verbosity
( "haddocks of "
++ unUnitId unitId
++ " not found in the store"
)
return []
False
| not localStyle ->
return []
False -> do
let pkg_descr = elabPkgDescription package
unitId = unUnitId (elabUnitId package)
packageDir =
storePackageDirectory
(cabalStoreDirLayout cabalLayout)
(pkgConfigCompiler sharedConfig')
(elabUnitId package)
-- TODO: use `InstallDirTemplates`
docDir = packageDir </> "share" </> "doc" </> "html"
destDir = outputDir </> haddockDirName ForDevelopment pkg_descr
interfacePath = destDir </> haddockPath pkg_descr
a <- doesDirectoryExist docDir
case a of
True -> do
copyDirectoryRecursive verbosity docDir destDir
-- non local packages will be hidden in haddock's
-- generated contents page
return [Right (unitId, interfacePath, Hidden)]
False -> do
return [Left unitId]
--
-- generate index, content, etc.
--
let (missingHaddocks, packageInfos') = partitionEithers packageInfos
when (not (null missingHaddocks)) $ do
warn verbosity "missing haddocks for some packages from the store"
-- Show the package list if `-v1` is passed; it's usually a long list.
-- One needs to add `package` stantza in `cabal.project` file for
-- `cabal` to include a version which has haddocks (or set
-- `documentation: True` in the global config).
info verbosity (intercalate "\n" missingHaddocks)
let flags' =
flags
{ haddockProjectDir = Flag outputDir
, haddockProjectInterfaces =
Flag
[ ( interfacePath
, Just url
, Just url
, visibility
)
| (url, interfacePath, visibility) <- packageInfos'
]
, haddockProjectUseUnicode = NoFlag
}
createHaddockIndex
verbosity
(pkgConfigCompilerProgs sharedConfig')
(pkgConfigCompiler sharedConfig')
(pkgConfigPlatform sharedConfig')
Nothing
flags'
where
-- build all packages with appropriate haddock flags
commonFlags = haddockProjectCommonFlags flags
verbosity = fromFlagOrDefault normal (setupVerbosity commonFlags)
haddockFlags =
defaultHaddockFlags
{ haddockCommonFlags = commonFlags
, haddockHtml = Flag True
, -- one can either use `--haddock-base-url` or
-- `--haddock-html-location`.
haddockBaseUrl =
if localStyle
then Flag ".."
else NoFlag
, haddockProgramPaths = haddockProjectProgramPaths flags
, haddockProgramArgs = haddockProjectProgramArgs flags
, haddockHtmlLocation =
if fromFlagOrDefault False (haddockProjectHackage flags)
then Flag "https://hackage.haskell.org/package/$pkg-$version/docs"
else haddockProjectHtmlLocation flags
, haddockHoogle = haddockProjectHoogle flags
, haddockExecutables = haddockProjectExecutables flags
, haddockTestSuites = haddockProjectTestSuites flags
, haddockBenchmarks = haddockProjectBenchmarks flags
, haddockForeignLibs = haddockProjectForeignLibs flags
, haddockInternal = haddockProjectInternal flags
, haddockCss = haddockProjectCss flags
, haddockLinkedSource = Flag True
, haddockQuickJump = Flag True
, haddockHscolourCss = haddockProjectHscolourCss flags
, haddockContents =
if localStyle
then Flag (toPathTemplate "../index.html")
else NoFlag
, haddockIndex =
if localStyle
then Flag (toPathTemplate "../doc-index.html")
else NoFlag
, haddockResourcesDir = haddockProjectResourcesDir flags
, haddockUseUnicode = haddockProjectUseUnicode flags
-- NOTE: we don't pass `haddockOutputDir`. If we do, we'll need to
-- make sure `InstalledPackageInfo` contains the right path to
-- haddock interfaces. Instead we build documentation inside
-- `dist-newstyle` directory and copy it to the output directory.
}
nixFlags =
(commandDefaultFlags CmdHaddock.haddockCommand)
{ NixStyleOptions.haddockFlags = haddockFlags
, NixStyleOptions.configFlags =
(NixStyleOptions.configFlags (commandDefaultFlags CmdBuild.buildCommand))
{ configCommonFlags = commonFlags
}
}
-- Build a self contained directory which contains haddocks of all
-- transitive dependencies; or depend on `--haddocks-html-location` to
-- provide location of the documentation of dependencies.
localStyle =
let hackage = fromFlagOrDefault False (haddockProjectHackage flags)
location = fromFlagOrDefault False (const True <$> haddockProjectHtmlLocation flags)
in not hackage && not location
reportTargetProblems :: Show x => [x] -> IO a
reportTargetProblems =
dieWithException verbosity . CmdHaddockReportTargetProblems . map show
-- TODO: this is just a sketch
selectPackageTargets
:: TargetSelector
-> [AvailableTarget k]
-> Either (TargetProblem ()) [k]
selectPackageTargets _ ts =
Right $
mapMaybe
( \t -> case availableTargetStatus t of
TargetBuildable k _
| availableTargetLocalToProject t ->
Just k
_ -> Nothing
)
ts
matchingPackages
:: ElaboratedInstallPlan
-> [Either InstalledPackageInfo ElaboratedConfiguredPackage]
matchingPackages =
fmap (foldPlanPackage Left Right)
. InstallPlan.toList