at 18.09-beta 12 kB view raw
1{ pkgs, options, config, version, revision, extraSources ? [] }: 2 3with pkgs; 4 5let 6 lib = pkgs.lib; 7 8 # Remove invisible and internal options. 9 optionsListVisible = lib.filter (opt: opt.visible && !opt.internal) (lib.optionAttrSetToDocList options); 10 11 # Replace functions by the string <function> 12 substFunction = x: 13 if builtins.isAttrs x then lib.mapAttrs (name: substFunction) x 14 else if builtins.isList x then map substFunction x 15 else if lib.isFunction x then "<function>" 16 else x; 17 18 # Generate DocBook documentation for a list of packages. This is 19 # what `relatedPackages` option of `mkOption` from 20 # ../../../lib/options.nix influences. 21 # 22 # Each element of `relatedPackages` can be either 23 # - a string: that will be interpreted as an attribute name from `pkgs`, 24 # - a list: that will be interpreted as an attribute path from `pkgs`, 25 # - an attrset: that can specify `name`, `path`, `package`, `comment` 26 # (either of `name`, `path` is required, the rest are optional). 27 genRelatedPackages = packages: 28 let 29 unpack = p: if lib.isString p then { name = p; } 30 else if lib.isList p then { path = p; } 31 else p; 32 describe = args: 33 let 34 title = args.title or null; 35 name = args.name or (lib.concatStringsSep "." args.path); 36 path = args.path or [ args.name ]; 37 package = args.package or (lib.attrByPath path (throw "Invalid package attribute path `${toString path}'") pkgs); 38 in "<listitem>" 39 + "<para><literal>${lib.optionalString (title != null) "${title} aka "}pkgs.${name} (${package.meta.name})</literal>" 40 + lib.optionalString (!package.meta.available) " <emphasis>[UNAVAILABLE]</emphasis>" 41 + ": ${package.meta.description or "???"}.</para>" 42 + lib.optionalString (args ? comment) "\n<para>${args.comment}</para>" 43 # Lots of `longDescription's break DocBook, so we just wrap them into <programlisting> 44 + lib.optionalString (package.meta ? longDescription) "\n<programlisting>${package.meta.longDescription}</programlisting>" 45 + "</listitem>"; 46 in "<itemizedlist>${lib.concatStringsSep "\n" (map (p: describe (unpack p)) packages)}</itemizedlist>"; 47 48 optionsListDesc = lib.flip map optionsListVisible (opt: opt // { 49 # Clean up declaration sites to not refer to the NixOS source tree. 50 declarations = map stripAnyPrefixes opt.declarations; 51 } 52 // lib.optionalAttrs (opt ? example) { example = substFunction opt.example; } 53 // lib.optionalAttrs (opt ? default) { default = substFunction opt.default; } 54 // lib.optionalAttrs (opt ? type) { type = substFunction opt.type; } 55 // lib.optionalAttrs (opt ? relatedPackages && opt.relatedPackages != []) { relatedPackages = genRelatedPackages opt.relatedPackages; }); 56 57 # We need to strip references to /nix/store/* from options, 58 # including any `extraSources` if some modules came from elsewhere, 59 # or else the build will fail. 60 # 61 # E.g. if some `options` came from modules in ${pkgs.customModules}/nix, 62 # you'd need to include `extraSources = [ pkgs.customModules ]` 63 prefixesToStrip = map (p: "${toString p}/") ([ ../../.. ] ++ extraSources); 64 stripAnyPrefixes = lib.flip (lib.fold lib.removePrefix) prefixesToStrip; 65 66 # Custom "less" that pushes up all the things ending in ".enable*" 67 # and ".package*" 68 optionLess = a: b: 69 let 70 ise = lib.hasPrefix "enable"; 71 isp = lib.hasPrefix "package"; 72 cmp = lib.splitByAndCompare ise lib.compare 73 (lib.splitByAndCompare isp lib.compare lib.compare); 74 in lib.compareLists cmp a.loc b.loc < 0; 75 76 # Customly sort option list for the man page. 77 optionsList = lib.sort optionLess optionsListDesc; 78 79 # Convert the list of options into an XML file. 80 optionsXML = builtins.toFile "options.xml" (builtins.toXML optionsList); 81 82 optionsDocBook = runCommand "options-db.xml" {} '' 83 optionsXML=${optionsXML} 84 if grep /nixpkgs/nixos/modules $optionsXML; then 85 echo "The manual appears to depend on the location of Nixpkgs, which is bad" 86 echo "since this prevents sharing via the NixOS channel. This is typically" 87 echo "caused by an option default that refers to a relative path (see above" 88 echo "for hints about the offending path)." 89 exit 1 90 fi 91 ${buildPackages.libxslt.bin}/bin/xsltproc \ 92 --stringparam revision '${revision}' \ 93 -o $out ${./options-to-docbook.xsl} $optionsXML 94 ''; 95 96 sources = lib.sourceFilesBySuffices ./. [".xml"]; 97 98 modulesDoc = builtins.toFile "modules.xml" '' 99 <section xmlns:xi="http://www.w3.org/2001/XInclude" id="modules"> 100 ${(lib.concatMapStrings (path: '' 101 <xi:include href="${path}" /> 102 '') (lib.catAttrs "value" config.meta.doc))} 103 </section> 104 ''; 105 106 generatedSources = runCommand "generated-docbook" {} '' 107 mkdir $out 108 ln -s ${modulesDoc} $out/modules.xml 109 ln -s ${optionsDocBook} $out/options-db.xml 110 printf "%s" "${version}" > $out/version 111 ''; 112 113 copySources = 114 '' 115 cp -prd $sources/* . # */ 116 ln -s ${generatedSources} ./generated 117 chmod -R u+w . 118 ''; 119 120 toc = builtins.toFile "toc.xml" 121 '' 122 <toc role="chunk-toc"> 123 <d:tocentry xmlns:d="http://docbook.org/ns/docbook" linkend="book-nixos-manual"><?dbhtml filename="index.html"?> 124 <d:tocentry linkend="ch-options"><?dbhtml filename="options.html"?></d:tocentry> 125 <d:tocentry linkend="ch-release-notes"><?dbhtml filename="release-notes.html"?></d:tocentry> 126 </d:tocentry> 127 </toc> 128 ''; 129 130 manualXsltprocOptions = toString [ 131 "--param section.autolabel 1" 132 "--param section.label.includes.component.label 1" 133 "--stringparam html.stylesheet 'style.css overrides.css highlightjs/mono-blue.css'" 134 "--stringparam html.script './highlightjs/highlight.pack.js ./highlightjs/loader.js'" 135 "--param xref.with.number.and.title 1" 136 "--param toc.section.depth 3" 137 "--stringparam admon.style ''" 138 "--stringparam callout.graphics.extension .svg" 139 "--stringparam current.docid manual" 140 "--param chunk.section.depth 0" 141 "--param chunk.first.sections 1" 142 "--param use.id.as.filename 1" 143 "--stringparam generate.toc 'book toc appendix toc'" 144 "--stringparam chunk.toc ${toc}" 145 ]; 146 147 manual-combined = runCommand "nixos-manual-combined" 148 { inherit sources; 149 nativeBuildInputs = [ buildPackages.libxml2.bin buildPackages.libxslt.bin ]; 150 meta.description = "The NixOS manual as plain docbook XML"; 151 } 152 '' 153 ${copySources} 154 155 xmllint --xinclude --output ./manual-combined.xml ./manual.xml 156 xmllint --xinclude --noxincludenode \ 157 --output ./man-pages-combined.xml ./man-pages.xml 158 159 # outputs the context of an xmllint error output 160 # LEN lines around the failing line are printed 161 function context { 162 # length of context 163 local LEN=6 164 # lines to print before error line 165 local BEFORE=4 166 167 # xmllint output lines are: 168 # file.xml:1234: there was an error on line 1234 169 while IFS=':' read -r file line rest; do 170 echo 171 if [[ -n "$rest" ]]; then 172 echo "$file:$line:$rest" 173 local FROM=$(($line>$BEFORE ? $line - $BEFORE : 1)) 174 # number lines & filter context 175 nl --body-numbering=a "$file" | sed -n "$FROM,+$LEN p" 176 else 177 if [[ -n "$line" ]]; then 178 echo "$file:$line" 179 else 180 echo "$file" 181 fi 182 fi 183 done 184 } 185 186 function lintrng { 187 xmllint --debug --noout --nonet \ 188 --relaxng ${docbook5}/xml/rng/docbook/docbook.rng \ 189 "$1" \ 190 2>&1 | context 1>&2 191 # ^ redirect assumes xmllint doesnt print to stdout 192 } 193 194 lintrng manual-combined.xml 195 lintrng man-pages-combined.xml 196 197 mkdir $out 198 cp manual-combined.xml $out/ 199 cp man-pages-combined.xml $out/ 200 ''; 201 202 olinkDB = runCommand "manual-olinkdb" 203 { inherit sources; 204 nativeBuildInputs = [ buildPackages.libxml2.bin buildPackages.libxslt.bin ]; 205 } 206 '' 207 xsltproc \ 208 ${manualXsltprocOptions} \ 209 --stringparam collect.xref.targets only \ 210 --stringparam targets.filename "$out/manual.db" \ 211 --nonet \ 212 ${docbook_xsl_ns}/xml/xsl/docbook/xhtml/chunktoc.xsl \ 213 ${manual-combined}/manual-combined.xml 214 215 cat > "$out/olinkdb.xml" <<EOF 216 <?xml version="1.0" encoding="utf-8"?> 217 <!DOCTYPE targetset SYSTEM 218 "file://${docbook_xsl_ns}/xml/xsl/docbook/common/targetdatabase.dtd" [ 219 <!ENTITY manualtargets SYSTEM "file://$out/manual.db"> 220 ]> 221 <targetset> 222 <targetsetinfo> 223 Allows for cross-referencing olinks between the manpages 224 and manual. 225 </targetsetinfo> 226 227 <document targetdoc="manual">&manualtargets;</document> 228 </targetset> 229 EOF 230 ''; 231 232in rec { 233 inherit generatedSources; 234 235 # The NixOS options in JSON format. 236 optionsJSON = runCommand "options-json" 237 { meta.description = "List of NixOS options in JSON format"; 238 } 239 '' 240 # Export list of options in different format. 241 dst=$out/share/doc/nixos 242 mkdir -p $dst 243 244 cp ${builtins.toFile "options.json" (builtins.unsafeDiscardStringContext (builtins.toJSON 245 (builtins.listToAttrs (map (o: { name = o.name; value = removeAttrs o ["name" "visible" "internal"]; }) optionsList)))) 246 } $dst/options.json 247 248 mkdir -p $out/nix-support 249 echo "file json $dst/options.json" >> $out/nix-support/hydra-build-products 250 ''; # */ 251 252 # Generate the NixOS manual. 253 manual = runCommand "nixos-manual" 254 { inherit sources; 255 nativeBuildInputs = [ buildPackages.libxml2.bin buildPackages.libxslt.bin ]; 256 meta.description = "The NixOS manual in HTML format"; 257 allowedReferences = ["out"]; 258 } 259 '' 260 # Generate the HTML manual. 261 dst=$out/share/doc/nixos 262 mkdir -p $dst 263 xsltproc \ 264 ${manualXsltprocOptions} \ 265 --stringparam target.database.document "${olinkDB}/olinkdb.xml" \ 266 --nonet --output $dst/ \ 267 ${docbook_xsl_ns}/xml/xsl/docbook/xhtml/chunktoc.xsl \ 268 ${manual-combined}/manual-combined.xml 269 270 mkdir -p $dst/images/callouts 271 cp ${docbook_xsl_ns}/xml/xsl/docbook/images/callouts/*.svg $dst/images/callouts/ 272 273 cp ${../../../doc/style.css} $dst/style.css 274 cp ${../../../doc/overrides.css} $dst/overrides.css 275 cp -r ${pkgs.documentation-highlighter} $dst/highlightjs 276 277 mkdir -p $out/nix-support 278 echo "nix-build out $out" >> $out/nix-support/hydra-build-products 279 echo "doc manual $dst" >> $out/nix-support/hydra-build-products 280 ''; # */ 281 282 283 manualEpub = runCommand "nixos-manual-epub" 284 { inherit sources; 285 buildInputs = [ libxml2.bin libxslt.bin zip ]; 286 } 287 '' 288 # Generate the epub manual. 289 dst=$out/share/doc/nixos 290 291 xsltproc \ 292 ${manualXsltprocOptions} \ 293 --stringparam target.database.document "${olinkDB}/olinkdb.xml" \ 294 --nonet --xinclude --output $dst/epub/ \ 295 ${docbook_xsl_ns}/xml/xsl/docbook/epub/docbook.xsl \ 296 ${manual-combined}/manual-combined.xml 297 298 mkdir -p $dst/epub/OEBPS/images/callouts 299 cp -r ${docbook_xsl_ns}/xml/xsl/docbook/images/callouts/*.svg $dst/epub/OEBPS/images/callouts # */ 300 echo "application/epub+zip" > mimetype 301 manual="$dst/nixos-manual.epub" 302 zip -0Xq "$manual" mimetype 303 cd $dst/epub && zip -Xr9D "$manual" * 304 305 rm -rf $dst/epub 306 307 mkdir -p $out/nix-support 308 echo "doc-epub manual $manual" >> $out/nix-support/hydra-build-products 309 ''; 310 311 312 # Generate the NixOS manpages. 313 manpages = runCommand "nixos-manpages" 314 { inherit sources; 315 nativeBuildInputs = [ buildPackages.libxml2.bin buildPackages.libxslt.bin ]; 316 allowedReferences = ["out"]; 317 } 318 '' 319 # Generate manpages. 320 mkdir -p $out/share/man 321 xsltproc --nonet \ 322 --param man.output.in.separate.dir 1 \ 323 --param man.output.base.dir "'$out/share/man/'" \ 324 --param man.endnotes.are.numbered 0 \ 325 --param man.break.after.slash 1 \ 326 --stringparam target.database.document "${olinkDB}/olinkdb.xml" \ 327 ${docbook_xsl_ns}/xml/xsl/docbook/manpages/docbook.xsl \ 328 ${manual-combined}/man-pages-combined.xml 329 ''; 330 331}