at 22.05-pre 7.0 kB view raw
1/* Generate JSON, XML and DocBook documentation for given NixOS options. 2 3 Minimal example: 4 5 { pkgs, }: 6 7 let 8 eval = import (pkgs.path + "/nixos/lib/eval-config.nix") { 9 baseModules = [ 10 ../module.nix 11 ]; 12 modules = []; 13 }; 14 in pkgs.nixosOptionsDoc { 15 options = eval.options; 16 } 17 18*/ 19{ pkgs 20, lib 21, options 22, transformOptions ? lib.id # function for additional tranformations of the options 23, revision ? "" # Specify revision for the options 24}: 25 26let 27 # Replace functions by the string <function> 28 substFunction = x: 29 if builtins.isAttrs x then lib.mapAttrs (name: substFunction) x 30 else if builtins.isList x then map substFunction x 31 else if lib.isFunction x then "<function>" 32 else x; 33 34 optionsListDesc = lib.flip map optionsListVisible 35 (opt: transformOptions opt 36 // lib.optionalAttrs (opt ? example) { example = substFunction opt.example; } 37 // lib.optionalAttrs (opt ? default) { default = substFunction opt.default; } 38 // lib.optionalAttrs (opt ? type) { type = substFunction opt.type; } 39 // lib.optionalAttrs (opt ? relatedPackages && opt.relatedPackages != []) { relatedPackages = genRelatedPackages opt.relatedPackages opt.name; } 40 ); 41 42 # Generate DocBook documentation for a list of packages. This is 43 # what `relatedPackages` option of `mkOption` from 44 # ../../../lib/options.nix influences. 45 # 46 # Each element of `relatedPackages` can be either 47 # - a string: that will be interpreted as an attribute name from `pkgs`, 48 # - a list: that will be interpreted as an attribute path from `pkgs`, 49 # - an attrset: that can specify `name`, `path`, `package`, `comment` 50 # (either of `name`, `path` is required, the rest are optional). 51 genRelatedPackages = packages: optName: 52 let 53 unpack = p: if lib.isString p then { name = p; } 54 else if lib.isList p then { path = p; } 55 else p; 56 describe = args: 57 let 58 title = args.title or null; 59 name = args.name or (lib.concatStringsSep "." args.path); 60 path = args.path or [ args.name ]; 61 package = args.package or (lib.attrByPath path (throw "Invalid package attribute path `${toString path}' found while evaluating `relatedPackages' of option `${optName}'") pkgs); 62 in "<listitem>" 63 + "<para><literal>${lib.optionalString (title != null) "${title} aka "}pkgs.${name} (${package.meta.name})</literal>" 64 + lib.optionalString (!package.meta.available) " <emphasis>[UNAVAILABLE]</emphasis>" 65 + ": ${package.meta.description or "???"}.</para>" 66 + lib.optionalString (args ? comment) "\n<para>${args.comment}</para>" 67 # Lots of `longDescription's break DocBook, so we just wrap them into <programlisting> 68 + lib.optionalString (package.meta ? longDescription) "\n<programlisting>${package.meta.longDescription}</programlisting>" 69 + "</listitem>"; 70 in "<itemizedlist>${lib.concatStringsSep "\n" (map (p: describe (unpack p)) packages)}</itemizedlist>"; 71 72 # Custom "less" that pushes up all the things ending in ".enable*" 73 # and ".package*" 74 optionLess = a: b: 75 let 76 ise = lib.hasPrefix "enable"; 77 isp = lib.hasPrefix "package"; 78 cmp = lib.splitByAndCompare ise lib.compare 79 (lib.splitByAndCompare isp lib.compare lib.compare); 80 in lib.compareLists cmp a.loc b.loc < 0; 81 82 # Remove invisible and internal options. 83 optionsListVisible = lib.filter (opt: opt.visible && !opt.internal) (lib.optionAttrSetToDocList options); 84 85 # Customly sort option list for the man page. 86 # Always ensure that the sort order matches sortXML.py! 87 optionsList = lib.sort optionLess optionsListDesc; 88 89 # Convert the list of options into an XML file. 90 # This file is *not* sorted sorted to save on eval time, since the docbook XML 91 # and the manpage depend on it and thus we evaluate this on every system rebuild. 92 optionsXML = builtins.toFile "options.xml" (builtins.toXML optionsListDesc); 93 94 optionsNix = builtins.listToAttrs (map (o: { name = o.name; value = removeAttrs o ["name" "visible" "internal"]; }) optionsList); 95 96 # TODO: declarations: link to github 97 singleAsciiDoc = name: value: '' 98 == ${name} 99 100 ${value.description} 101 102 [discrete] 103 === details 104 105 Type:: ${value.type} 106 ${ if lib.hasAttr "default" value 107 then '' 108 Default:: 109 + 110 ---- 111 ${builtins.toJSON value.default} 112 ---- 113 '' 114 else "No Default:: {blank}" 115 } 116 ${ if value.readOnly 117 then "Read Only:: {blank}" 118 else "" 119 } 120 ${ if lib.hasAttr "example" value 121 then '' 122 Example:: 123 + 124 ---- 125 ${builtins.toJSON value.example} 126 ---- 127 '' 128 else "No Example:: {blank}" 129 } 130 ''; 131 132 singleMDDoc = name: value: '' 133 ## ${lib.escape [ "<" ">" ] name} 134 ${value.description} 135 136 ${lib.optionalString (value ? type) '' 137 *_Type_*: 138 ${value.type} 139 ''} 140 141 ${lib.optionalString (value ? default) '' 142 *_Default_* 143 ``` 144 ${builtins.toJSON value.default} 145 ``` 146 ''} 147 148 ${lib.optionalString (value ? example) '' 149 *_Example_* 150 ``` 151 ${builtins.toJSON value.example} 152 ``` 153 ''} 154 ''; 155 156in { 157 inherit optionsNix; 158 159 optionsAsciiDoc = lib.concatStringsSep "\n" (lib.mapAttrsToList singleAsciiDoc optionsNix); 160 161 optionsMDDoc = lib.concatStringsSep "\n" (lib.mapAttrsToList singleMDDoc optionsNix); 162 163 optionsJSON = pkgs.runCommand "options.json" 164 { meta.description = "List of NixOS options in JSON format"; 165 buildInputs = [ pkgs.brotli ]; 166 } 167 '' 168 # Export list of options in different format. 169 dst=$out/share/doc/nixos 170 mkdir -p $dst 171 172 cp ${builtins.toFile "options.json" (builtins.unsafeDiscardStringContext (builtins.toJSON optionsNix))} $dst/options.json 173 174 brotli -9 < $dst/options.json > $dst/options.json.br 175 176 mkdir -p $out/nix-support 177 echo "file json $dst/options.json" >> $out/nix-support/hydra-build-products 178 echo "file json-br $dst/options.json.br" >> $out/nix-support/hydra-build-products 179 ''; # */ 180 181 optionsDocBook = pkgs.runCommand "options-docbook.xml" {} '' 182 optionsXML=${optionsXML} 183 if grep /nixpkgs/nixos/modules $optionsXML; then 184 echo "The manual appears to depend on the location of Nixpkgs, which is bad" 185 echo "since this prevents sharing via the NixOS channel. This is typically" 186 echo "caused by an option default that refers to a relative path (see above" 187 echo "for hints about the offending path)." 188 exit 1 189 fi 190 191 ${pkgs.python3Minimal}/bin/python ${./sortXML.py} $optionsXML sorted.xml 192 ${pkgs.libxslt.bin}/bin/xsltproc \ 193 --stringparam revision '${revision}' \ 194 -o intermediate.xml ${./options-to-docbook.xsl} sorted.xml 195 ${pkgs.libxslt.bin}/bin/xsltproc \ 196 -o "$out" ${./postprocess-option-descriptions.xsl} intermediate.xml 197 ''; 198}