1# Tests: ./tests.nix
2
3/**
4 Generates documentation for [nix modules](https://nix.dev/tutorials/module-system/index.html).
5
6 It uses the declared `options` to generate documentation in various formats.
7
8 # Outputs
9
10 This function returns an attribute set with the following entries.
11
12 ## optionsCommonMark
13
14 Documentation in CommonMark text format.
15
16 ## optionsJSON
17
18 All options in a JSON format suitable for further automated processing.
19
20 `example.json`
21 ```json
22 {
23 ...
24 "fileSystems.<name>.options": {
25 "declarations": ["nixos/modules/tasks/filesystems.nix"],
26 "default": {
27 "_type": "literalExpression",
28 "text": "[\n \"defaults\"\n]"
29 },
30 "description": "Options used to mount the file system.",
31 "example": {
32 "_type": "literalExpression",
33 "text": "[\n \"data=journal\"\n]"
34 },
35 "loc": ["fileSystems", "<name>", "options"],
36 "readOnly": false,
37 "type": "non-empty (list of string (with check: non-empty))"
38 "relatedPackages": "- [`pkgs.tmux`](\n https://search.nixos.org/packages?show=tmux&sort=relevance&query=tmux\n )\n",
39 },
40 ...
41 }
42 ```
43
44 ## optionsAsciiDoc
45
46 Documentation rendered as AsciiDoc. This is useful for e.g. man pages.
47
48 > Note: NixOS itself uses this output to to build the configuration.nix man page"
49
50 ## optionsNix
51
52 All options as a Nix attribute set value, with the same schema as `optionsJSON`.
53
54 # Example
55
56 ## Example: NixOS configuration
57
58 ```nix
59 let
60 # Evaluate a NixOS configuration
61 eval = import (pkgs.path + "/nixos/lib/eval-config.nix") {
62 modules = [
63 ./module.nix
64 ];
65 };
66 in
67 pkgs.nixosOptionsDoc {
68 inherit (eval) options;
69 }
70 ```
71
72 ## Example: non-NixOS modules
73
74 `nixosOptionsDoc` can also be used to build documentation for non-NixOS modules.
75
76 ```nix
77 let
78 eval = lib.evalModules {
79 modules = [
80 ./module.nix
81 ];
82 };
83 in
84 pkgs.nixosOptionsDoc {
85 inherit (eval) options;
86 }
87 ```
88*/
89{
90 pkgs,
91 lib,
92 options,
93 transformOptions ? lib.id, # function for additional transformations of the options
94 documentType ? "appendix",
95 # TODO deprecate "appendix" in favor of "none"
96 # and/or rename function to moduleOptionDoc for clean slate
97
98 # If you include more than one option list into a document, you need to
99 # provide different ids.
100 variablelistId ? "configuration-variable-list",
101 # String to prefix to the option XML/HTML id attributes.
102 optionIdPrefix ? "opt-",
103 revision ? "", # Specify revision for the options
104 # a set of options the docs we are generating will be merged into, as if by recursiveUpdate.
105 # used to split the options doc build into a static part (nixos/modules) and a dynamic part
106 # (non-nixos modules imported via configuration.nix, other module sources).
107 baseOptionsJSON ? null,
108 # instead of printing warnings for eg options with missing descriptions (which may be lost
109 # by nix build unless -L is given), emit errors instead and fail the build
110 warningsAreErrors ? true,
111}:
112
113let
114 rawOpts = lib.optionAttrSetToDocList options;
115 transformedOpts = map transformOptions rawOpts;
116 filteredOpts = lib.filter (opt: opt.visible && !opt.internal) transformedOpts;
117 optionsList = lib.flip map filteredOpts (
118 opt:
119 opt
120 // lib.optionalAttrs (opt ? relatedPackages && opt.relatedPackages != [ ]) {
121 relatedPackages = genRelatedPackages opt.relatedPackages opt.name;
122 }
123 );
124
125 # Generate DocBook documentation for a list of packages. This is
126 # what `relatedPackages` option of `mkOption` from
127 # ../../../lib/options.nix influences.
128 #
129 # Each element of `relatedPackages` can be either
130 # - a string: that will be interpreted as an attribute name from `pkgs` and turned into a link
131 # to search.nixos.org,
132 # - a list: that will be interpreted as an attribute path from `pkgs` and turned into a link
133 # to search.nixos.org,
134 # - an attrset: that can specify `name`, `path`, `comment`
135 # (either of `name`, `path` is required, the rest are optional).
136 #
137 # NOTE: No checks against `pkgs` are made to ensure that the referenced package actually exists.
138 # Such checks are not compatible with option docs caching.
139 genRelatedPackages =
140 packages: optName:
141 let
142 unpack =
143 p:
144 if lib.isString p then
145 { name = p; }
146 else if lib.isList p then
147 { path = p; }
148 else
149 p;
150 describe =
151 args:
152 let
153 title = args.title or null;
154 name = args.name or (lib.concatStringsSep "." args.path);
155 in
156 ''
157 - [${lib.optionalString (title != null) "${title} aka "}`pkgs.${name}`](
158 https://search.nixos.org/packages?show=${name}&sort=relevance&query=${name}
159 )${lib.optionalString (args ? comment) "\n\n ${args.comment}"}
160 '';
161 in
162 lib.concatMapStrings (p: describe (unpack p)) packages;
163
164 optionsNix = builtins.listToAttrs (
165 map (o: {
166 name = o.name;
167 value = removeAttrs o [
168 "name"
169 "visible"
170 "internal"
171 ];
172 }) optionsList
173 );
174
175in
176rec {
177 inherit optionsNix;
178
179 optionsAsciiDoc =
180 pkgs.runCommand "options.adoc"
181 {
182 nativeBuildInputs = [ pkgs.nixos-render-docs ];
183 }
184 ''
185 nixos-render-docs -j $NIX_BUILD_CORES options asciidoc \
186 --manpage-urls ${pkgs.path + "/doc/manpage-urls.json"} \
187 --revision ${lib.escapeShellArg revision} \
188 ${optionsJSON}/share/doc/nixos/options.json \
189 $out
190 '';
191
192 optionsCommonMark =
193 pkgs.runCommand "options.md"
194 {
195 __structuredAttrs = true;
196 nativeBuildInputs = [ pkgs.nixos-render-docs ];
197 # For overriding
198 extraArgs = [ ];
199 }
200 ''
201 nixos-render-docs -j $NIX_BUILD_CORES options commonmark \
202 --manpage-urls ${pkgs.path + "/doc/manpage-urls.json"} \
203 --revision ${lib.escapeShellArg revision} \
204 ''${extraArgs[@]} \
205 ${optionsJSON}/share/doc/nixos/options.json \
206 $out
207 '';
208
209 optionsJSON =
210 pkgs.runCommand "options.json"
211 {
212 meta.description = "List of NixOS options in JSON format";
213 nativeBuildInputs = [
214 pkgs.brotli
215 pkgs.python3
216 ];
217 options = builtins.toFile "options.json" (
218 builtins.unsafeDiscardStringContext (builtins.toJSON optionsNix)
219 );
220 # merge with an empty set if baseOptionsJSON is null to run markdown
221 # processing on the input options
222 baseJSON = if baseOptionsJSON == null then builtins.toFile "base.json" "{}" else baseOptionsJSON;
223 }
224 ''
225 # Export list of options in different format.
226 dst=$out/share/doc/nixos
227 mkdir -p $dst
228
229 TOUCH_IF_DB=$dst/.used-docbook \
230 python ${./mergeJSON.py} \
231 ${lib.optionalString warningsAreErrors "--warnings-are-errors"} \
232 $baseJSON $options \
233 > $dst/options.json
234
235 if grep /nixpkgs/nixos/modules $dst/options.json; then
236 echo "The manual appears to depend on the location of Nixpkgs, which is bad"
237 echo "since this prevents sharing via the NixOS channel. This is typically"
238 echo "caused by an option default that refers to a relative path (see above"
239 echo "for hints about the offending path)."
240 exit 1
241 fi
242
243 brotli -9 < $dst/options.json > $dst/options.json.br
244
245 mkdir -p $out/nix-support
246 echo "file json $dst/options.json" >> $out/nix-support/hydra-build-products
247 echo "file json-br $dst/options.json.br" >> $out/nix-support/hydra-build-products
248 '';
249}