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 ouput 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 # Overriden explicitly here, this would include all modules from NixOS otherwise.
63 # See: docs of eval-config.nix for more details
64 baseModules = [];
65 modules = [
66 ./module.nix
67 ];
68 };
69 in
70 pkgs.nixosOptionsDoc {
71 inherit (eval) options;
72 }
73 ```
74
75 ## Example: non-NixOS modules
76
77 `nixosOptionsDoc` can also be used to build documentation for non-NixOS modules.
78
79 ```nix
80 let
81 eval = lib.evalModules {
82 modules = [
83 ./module.nix
84 ];
85 };
86 in
87 pkgs.nixosOptionsDoc {
88 inherit (eval) options;
89 }
90 ```
91*/
92{
93 pkgs,
94 lib,
95 options,
96 transformOptions ? lib.id, # function for additional transformations of the options
97 documentType ? "appendix",
98 # TODO deprecate "appendix" in favor of "none"
99 # and/or rename function to moduleOptionDoc for clean slate
100
101 # If you include more than one option list into a document, you need to
102 # provide different ids.
103 variablelistId ? "configuration-variable-list",
104 # String to prefix to the option XML/HTML id attributes.
105 optionIdPrefix ? "opt-",
106 revision ? "", # Specify revision for the options
107 # a set of options the docs we are generating will be merged into, as if by recursiveUpdate.
108 # used to split the options doc build into a static part (nixos/modules) and a dynamic part
109 # (non-nixos modules imported via configuration.nix, other module sources).
110 baseOptionsJSON ? null,
111 # instead of printing warnings for eg options with missing descriptions (which may be lost
112 # by nix build unless -L is given), emit errors instead and fail the build
113 warningsAreErrors ? true,
114}:
115
116let
117 rawOpts = lib.optionAttrSetToDocList options;
118 transformedOpts = map transformOptions rawOpts;
119 filteredOpts = lib.filter (opt: opt.visible && !opt.internal) transformedOpts;
120 optionsList = lib.flip map filteredOpts (
121 opt:
122 opt
123 // lib.optionalAttrs (opt ? relatedPackages && opt.relatedPackages != [ ]) {
124 relatedPackages = genRelatedPackages opt.relatedPackages opt.name;
125 }
126 );
127
128 # Generate DocBook documentation for a list of packages. This is
129 # what `relatedPackages` option of `mkOption` from
130 # ../../../lib/options.nix influences.
131 #
132 # Each element of `relatedPackages` can be either
133 # - a string: that will be interpreted as an attribute name from `pkgs` and turned into a link
134 # to search.nixos.org,
135 # - a list: that will be interpreted as an attribute path from `pkgs` and turned into a link
136 # to search.nixos.org,
137 # - an attrset: that can specify `name`, `path`, `comment`
138 # (either of `name`, `path` is required, the rest are optional).
139 #
140 # NOTE: No checks against `pkgs` are made to ensure that the referenced package actually exists.
141 # Such checks are not compatible with option docs caching.
142 genRelatedPackages =
143 packages: optName:
144 let
145 unpack =
146 p:
147 if lib.isString p then
148 { name = p; }
149 else if lib.isList p then
150 { path = p; }
151 else
152 p;
153 describe =
154 args:
155 let
156 title = args.title or null;
157 name = args.name or (lib.concatStringsSep "." args.path);
158 in
159 ''
160 - [${lib.optionalString (title != null) "${title} aka "}`pkgs.${name}`](
161 https://search.nixos.org/packages?show=${name}&sort=relevance&query=${name}
162 )${lib.optionalString (args ? comment) "\n\n ${args.comment}"}
163 '';
164 in
165 lib.concatMapStrings (p: describe (unpack p)) packages;
166
167 optionsNix = builtins.listToAttrs (
168 map (o: {
169 name = o.name;
170 value = removeAttrs o [
171 "name"
172 "visible"
173 "internal"
174 ];
175 }) optionsList
176 );
177
178in
179rec {
180 inherit optionsNix;
181
182 optionsAsciiDoc =
183 pkgs.runCommand "options.adoc"
184 {
185 nativeBuildInputs = [ pkgs.nixos-render-docs ];
186 }
187 ''
188 nixos-render-docs -j $NIX_BUILD_CORES options asciidoc \
189 --manpage-urls ${pkgs.path + "/doc/manpage-urls.json"} \
190 --revision ${lib.escapeShellArg revision} \
191 ${optionsJSON}/share/doc/nixos/options.json \
192 $out
193 '';
194
195 optionsCommonMark =
196 pkgs.runCommand "options.md"
197 {
198 __structuredAttrs = true;
199 nativeBuildInputs = [ pkgs.nixos-render-docs ];
200 # For overriding
201 extraArgs = [ ];
202 }
203 ''
204 nixos-render-docs -j $NIX_BUILD_CORES options commonmark \
205 --manpage-urls ${pkgs.path + "/doc/manpage-urls.json"} \
206 --revision ${lib.escapeShellArg revision} \
207 ''${extraArgs[@]} \
208 ${optionsJSON}/share/doc/nixos/options.json \
209 $out
210 '';
211
212 optionsJSON =
213 pkgs.runCommand "options.json"
214 {
215 meta.description = "List of NixOS options in JSON format";
216 nativeBuildInputs = [
217 pkgs.brotli
218 pkgs.python3
219 ];
220 options = builtins.toFile "options.json" (
221 builtins.unsafeDiscardStringContext (builtins.toJSON optionsNix)
222 );
223 # merge with an empty set if baseOptionsJSON is null to run markdown
224 # processing on the input options
225 baseJSON = if baseOptionsJSON == null then builtins.toFile "base.json" "{}" else baseOptionsJSON;
226 }
227 ''
228 # Export list of options in different format.
229 dst=$out/share/doc/nixos
230 mkdir -p $dst
231
232 TOUCH_IF_DB=$dst/.used-docbook \
233 python ${./mergeJSON.py} \
234 ${lib.optionalString warningsAreErrors "--warnings-are-errors"} \
235 $baseJSON $options \
236 > $dst/options.json
237
238 if grep /nixpkgs/nixos/modules $dst/options.json; then
239 echo "The manual appears to depend on the location of Nixpkgs, which is bad"
240 echo "since this prevents sharing via the NixOS channel. This is typically"
241 echo "caused by an option default that refers to a relative path (see above"
242 echo "for hints about the offending path)."
243 exit 1
244 fi
245
246 brotli -9 < $dst/options.json > $dst/options.json.br
247
248 mkdir -p $out/nix-support
249 echo "file json $dst/options.json" >> $out/nix-support/hydra-build-products
250 echo "file json-br $dst/options.json.br" >> $out/nix-support/hydra-build-products
251 '';
252}