Hadrian: handle Haddock's resource directory
[ghc.git] / hadrian / src / Rules / Documentation.hs
1 module Rules.Documentation (
2 -- * Rules
3 buildPackageDocumentation, documentationRules,
4
5 -- * Utilities
6 haddockDependencies
7 ) where
8
9 import Hadrian.BuildPath
10 import Hadrian.Haskell.Cabal
11 import Hadrian.Haskell.Cabal.Type
12
13 import Rules.Generate (ghcPrimDependencies)
14 import Base
15 import Context
16 import Expression (getContextData, interpretInContext, (?), package)
17 import Flavour
18 import Oracles.ModuleFiles
19 import Packages
20 import Settings
21 import Target
22 import Utilities
23
24 import Data.List (union)
25 import qualified Text.Parsec as Parsec
26
27 docRoot :: FilePath
28 docRoot = "docs"
29
30 htmlRoot :: FilePath
31 htmlRoot = docRoot -/- "html"
32
33 pdfRoot :: FilePath
34 pdfRoot = docRoot -/- "pdfs"
35
36 archiveRoot :: FilePath
37 archiveRoot = docRoot -/- "archives"
38
39 manPageBuildPath :: FilePath
40 manPageBuildPath = "docs/users_guide/build-man/ghc.1"
41
42 -- TODO: Get rid of this hack.
43 docContext :: Context
44 docContext = vanillaContext Stage2 (library "Documentation" "docs")
45
46 docPaths :: [FilePath]
47 docPaths = ["libraries", "users_guide", "Haddock"]
48
49 pathPdf :: FilePath -> FilePath
50 pathPdf path = pdfRoot -/- path <.> ".pdf"
51
52 pathIndex :: FilePath -> FilePath
53 pathIndex path = htmlRoot -/- path -/- "index.html"
54
55 pathArchive :: FilePath -> FilePath
56 pathArchive path = archiveRoot -/- path <.> "html.tar.xz"
57
58 -- TODO: Get rid of this hack.
59 pathPath :: FilePath -> FilePath
60 pathPath "users_guide" = "docs/users_guide"
61 pathPath "Haddock" = "utils/haddock/doc"
62 pathPath _ = ""
63
64 -- | Build all documentation
65 documentationRules :: Rules ()
66 documentationRules = do
67 haddockResources
68
69 buildDocumentationArchives
70 buildHtmlDocumentation
71 buildManPage
72 buildPdfDocumentation
73
74 -- a phony rule that runs Haddock for "Haskell Hierarchical Libraries" and
75 -- the "GHC-API"
76 "docs-haddock" ~> do
77 root <- buildRoot
78 need [ root -/- pathIndex "libraries" ]
79
80 -- a phony rule that runs Haddock, builds the User's guide, builds
81 -- Haddock's manual, and builds man pages
82 "docs" ~> do
83 root <- buildRoot
84 let html = htmlRoot -/- "index.html" -- also implies "docs-haddock"
85 archives = map pathArchive docPaths
86 pdfs = map pathPdf $ docPaths \\ ["libraries"]
87 need $ map (root -/-) $ [html] ++ archives ++ pdfs ++ [manPageBuildPath]
88
89 ------------------------------------- HTML -------------------------------------
90
91 -- | Build rules for HTML documentation.
92 buildHtmlDocumentation :: Rules ()
93 buildHtmlDocumentation = do
94 mapM_ buildSphinxHtml $ docPaths \\ ["libraries"]
95 buildLibraryDocumentation
96 root <- buildRootRules
97
98 root -/- htmlRoot -/- "index.html" %> \file -> do
99 need $ map ((root -/-) . pathIndex) docPaths
100 copyFileUntracked "docs/index.html" file
101
102 -- | Compile a Sphinx ReStructured Text package to HTML.
103 buildSphinxHtml :: FilePath -> Rules ()
104 buildSphinxHtml path = do
105 root <- buildRootRules
106 root -/- htmlRoot -/- path -/- "index.html" %> \file -> do
107 let dest = takeDirectory file
108 build $ target docContext (Sphinx Html) [pathPath path] [dest]
109
110 ------------------------------------ Haddock -----------------------------------
111
112 -- | Copy resources into the @lib@ directory
113 haddockResources :: Rules ()
114 haddockResources = do
115 root <- buildRootRules
116 let resdir = "utils/haddock/haddock-api/resources"
117 haddockLib = root -/- "stage1/lib" -- Haddock is built in stage1
118
119 [ haddockLib -/- "html//*", haddockLib -/- "latex//*" ] |%> \target -> do
120 let source = resdir -/- makeRelative haddockLib target
121 copyFile source target
122
123 -- | Build the haddocks for GHC's libraries.
124 buildLibraryDocumentation :: Rules ()
125 buildLibraryDocumentation = do
126 root <- buildRootRules
127
128 -- Building the "Haskell Hierarchical Libraries" index
129 root -/- htmlRoot -/- "libraries/index.html" %> \file -> do
130 need [ "libraries/prologue.txt" ]
131
132 -- We want Haddocks for everything except `rts` to be built, but we
133 -- don't want the index to be polluted by stuff from `ghc`-the-library
134 -- (there will be a seperate top-level link to those Haddocks).
135 haddocks <- allHaddocks
136 let neededDocs = filter (\x -> takeFileName x /= "rts.haddock") haddocks
137 libDocs = filter (\x -> takeFileName x /= "ghc.haddock") neededDocs
138
139 need neededDocs
140 build $ target docContext (Haddock BuildIndex) libDocs [file]
141
142 allHaddocks :: Action [FilePath]
143 allHaddocks = do
144 pkgs <- stagePackages Stage1
145 sequence [ pkgHaddockFile $ vanillaContext Stage1 pkg
146 | pkg <- pkgs, isLibrary pkg, pkgName pkg /= "rts" ]
147
148 -- Note: this build rule creates plenty of files, not just the .haddock one.
149 -- All of them go into the 'docRoot' subdirectory. Pedantically tracking all
150 -- built files in the Shake database seems fragile and unnecessary.
151 buildPackageDocumentation :: Rules ()
152 buildPackageDocumentation = do
153 root <- buildRootRules
154
155 -- Per-package haddocks
156 root -/- htmlRoot -/- "libraries/*/haddock-prologue.txt" %> \file -> do
157 ctx <- getPkgDocTarget root file >>= pkgDocContext
158 -- This is how @ghc-cabal@ used to produces "haddock-prologue.txt" files.
159 syn <- pkgSynopsis (Context.package ctx)
160 desc <- pkgDescription (Context.package ctx)
161 let prologue = if null desc then syn else desc
162 liftIO $ writeFile file prologue
163
164 root -/- htmlRoot -/- "libraries/*/*.haddock" %> \file -> do
165 context <- getPkgDocTarget root file >>= pkgDocContext
166 need [ takeDirectory file -/- "haddock-prologue.txt"]
167 haddocks <- haddockDependencies context
168
169 -- `ghc-prim` has a source file for 'GHC.Prim' which is generated just
170 -- for Haddock. We need to 'union' (instead of '++') to avoid passing
171 -- 'GHC.PrimopWrappers' (which unfortunately shows up in both
172 -- `generatedSrcs` and `vanillaSrcs`) to Haddock twice.
173 generatedSrcs <- interpretInContext context (Expression.package ghcPrim ? ghcPrimDependencies)
174 vanillaSrcs <- hsSources context
175 let srcs = vanillaSrcs `union` generatedSrcs
176
177 need $ srcs ++ haddocks
178
179 -- Build Haddock documentation
180 -- TODO: Pass the correct way from Rules via Context.
181 dynamicPrograms <- dynamicGhcPrograms =<< flavour
182 let haddockWay = if dynamicPrograms then dynamic else vanilla
183 build $ target (context {way = haddockWay}) (Haddock BuildPackage) srcs [file]
184
185 data PkgDocTarget = DotHaddock PackageName | HaddockPrologue PackageName
186 deriving (Eq, Show)
187
188 pkgDocContext :: PkgDocTarget -> Action Context
189 pkgDocContext target = case findPackageByName pkgname of
190 Nothing -> error $ "pkgDocContext: couldn't find package " ++ pkgname
191 Just p -> return (Context Stage1 p vanilla)
192
193 where pkgname = case target of
194 DotHaddock n -> n
195 HaddockPrologue n -> n
196
197 parsePkgDocTarget :: FilePath -> Parsec.Parsec String () PkgDocTarget
198 parsePkgDocTarget root = do
199 _ <- Parsec.string root *> Parsec.optional (Parsec.char '/')
200 _ <- Parsec.string (htmlRoot ++ "/")
201 _ <- Parsec.string "libraries/"
202 pkgname <- Parsec.manyTill Parsec.anyChar (Parsec.char '/')
203 Parsec.choice
204 [ Parsec.try (Parsec.string "haddock-prologue.txt")
205 *> pure (HaddockPrologue pkgname)
206 , Parsec.string (pkgname <.> "haddock")
207 *> pure (DotHaddock pkgname)
208 ]
209
210 getPkgDocTarget :: FilePath -> FilePath -> Action PkgDocTarget
211 getPkgDocTarget root path =
212 parsePath (parsePkgDocTarget root) "<doc target>" path
213
214 -------------------------------------- PDF -------------------------------------
215
216 -- | Build all PDF documentation
217 buildPdfDocumentation :: Rules ()
218 buildPdfDocumentation = mapM_ buildSphinxPdf docPaths
219
220 -- | Compile a Sphinx ReStructured Text package to LaTeX
221 buildSphinxPdf :: FilePath -> Rules ()
222 buildSphinxPdf path = do
223 root <- buildRootRules
224 root -/- pdfRoot -/- path <.> "pdf" %> \file -> do
225 withTempDir $ \dir -> do
226 build $ target docContext (Sphinx Latex) [pathPath path] [dir]
227 build $ target docContext Xelatex [path <.> "tex"] [dir]
228 copyFileUntracked (dir -/- path <.> "pdf") file
229
230 ------------------------------------ Archive -----------------------------------
231
232 -- | Build documentation archives.
233 buildDocumentationArchives :: Rules ()
234 buildDocumentationArchives = mapM_ buildArchive docPaths
235
236 buildArchive :: FilePath -> Rules ()
237 buildArchive path = do
238 root <- buildRootRules
239 root -/- pathArchive path %> \file -> do
240 root <- buildRoot
241 let src = root -/- pathIndex path
242 need [src]
243 build $ target docContext (Tar Create) [takeDirectory src] [file]
244
245 -- | Build the man page.
246 buildManPage :: Rules ()
247 buildManPage = do
248 root <- buildRootRules
249 root -/- manPageBuildPath %> \file -> do
250 need ["docs/users_guide/ghc.rst"]
251 withTempDir $ \dir -> do
252 build $ target docContext (Sphinx Man) ["docs/users_guide"] [dir]
253 copyFileUntracked (dir -/- "ghc.1") file
254
255 -- | Find the Haddock files for the dependencies of the current library.
256 haddockDependencies :: Context -> Action [FilePath]
257 haddockDependencies context = do
258 depNames <- interpretInContext context (getContextData depNames)
259 sequence [ pkgHaddockFile $ vanillaContext Stage1 depPkg
260 | Just depPkg <- map findPackageByName depNames, depPkg /= rts ]