forked from commercialhaskell/stack
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathHaddock.hs
More file actions
239 lines (227 loc) · 9.89 KB
/
Haddock.hs
File metadata and controls
239 lines (227 loc) · 9.89 KB
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
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TemplateHaskell #-}
-- | Generate haddocks
module Stack.Build.Haddock
( copyDepHaddocks
, generateLocalHaddockIndex
, generateDepsHaddockIndex
, generateSnapHaddockIndex
, shouldHaddockPackage
, shouldHaddockDeps
) where
import Control.Exception (tryJust)
import Control.Monad
import Control.Monad.Catch (MonadCatch)
import Control.Monad.IO.Class
import Control.Monad.Logger
import Control.Monad.Trans.Resource
import Control.Monad.Writer
import Data.Function
import Data.List
import Data.Maybe
import Data.Set (Set)
import qualified Data.Set as Set
import Data.Text (Text)
import qualified Data.Text as T
import Path
import Path.IO
import Prelude
import Safe (maximumMay)
import Stack.Types.Build
import Stack.GhcPkg
import Stack.Package
import Stack.Types
import System.Directory (getModificationTime)
import qualified System.FilePath as FP
import System.IO.Error (isDoesNotExistError)
import System.Process.Read
-- | Determine whether we should haddock for a package.
shouldHaddockPackage :: BuildOpts -> Set PackageName -> PackageName -> Bool
shouldHaddockPackage bopts wanted name =
if Set.member name wanted
then boptsHaddock bopts
else shouldHaddockDeps bopts
-- | Determine whether to build haddocks for dependencies.
shouldHaddockDeps :: BuildOpts -> Bool
shouldHaddockDeps bopts = fromMaybe (boptsHaddock bopts) (boptsHaddockDeps bopts)
-- | Copy dependencies' haddocks to documentation directory. This way, relative @../$pkg-$ver@
-- links work and it's easy to upload docs to a web server or otherwise view them in a
-- non-local-filesystem context. We copy instead of symlink for two reasons: (1) symlinks aren't
-- reliably supported on Windows, and (2) the filesystem containing dependencies' docs may not be
-- available where viewing the docs (e.g. if building in a Docker container).
copyDepHaddocks :: (MonadIO m, MonadLogger m, MonadThrow m, MonadCatch m, MonadBaseControl IO m)
=> EnvOverride
-> WhichCompiler
-> BaseConfigOpts
-> [Path Abs Dir]
-> PackageIdentifier
-> Set (Path Abs Dir)
-> m ()
copyDepHaddocks envOverride wc bco pkgDbs pkgId extraDestDirs = do
mpkgHtmlDir <- findGhcPkgHaddockHtml envOverride wc pkgDbs $ packageIdentifierString pkgId
case mpkgHtmlDir of
Nothing -> return ()
Just (_pkgId, pkgHtmlDir) -> do
depGhcIds <- findGhcPkgDepends envOverride wc pkgDbs $ packageIdentifierString pkgId
forM_ depGhcIds $ copyDepWhenNeeded pkgHtmlDir
where
copyDepWhenNeeded pkgHtmlDir depGhcId = do
mDepOrigDir <- findGhcPkgHaddockHtml envOverride wc pkgDbs $ ghcPkgIdString depGhcId
case mDepOrigDir of
Nothing -> return ()
Just (depId, depOrigDir) -> do
let extraDestDirs' =
-- Parent test ensures we don't try to copy docs to global locations
if bcoSnapInstallRoot bco `isParentOf` pkgHtmlDir ||
bcoLocalInstallRoot bco `isParentOf` pkgHtmlDir
then Set.insert (parent pkgHtmlDir) extraDestDirs
else extraDestDirs
copyWhenNeeded extraDestDirs' depId depOrigDir
copyWhenNeeded destDirs depId depOrigDir = do
depRelDir <- parseRelDir (packageIdentifierString depId)
copied <- forM (Set.toList destDirs) $ \destDir -> do
let depCopyDir = destDir </> depRelDir
if depCopyDir == depOrigDir
then return False
else do
needCopy <- getNeedCopy depOrigDir depCopyDir
when needCopy $ doCopy depOrigDir depCopyDir
return needCopy
when (or copied) $
copyDepHaddocks envOverride wc bco pkgDbs depId destDirs
getNeedCopy depOrigDir depCopyDir = do
let depOrigIndex = haddockIndexFile depOrigDir
depCopyIndex = haddockIndexFile depCopyDir
depOrigExists <- fileExists depOrigIndex
depCopyExists <- fileExists depCopyIndex
case (depOrigExists, depCopyExists) of
(False, _) -> return False
(True, False) -> return True
(True, True) -> do
copyMod <- liftIO $ getModificationTime (toFilePath depCopyIndex)
origMod <- liftIO $ getModificationTime (toFilePath depOrigIndex)
return (copyMod <= origMod)
doCopy depOrigDir depCopyDir = do
removeTreeIfExists depCopyDir
createTree depCopyDir
copyDirectoryRecursive depOrigDir depCopyDir
-- | Generate Haddock index and contents for local packages.
generateLocalHaddockIndex
:: (MonadIO m, MonadCatch m, MonadThrow m, MonadLogger m, MonadBaseControl IO m)
=> EnvOverride -> WhichCompiler -> BaseConfigOpts -> [LocalPackage] -> m ()
generateLocalHaddockIndex envOverride wc bco locals = do
let packageIDs =
map
(\LocalPackage{lpPackage = Package{..}} ->
PackageIdentifier packageName packageVersion)
locals
generateHaddockIndex
"local packages"
envOverride
wc
packageIDs
"."
(localDocDir bco)
-- | Generate Haddock index and contents for local packages and their dependencies.
generateDepsHaddockIndex
:: (MonadIO m, MonadCatch m, MonadThrow m, MonadLogger m, MonadBaseControl IO m)
=> EnvOverride -> WhichCompiler -> BaseConfigOpts -> [LocalPackage] -> m ()
generateDepsHaddockIndex envOverride wc bco locals = do
depSets <-
mapM
(\LocalPackage{lpPackage = Package{..}} ->
findTransitiveGhcPkgDepends
envOverride
wc
[bcoSnapDB bco, bcoLocalDB bco]
(PackageIdentifier packageName packageVersion))
locals
generateHaddockIndex
"local packages and dependencies"
envOverride
wc
(Set.toList (Set.unions depSets))
".."
(localDocDir bco </> $(mkRelDir "all"))
-- | Generate Haddock index and contents for all snapshot packages.
generateSnapHaddockIndex
:: (MonadIO m, MonadCatch m, MonadThrow m, MonadLogger m, MonadBaseControl IO m)
=> EnvOverride -> WhichCompiler -> BaseConfigOpts -> Path Abs Dir -> m ()
generateSnapHaddockIndex envOverride wc bco globalDB = do
pkgIds <- listGhcPkgDbs envOverride wc [globalDB, bcoSnapDB bco]
generateHaddockIndex
"snapshot packages"
envOverride
wc
pkgIds
"."
(snapDocDir bco)
-- | Generate Haddock index and contents for specified packages.
generateHaddockIndex
:: (MonadIO m, MonadCatch m, MonadThrow m, MonadLogger m, MonadBaseControl IO m)
=> Text
-> EnvOverride
-> WhichCompiler
-> [PackageIdentifier]
-> FilePath
-> Path Abs Dir
-> m ()
generateHaddockIndex descr envOverride wc packageIDs docRelDir destDir = do
createTree destDir
interfaceOpts <- liftIO $ fmap catMaybes (mapM toInterfaceOpt packageIDs)
case maximumMay (map snd interfaceOpts) of
Nothing -> return ()
Just maxInterfaceModTime -> do
eindexModTime <-
liftIO $
tryJust (guard . isDoesNotExistError) $
getModificationTime (toFilePath (haddockIndexFile destDir))
let needUpdate =
case eindexModTime of
Left _ -> True
Right indexModTime ->
indexModTime < maxInterfaceModTime
when
needUpdate $
do $logInfo
("Updating Haddock index for " <> descr <> " in\n" <>
T.pack (toFilePath (haddockIndexFile destDir)))
readProcessNull
(Just destDir)
envOverride
(haddockExeName wc)
(["--gen-contents", "--gen-index"] ++ concatMap fst interfaceOpts)
where
toInterfaceOpt pid@(PackageIdentifier name _) = do
let interfaceRelFile =
docRelDir FP.</> packageIdentifierString pid FP.</>
packageNameString name FP.<.>
"haddock"
interfaceAbsFile = toFilePath destDir FP.</> interfaceRelFile
einterfaceModTime <-
tryJust (guard . isDoesNotExistError) $
getModificationTime interfaceAbsFile
return $
case einterfaceModTime of
Left _ -> Nothing
Right interfaceModTime ->
Just
( [ "-i"
, concat
[ docRelDir FP.</> packageIdentifierString pid
, ","
, interfaceRelFile]]
, interfaceModTime)
-- | Path of haddock index file.
haddockIndexFile :: Path Abs Dir -> Path Abs File
haddockIndexFile destDir = destDir </> $(mkRelFile "index.html")
-- | Path of local packages documentation directory.
localDocDir :: BaseConfigOpts -> Path Abs Dir
localDocDir bco = bcoLocalInstallRoot bco </> docDirSuffix
-- | Path of snapshot packages documentation directory.
snapDocDir :: BaseConfigOpts -> Path Abs Dir
snapDocDir bco = bcoSnapInstallRoot bco </> docDirSuffix