c9de3038edad12f777c3bbce0f6a4e82c653cfa2
[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 buildDocumentationArchives
68 buildHtmlDocumentation
69 buildManPage
70 buildPdfDocumentation
71
72 -- a phony rule that runs Haddock for "Haskell Hierarchical Libraries" and
73 -- the "GHC-API"
74 "docs-haddock" ~> do
75 root <- buildRoot
76 need [ root -/- pathIndex "libraries" ]
77
78 -- a phony rule that runs Haddock, builds the User's guide, builds
79 -- Haddock's manual, and builds man pages
80 "docs" ~> do
81 root <- buildRoot
82 let html = htmlRoot -/- "index.html" -- also implies "docs-haddock"
83 archives = map pathArchive docPaths
84 pdfs = map pathPdf $ docPaths \\ ["libraries"]
85 need $ map (root -/-) $ [html] ++ archives ++ pdfs ++ [manPageBuildPath]
86
87 ------------------------------------- HTML -------------------------------------
88
89 -- | Build rules for HTML documentation.
90 buildHtmlDocumentation :: Rules ()
91 buildHtmlDocumentation = do
92 mapM_ buildSphinxHtml $ docPaths \\ ["libraries"]
93 buildLibraryDocumentation
94 root <- buildRootRules
95
96 root -/- htmlRoot -/- "index.html" %> \file -> do
97 need $ map ((root -/-) . pathIndex) docPaths
98 copyFileUntracked "docs/index.html" file
99
100 -- | Compile a Sphinx ReStructured Text package to HTML.
101 buildSphinxHtml :: FilePath -> Rules ()
102 buildSphinxHtml path = do
103 root <- buildRootRules
104 root -/- htmlRoot -/- path -/- "index.html" %> \file -> do
105 let dest = takeDirectory file
106 build $ target docContext (Sphinx Html) [pathPath path] [dest]
107
108 ------------------------------------ Haddock -----------------------------------
109
110 -- | Build the haddocks for GHC's libraries.
111 buildLibraryDocumentation :: Rules ()
112 buildLibraryDocumentation = do
113 root <- buildRootRules
114
115 -- Building the "Haskell Hierarchical Libraries" index
116 root -/- htmlRoot -/- "libraries/index.html" %> \file -> do
117 need [ "libraries/prologue.txt" ]
118
119 -- We want Haddocks for everything except `rts` to be built, but we
120 -- don't want the index to be polluted by stuff from `ghc`-the-library
121 -- (there will be a separate top-level link to those Haddocks).
122 haddocks <- allHaddocks
123 let neededDocs = filter (\x -> takeFileName x /= "rts.haddock") haddocks
124 libDocs = filter (\x -> takeFileName x /= "ghc.haddock") neededDocs
125
126 need neededDocs
127 build $ target docContext (Haddock BuildIndex) libDocs [file]
128
129 allHaddocks :: Action [FilePath]
130 allHaddocks = do
131 pkgs <- stagePackages Stage1
132 sequence [ pkgHaddockFile $ vanillaContext Stage1 pkg
133 | pkg <- pkgs, isLibrary pkg, pkgName pkg /= "rts" ]
134
135 -- Note: this build rule creates plenty of files, not just the .haddock one.
136 -- All of them go into the 'docRoot' subdirectory. Pedantically tracking all
137 -- built files in the Shake database seems fragile and unnecessary.
138 buildPackageDocumentation :: Rules ()
139 buildPackageDocumentation = do
140 root <- buildRootRules
141
142 -- Per-package haddocks
143 root -/- htmlRoot -/- "libraries/*/haddock-prologue.txt" %> \file -> do
144 ctx <- pkgDocContext <$> getPkgDocTarget root file
145 -- This is how @ghc-cabal@ used to produces "haddock-prologue.txt" files.
146 syn <- pkgSynopsis (Context.package ctx)
147 desc <- pkgDescription (Context.package ctx)
148 let prologue = if null desc then syn else desc
149 liftIO $ writeFile file prologue
150
151 root -/- htmlRoot -/- "libraries/*/*.haddock" %> \file -> do
152 context <- pkgDocContext <$> getPkgDocTarget root file
153 need [ takeDirectory file -/- "haddock-prologue.txt"]
154 haddocks <- haddockDependencies context
155
156 -- `ghc-prim` has a source file for 'GHC.Prim' which is generated just
157 -- for Haddock. We need to 'union' (instead of '++') to avoid passing
158 -- 'GHC.PrimopWrappers' (which unfortunately shows up in both
159 -- `generatedSrcs` and `vanillaSrcs`) to Haddock twice.
160 generatedSrcs <- interpretInContext context (Expression.package ghcPrim ? ghcPrimDependencies)
161 vanillaSrcs <- hsSources context
162 let srcs = vanillaSrcs `union` generatedSrcs
163
164 need $ srcs ++ haddocks
165
166 -- Build Haddock documentation
167 -- TODO: Pass the correct way from Rules via Context.
168 dynamicPrograms <- dynamicGhcPrograms =<< flavour
169 let haddockWay = if dynamicPrograms then dynamic else vanilla
170 build $ target (context {way = haddockWay}) (Haddock BuildPackage) srcs [file]
171
172 data PkgDocTarget = DotHaddock PackageName | HaddockPrologue PackageName
173 deriving (Eq, Show)
174
175 pkgDocContext :: PkgDocTarget -> Context
176 pkgDocContext target = Context Stage1 (unsafeFindPackageByName name) vanilla
177 where
178 name = case target of DotHaddock n -> n
179 HaddockPrologue n -> n
180
181 parsePkgDocTarget :: FilePath -> Parsec.Parsec String () PkgDocTarget
182 parsePkgDocTarget root = do
183 _ <- Parsec.string root *> Parsec.optional (Parsec.char '/')
184 _ <- Parsec.string (htmlRoot ++ "/")
185 _ <- Parsec.string "libraries/"
186 pkgname <- Parsec.manyTill Parsec.anyChar (Parsec.char '/')
187 Parsec.choice
188 [ Parsec.try (Parsec.string "haddock-prologue.txt")
189 *> pure (HaddockPrologue pkgname)
190 , Parsec.string (pkgname <.> "haddock")
191 *> pure (DotHaddock pkgname)
192 ]
193
194 getPkgDocTarget :: FilePath -> FilePath -> Action PkgDocTarget
195 getPkgDocTarget root path =
196 parsePath (parsePkgDocTarget root) "<doc target>" path
197
198 -------------------------------------- PDF -------------------------------------
199
200 -- | Build all PDF documentation
201 buildPdfDocumentation :: Rules ()
202 buildPdfDocumentation = mapM_ buildSphinxPdf docPaths
203
204 -- | Compile a Sphinx ReStructured Text package to LaTeX
205 buildSphinxPdf :: FilePath -> Rules ()
206 buildSphinxPdf path = do
207 root <- buildRootRules
208 root -/- pdfRoot -/- path <.> "pdf" %> \file -> do
209 withTempDir $ \dir -> do
210 build $ target docContext (Sphinx Latex) [pathPath path] [dir]
211 build $ target docContext Xelatex [path <.> "tex"] [dir]
212 copyFileUntracked (dir -/- path <.> "pdf") file
213
214 ------------------------------------ Archive -----------------------------------
215
216 -- | Build documentation archives.
217 buildDocumentationArchives :: Rules ()
218 buildDocumentationArchives = mapM_ buildArchive docPaths
219
220 buildArchive :: FilePath -> Rules ()
221 buildArchive path = do
222 root <- buildRootRules
223 root -/- pathArchive path %> \file -> do
224 root <- buildRoot
225 let src = root -/- pathIndex path
226 need [src]
227 build $ target docContext (Tar Create) [takeDirectory src] [file]
228
229 -- | Build the man page.
230 buildManPage :: Rules ()
231 buildManPage = do
232 root <- buildRootRules
233 root -/- manPageBuildPath %> \file -> do
234 need ["docs/users_guide/ghc.rst"]
235 withTempDir $ \dir -> do
236 build $ target docContext (Sphinx Man) ["docs/users_guide"] [dir]
237 copyFileUntracked (dir -/- "ghc.1") file
238
239 -- | Find the Haddock files for the dependencies of the current library.
240 haddockDependencies :: Context -> Action [FilePath]
241 haddockDependencies context = do
242 depNames <- interpretInContext context (getContextData depNames)
243 sequence [ pkgHaddockFile $ vanillaContext Stage1 depPkg
244 | Just depPkg <- map findPackageByName depNames, depPkg /= rts ]