1{ pkgs
2, options
3, config
4, version
5, revision
6, extraSources ? []
7, baseOptionsJSON ? null
8, warningsAreErrors ? true
9, allowDocBook ? true
10, prefix ? ../../..
11}:
12
13with pkgs;
14
15let
16 inherit (lib) hasPrefix removePrefix;
17
18 lib = pkgs.lib;
19
20 docbook_xsl_ns = pkgs.docbook-xsl-ns.override {
21 withManOptDedupPatch = true;
22 };
23
24 # We need to strip references to /nix/store/* from options,
25 # including any `extraSources` if some modules came from elsewhere,
26 # or else the build will fail.
27 #
28 # E.g. if some `options` came from modules in ${pkgs.customModules}/nix,
29 # you'd need to include `extraSources = [ pkgs.customModules ]`
30 prefixesToStrip = map (p: "${toString p}/") ([ prefix ] ++ extraSources);
31 stripAnyPrefixes = lib.flip (lib.foldr lib.removePrefix) prefixesToStrip;
32
33 optionsDoc = buildPackages.nixosOptionsDoc {
34 inherit options revision baseOptionsJSON warningsAreErrors allowDocBook;
35 transformOptions = opt: opt // {
36 # Clean up declaration sites to not refer to the NixOS source tree.
37 declarations = map stripAnyPrefixes opt.declarations;
38 };
39 };
40
41 nixos-lib = import ../../lib { };
42
43 testOptionsDoc = let
44 eval = nixos-lib.evalTest {
45 # Avoid evaluating a NixOS config prototype.
46 config.node.type = lib.types.deferredModule;
47 options._module.args = lib.mkOption { internal = true; };
48 };
49 in buildPackages.nixosOptionsDoc {
50 inherit (eval) options;
51 inherit (revision);
52 transformOptions = opt: opt // {
53 # Clean up declaration sites to not refer to the NixOS source tree.
54 declarations =
55 map
56 (decl:
57 if hasPrefix (toString ../../..) (toString decl)
58 then
59 let subpath = removePrefix "/" (removePrefix (toString ../../..) (toString decl));
60 in { url = "https://github.com/NixOS/nixpkgs/blob/master/${subpath}"; name = subpath; }
61 else decl)
62 opt.declarations;
63 };
64 documentType = "none";
65 variablelistId = "test-options-list";
66 optionIdPrefix = "test-opt-";
67 };
68
69 sources = lib.sourceFilesBySuffices ./. [".xml"];
70
71 modulesDoc = builtins.toFile "modules.xml" ''
72 <section xmlns:xi="http://www.w3.org/2001/XInclude" id="modules">
73 ${(lib.concatMapStrings (path: ''
74 <xi:include href="${path}" />
75 '') (lib.catAttrs "value" config.meta.doc))}
76 </section>
77 '';
78
79 generatedSources = runCommand "generated-docbook" {} ''
80 mkdir $out
81 ln -s ${modulesDoc} $out/modules.xml
82 ln -s ${optionsDoc.optionsDocBook} $out/options-db.xml
83 ln -s ${testOptionsDoc.optionsDocBook} $out/test-options-db.xml
84 printf "%s" "${version}" > $out/version
85 '';
86
87 copySources =
88 ''
89 cp -prd $sources/* . # */
90 ln -s ${generatedSources} ./generated
91 chmod -R u+w .
92 '';
93
94 toc = builtins.toFile "toc.xml"
95 ''
96 <toc role="chunk-toc">
97 <d:tocentry xmlns:d="http://docbook.org/ns/docbook" linkend="book-nixos-manual"><?dbhtml filename="index.html"?>
98 <d:tocentry linkend="ch-options"><?dbhtml filename="options.html"?></d:tocentry>
99 <d:tocentry linkend="ch-release-notes"><?dbhtml filename="release-notes.html"?></d:tocentry>
100 </d:tocentry>
101 </toc>
102 '';
103
104 manualXsltprocOptions = toString [
105 "--param section.autolabel 1"
106 "--param section.label.includes.component.label 1"
107 "--stringparam html.stylesheet 'style.css overrides.css highlightjs/mono-blue.css'"
108 "--stringparam html.script './highlightjs/highlight.pack.js ./highlightjs/loader.js'"
109 "--param xref.with.number.and.title 1"
110 "--param toc.section.depth 0"
111 "--param generate.consistent.ids 1"
112 "--stringparam admon.style ''"
113 "--stringparam callout.graphics.extension .svg"
114 "--stringparam current.docid manual"
115 "--param chunk.section.depth 0"
116 "--param chunk.first.sections 1"
117 "--param use.id.as.filename 1"
118 "--stringparam chunk.toc ${toc}"
119 ];
120
121 manual-combined = runCommand "nixos-manual-combined"
122 { inherit sources;
123 nativeBuildInputs = [ buildPackages.libxml2.bin buildPackages.libxslt.bin ];
124 meta.description = "The NixOS manual as plain docbook XML";
125 }
126 ''
127 ${copySources}
128
129 xmllint --xinclude --output ./manual-combined.xml ./manual.xml
130 xmllint --xinclude --noxincludenode \
131 --output ./man-pages-combined.xml ./man-pages.xml
132
133 # outputs the context of an xmllint error output
134 # LEN lines around the failing line are printed
135 function context {
136 # length of context
137 local LEN=6
138 # lines to print before error line
139 local BEFORE=4
140
141 # xmllint output lines are:
142 # file.xml:1234: there was an error on line 1234
143 while IFS=':' read -r file line rest; do
144 echo
145 if [[ -n "$rest" ]]; then
146 echo "$file:$line:$rest"
147 local FROM=$(($line>$BEFORE ? $line - $BEFORE : 1))
148 # number lines & filter context
149 nl --body-numbering=a "$file" | sed -n "$FROM,+$LEN p"
150 else
151 if [[ -n "$line" ]]; then
152 echo "$file:$line"
153 else
154 echo "$file"
155 fi
156 fi
157 done
158 }
159
160 function lintrng {
161 xmllint --debug --noout --nonet \
162 --relaxng ${docbook5}/xml/rng/docbook/docbook.rng \
163 "$1" \
164 2>&1 | context 1>&2
165 # ^ redirect assumes xmllint doesn’t print to stdout
166 }
167
168 mkdir $out
169 cp manual-combined.xml $out/
170 cp man-pages-combined.xml $out/
171
172 lintrng $out/manual-combined.xml
173 lintrng $out/man-pages-combined.xml
174 '';
175
176 olinkDB = runCommand "manual-olinkdb"
177 { inherit sources;
178 nativeBuildInputs = [ buildPackages.libxml2.bin buildPackages.libxslt.bin ];
179 }
180 ''
181 xsltproc \
182 ${manualXsltprocOptions} \
183 --stringparam collect.xref.targets only \
184 --stringparam targets.filename "$out/manual.db" \
185 --nonet \
186 ${docbook_xsl_ns}/xml/xsl/docbook/xhtml/chunktoc.xsl \
187 ${manual-combined}/manual-combined.xml
188
189 cat > "$out/olinkdb.xml" <<EOF
190 <?xml version="1.0" encoding="utf-8"?>
191 <!DOCTYPE targetset SYSTEM
192 "file://${docbook_xsl_ns}/xml/xsl/docbook/common/targetdatabase.dtd" [
193 <!ENTITY manualtargets SYSTEM "file://$out/manual.db">
194 ]>
195 <targetset>
196 <targetsetinfo>
197 Allows for cross-referencing olinks between the manpages
198 and manual.
199 </targetsetinfo>
200
201 <document targetdoc="manual">&manualtargets;</document>
202 </targetset>
203 EOF
204 '';
205
206in rec {
207 inherit generatedSources;
208
209 inherit (optionsDoc) optionsJSON optionsNix optionsDocBook;
210
211 # Generate the NixOS manual.
212 manualHTML = runCommand "nixos-manual-html"
213 { inherit sources;
214 nativeBuildInputs = [ buildPackages.libxml2.bin buildPackages.libxslt.bin ];
215 meta.description = "The NixOS manual in HTML format";
216 allowedReferences = ["out"];
217 }
218 ''
219 # Generate the HTML manual.
220 dst=$out/share/doc/nixos
221 mkdir -p $dst
222 xsltproc \
223 ${manualXsltprocOptions} \
224 --stringparam target.database.document "${olinkDB}/olinkdb.xml" \
225 --stringparam id.warnings "1" \
226 --nonet --output $dst/ \
227 ${docbook_xsl_ns}/xml/xsl/docbook/xhtml/chunktoc.xsl \
228 ${manual-combined}/manual-combined.xml \
229 |& tee xsltproc.out
230 grep "^ID recommended on" xsltproc.out &>/dev/null && echo "error: some IDs are missing" && false
231 rm xsltproc.out
232
233 mkdir -p $dst/images/callouts
234 cp ${docbook_xsl_ns}/xml/xsl/docbook/images/callouts/*.svg $dst/images/callouts/
235
236 cp ${../../../doc/style.css} $dst/style.css
237 cp ${../../../doc/overrides.css} $dst/overrides.css
238 cp -r ${pkgs.documentation-highlighter} $dst/highlightjs
239
240 mkdir -p $out/nix-support
241 echo "nix-build out $out" >> $out/nix-support/hydra-build-products
242 echo "doc manual $dst" >> $out/nix-support/hydra-build-products
243 ''; # */
244
245 # Alias for backward compatibility. TODO(@oxij): remove eventually.
246 manual = manualHTML;
247
248 # Index page of the NixOS manual.
249 manualHTMLIndex = "${manualHTML}/share/doc/nixos/index.html";
250
251 manualEpub = runCommand "nixos-manual-epub"
252 { inherit sources;
253 nativeBuildInputs = [ buildPackages.libxml2.bin buildPackages.libxslt.bin buildPackages.zip ];
254 }
255 ''
256 # Generate the epub manual.
257 dst=$out/share/doc/nixos
258
259 xsltproc \
260 ${manualXsltprocOptions} \
261 --stringparam target.database.document "${olinkDB}/olinkdb.xml" \
262 --nonet --xinclude --output $dst/epub/ \
263 ${docbook_xsl_ns}/xml/xsl/docbook/epub/docbook.xsl \
264 ${manual-combined}/manual-combined.xml
265
266 mkdir -p $dst/epub/OEBPS/images/callouts
267 cp -r ${docbook_xsl_ns}/xml/xsl/docbook/images/callouts/*.svg $dst/epub/OEBPS/images/callouts # */
268 echo "application/epub+zip" > mimetype
269 manual="$dst/nixos-manual.epub"
270 zip -0Xq "$manual" mimetype
271 cd $dst/epub && zip -Xr9D "$manual" *
272
273 rm -rf $dst/epub
274
275 mkdir -p $out/nix-support
276 echo "doc-epub manual $manual" >> $out/nix-support/hydra-build-products
277 '';
278
279
280 # Generate the NixOS manpages.
281 manpages = runCommand "nixos-manpages"
282 { inherit sources;
283 nativeBuildInputs = [ buildPackages.libxml2.bin buildPackages.libxslt.bin ];
284 allowedReferences = ["out"];
285 }
286 ''
287 # Generate manpages.
288 mkdir -p $out/share/man
289 xsltproc --nonet \
290 --maxdepth 6000 \
291 --param man.output.in.separate.dir 1 \
292 --param man.output.base.dir "'$out/share/man/'" \
293 --param man.endnotes.are.numbered 0 \
294 --param man.break.after.slash 1 \
295 --stringparam target.database.document "${olinkDB}/olinkdb.xml" \
296 ${docbook_xsl_ns}/xml/xsl/docbook/manpages/docbook.xsl \
297 ${manual-combined}/man-pages-combined.xml
298 '';
299
300}