Merge pull request #147265 from pennae/option-docs-build

put all option docs build flavors on equal footing

+1 -1
nixos/doc/manual/default.nix
···
in rec {
inherit generatedSources;
-
inherit (optionsDoc) optionsJSON optionsXML optionsDocBook;
# Generate the NixOS manual.
manualHTML = runCommand "nixos-manual-html"
···
in rec {
inherit generatedSources;
+
inherit (optionsDoc) optionsJSON optionsDocBook;
# Generate the NixOS manual.
manualHTML = runCommand "nixos-manual-html"
+37 -90
nixos/lib/make-options-doc/default.nix
···
}:
let
-
# Replace functions by the string <function>
-
substFunction = x:
-
if builtins.isAttrs x then lib.mapAttrs (name: substFunction) x
-
else if builtins.isList x then map substFunction x
else if lib.isFunction x then "<function>"
else x;
-
optionsListDesc = lib.flip map optionsListVisible
(opt: transformOptions opt
-
// lib.optionalAttrs (opt ? example) { example = substFunction opt.example; }
-
// lib.optionalAttrs (opt ? default) { default = substFunction opt.default; }
-
// lib.optionalAttrs (opt ? type) { type = substFunction opt.type; }
// lib.optionalAttrs (opt ? relatedPackages && opt.relatedPackages != []) { relatedPackages = genRelatedPackages opt.relatedPackages opt.name; }
);
···
+ "</listitem>";
in "<itemizedlist>${lib.concatStringsSep "\n" (map (p: describe (unpack p)) packages)}</itemizedlist>";
-
# Custom "less" that pushes up all the things ending in ".enable*"
-
# and ".package*"
-
optionLess = a: b:
-
let
-
ise = lib.hasPrefix "enable";
-
isp = lib.hasPrefix "package";
-
cmp = lib.splitByAndCompare ise lib.compare
-
(lib.splitByAndCompare isp lib.compare lib.compare);
-
in lib.compareLists cmp a.loc b.loc < 0;
-
# Remove invisible and internal options.
optionsListVisible = lib.filter (opt: opt.visible && !opt.internal) (lib.optionAttrSetToDocList options);
-
# Customly sort option list for the man page.
-
# Always ensure that the sort order matches sortXML.py!
-
optionsList = lib.sort optionLess optionsListDesc;
-
-
# Convert the list of options into an XML file.
-
# This file is *not* sorted sorted to save on eval time, since the docbook XML
-
# and the manpage depend on it and thus we evaluate this on every system rebuild.
-
optionsXML = builtins.toFile "options.xml" (builtins.toXML optionsListDesc);
-
optionsNix = builtins.listToAttrs (map (o: { name = o.name; value = removeAttrs o ["name" "visible" "internal"]; }) optionsList);
-
# TODO: declarations: link to github
-
singleAsciiDoc = name: value: ''
-
== ${name}
-
-
${value.description}
-
-
[discrete]
-
=== details
-
Type:: ${value.type}
-
${ if lib.hasAttr "default" value
-
then ''
-
Default::
-
+
-
----
-
${builtins.toJSON value.default}
-
----
-
''
-
else "No Default:: {blank}"
-
}
-
${ if value.readOnly
-
then "Read Only:: {blank}"
-
else ""
-
}
-
${ if lib.hasAttr "example" value
-
then ''
-
Example::
-
+
-
----
-
${builtins.toJSON value.example}
-
----
-
''
-
else "No Example:: {blank}"
-
}
'';
-
singleMDDoc = name: value: ''
-
## ${lib.escape [ "<" ">" ] name}
-
${value.description}
-
-
${lib.optionalString (value ? type) ''
-
*_Type_*:
-
${value.type}
-
''}
-
-
${lib.optionalString (value ? default) ''
-
*_Default_*
-
```
-
${builtins.toJSON value.default}
-
```
-
''}
-
-
${lib.optionalString (value ? example) ''
-
*_Example_*
-
```
-
${builtins.toJSON value.example}
-
```
-
''}
'';
-
in {
-
inherit optionsNix;
-
-
optionsAsciiDoc = lib.concatStringsSep "\n" (lib.mapAttrsToList singleAsciiDoc optionsNix);
-
-
optionsMDDoc = lib.concatStringsSep "\n" (lib.mapAttrsToList singleMDDoc optionsNix);
-
optionsJSON = pkgs.runCommand "options.json"
{ meta.description = "List of NixOS options in JSON format";
buildInputs = [ pkgs.brotli ];
···
mkdir -p $out/nix-support
echo "file json $dst/options.json" >> $out/nix-support/hydra-build-products
echo "file json-br $dst/options.json.br" >> $out/nix-support/hydra-build-products
-
''; # */
optionsDocBook = pkgs.runCommand "options-docbook.xml" {} ''
optionsXML=${optionsXML}
···
}:
let
+
# Make a value safe for JSON. Functions are replaced by the string "<function>",
+
# derivations are replaced with an attrset
+
# { _type = "derivation"; name = <name of that derivation>; }.
+
# We need to handle derivations specially because consumers want to know about them,
+
# but we can't easily use the type,name subset of keys (since type is often used as
+
# a module option and might cause confusion). Use _type,name instead to the same
+
# effect, since _type is already used by the module system.
+
substSpecial = x:
+
if lib.isDerivation x then { _type = "derivation"; name = x.name; }
+
else if builtins.isAttrs x then lib.mapAttrs (name: substSpecial) x
+
else if builtins.isList x then map substSpecial x
else if lib.isFunction x then "<function>"
else x;
+
optionsList = lib.flip map optionsListVisible
(opt: transformOptions opt
+
// lib.optionalAttrs (opt ? example) { example = substSpecial opt.example; }
+
// lib.optionalAttrs (opt ? default) { default = substSpecial opt.default; }
+
// lib.optionalAttrs (opt ? type) { type = substSpecial opt.type; }
// lib.optionalAttrs (opt ? relatedPackages && opt.relatedPackages != []) { relatedPackages = genRelatedPackages opt.relatedPackages opt.name; }
);
···
+ "</listitem>";
in "<itemizedlist>${lib.concatStringsSep "\n" (map (p: describe (unpack p)) packages)}</itemizedlist>";
# Remove invisible and internal options.
optionsListVisible = lib.filter (opt: opt.visible && !opt.internal) (lib.optionAttrSetToDocList options);
optionsNix = builtins.listToAttrs (map (o: { name = o.name; value = removeAttrs o ["name" "visible" "internal"]; }) optionsList);
+
in rec {
+
inherit optionsNix;
+
optionsAsciiDoc = pkgs.runCommand "options.adoc" {} ''
+
${pkgs.python3Minimal}/bin/python ${./generateAsciiDoc.py} \
+
< ${optionsJSON}/share/doc/nixos/options.json \
+
> $out
'';
+
optionsCommonMark = pkgs.runCommand "options.md" {} ''
+
${pkgs.python3Minimal}/bin/python ${./generateCommonMark.py} \
+
< ${optionsJSON}/share/doc/nixos/options.json \
+
> $out
'';
optionsJSON = pkgs.runCommand "options.json"
{ meta.description = "List of NixOS options in JSON format";
buildInputs = [ pkgs.brotli ];
···
mkdir -p $out/nix-support
echo "file json $dst/options.json" >> $out/nix-support/hydra-build-products
echo "file json-br $dst/options.json.br" >> $out/nix-support/hydra-build-products
+
'';
+
+
# Convert options.json into an XML file.
+
# The actual generation of the xml file is done in nix purely for the convenience
+
# of not having to generate the xml some other way
+
optionsXML = pkgs.runCommand "options.xml" {} ''
+
${pkgs.nix}/bin/nix-instantiate \
+
--store dummy:// \
+
--eval --xml --strict ${./optionsJSONtoXML.nix} \
+
--argstr file ${optionsJSON}/share/doc/nixos/options.json \
+
> "$out"
+
'';
optionsDocBook = pkgs.runCommand "options-docbook.xml" {} ''
optionsXML=${optionsXML}
+37
nixos/lib/make-options-doc/generateAsciiDoc.py
···
···
+
import json
+
import sys
+
+
options = json.load(sys.stdin)
+
# TODO: declarations: link to github
+
for (name, value) in options.items():
+
print(f'== {name}')
+
print()
+
print(value['description'])
+
print()
+
print('[discrete]')
+
print('=== details')
+
print()
+
print(f'Type:: {value["type"]}')
+
if 'default' in value:
+
print('Default::')
+
print('+')
+
print('----')
+
print(json.dumps(value['default'], ensure_ascii=False, separators=(',', ':')))
+
print('----')
+
print()
+
else:
+
print('No Default:: {blank}')
+
if value['readOnly']:
+
print('Read Only:: {blank}')
+
else:
+
print()
+
if 'example' in value:
+
print('Example::')
+
print('+')
+
print('----')
+
print(json.dumps(value['example'], ensure_ascii=False, separators=(',', ':')))
+
print('----')
+
print()
+
else:
+
print('No Example:: {blank}')
+
print()
+27
nixos/lib/make-options-doc/generateCommonMark.py
···
···
+
import json
+
import sys
+
+
options = json.load(sys.stdin)
+
for (name, value) in options.items():
+
print('##', name.replace('<', '\\<').replace('>', '\\>'))
+
print(value['description'])
+
print()
+
if 'type' in value:
+
print('*_Type_*:')
+
print(value['type'])
+
print()
+
print()
+
if 'default' in value:
+
print('*_Default_*')
+
print('```')
+
print(json.dumps(value['default'], ensure_ascii=False, separators=(',', ':')))
+
print('```')
+
print()
+
print()
+
if 'example' in value:
+
print('*_Example_*')
+
print('```')
+
print(json.dumps(value['example'], ensure_ascii=False, separators=(',', ':')))
+
print('```')
+
print()
+
print()
+1 -1
nixos/lib/make-options-doc/options-to-docbook.xsl
···
</xsl:template>
-
<xsl:template match="derivation">
<replaceable>(build of <xsl:value-of select="attr[@name = 'name']/string/@value" />)</replaceable>
</xsl:template>
···
</xsl:template>
+
<xsl:template match="attrs[attr[@name = '_type' and string[@value = 'derivation']]]">
<replaceable>(build of <xsl:value-of select="attr[@name = 'name']/string/@value" />)</replaceable>
</xsl:template>
+6
nixos/lib/make-options-doc/optionsJSONtoXML.nix
···
···
+
{ file }:
+
+
builtins.attrValues
+
(builtins.mapAttrs
+
(name: def: def // { inherit name; })
+
(builtins.fromJSON (builtins.readFile file)))
-1
nixos/lib/make-options-doc/sortXML.py
···
for p in opt.findall('attr[@name="loc"]/list/string')
]
-
# always ensure that the sort order matches the order used in the nix expression!
options.sort(key=sortKey)
doc = ET.Element("expr")
···
for p in opt.findall('attr[@name="loc"]/list/string')
]
options.sort(key=sortKey)
doc = ET.Element("expr")