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