1{ config, options, lib, pkgs, utils, modules, baseModules, extraModules, modulesPath, specialArgs, ... }:
2
3with lib;
4
5let
6
7 cfg = config.documentation;
8 allOpts = options;
9
10 canCacheDocs = m:
11 let
12 f = import m;
13 instance = f (mapAttrs (n: _: abort "evaluating ${n} for `meta` failed") (functionArgs f));
14 in
15 cfg.nixos.options.splitBuild
16 && builtins.isPath m
17 && isFunction f
18 && instance ? options
19 && instance.meta.buildDocsInSandbox or true;
20
21 docModules =
22 let
23 p = partition canCacheDocs (baseModules ++ cfg.nixos.extraModules);
24 in
25 {
26 lazy = p.right;
27 eager = p.wrong ++ optionals cfg.nixos.includeAllModules (extraModules ++ modules);
28 };
29
30 manual = import ../../doc/manual rec {
31 inherit pkgs config;
32 version = config.system.nixos.release;
33 revision = "release-${version}";
34 extraSources = cfg.nixos.extraModuleSources;
35 options =
36 let
37 scrubbedEval = evalModules {
38 modules = [ {
39 _module.check = false;
40 } ] ++ docModules.eager;
41 specialArgs = specialArgs // {
42 pkgs = scrubDerivations "pkgs" pkgs;
43 # allow access to arbitrary options for eager modules, eg for getting
44 # option types from lazy modules
45 options = allOpts;
46 inherit modulesPath utils;
47 };
48 };
49 scrubDerivations = namePrefix: pkgSet: mapAttrs
50 (name: value:
51 let wholeName = "${namePrefix}.${name}"; in
52 if isAttrs value then
53 scrubDerivations wholeName value
54 // (optionalAttrs (isDerivation value) { outPath = "\${${wholeName}}"; })
55 else value
56 )
57 pkgSet;
58 in scrubbedEval.options;
59
60 baseOptionsJSON =
61 let
62 filter =
63 builtins.filterSource
64 (n: t:
65 cleanSourceFilter n t
66 && (t == "directory" -> baseNameOf n != "tests")
67 && (t == "file" -> hasSuffix ".nix" n)
68 );
69 in
70 pkgs.runCommand "lazy-options.json" {
71 libPath = filter (pkgs.path + "/lib");
72 pkgsLibPath = filter (pkgs.path + "/pkgs/pkgs-lib");
73 nixosPath = filter (pkgs.path + "/nixos");
74 modules = map (p: ''"${removePrefix "${modulesPath}/" (toString p)}"'') docModules.lazy;
75 } ''
76 export NIX_STORE_DIR=$TMPDIR/store
77 export NIX_STATE_DIR=$TMPDIR/state
78 ${pkgs.buildPackages.nix}/bin/nix-instantiate \
79 --show-trace \
80 --eval --json --strict \
81 --argstr libPath "$libPath" \
82 --argstr pkgsLibPath "$pkgsLibPath" \
83 --argstr nixosPath "$nixosPath" \
84 --arg modules "[ $modules ]" \
85 --argstr stateVersion "${options.system.stateVersion.default}" \
86 --argstr release "${config.system.nixos.release}" \
87 $nixosPath/lib/eval-cacheable-options.nix > $out \
88 || {
89 echo -en "\e[1;31m"
90 echo 'Cacheable portion of option doc build failed.'
91 echo 'Usually this means that an option attribute that ends up in documentation (eg' \
92 '`default` or `description`) depends on the restricted module arguments' \
93 '`config` or `pkgs`.'
94 echo
95 echo 'Rebuild your configuration with `--show-trace` to find the offending' \
96 'location. Remove the references to restricted arguments (eg by escaping' \
97 'their antiquotations or adding a `defaultText`) or disable the sandboxed' \
98 'build for the failing module by setting `meta.buildDocsInSandbox = false`.'
99 echo -en "\e[0m"
100 exit 1
101 } >&2
102 '';
103
104 inherit (cfg.nixos.options) warningsAreErrors allowDocBook;
105 };
106
107
108 nixos-help = let
109 helpScript = pkgs.writeShellScriptBin "nixos-help" ''
110 # Finds first executable browser in a colon-separated list.
111 # (see how xdg-open defines BROWSER)
112 browser="$(
113 IFS=: ; for b in $BROWSER; do
114 [ -n "$(type -P "$b" || true)" ] && echo "$b" && break
115 done
116 )"
117 if [ -z "$browser" ]; then
118 browser="$(type -P xdg-open || true)"
119 if [ -z "$browser" ]; then
120 browser="${pkgs.w3m-nographics}/bin/w3m"
121 fi
122 fi
123 exec "$browser" ${manual.manualHTMLIndex}
124 '';
125
126 desktopItem = pkgs.makeDesktopItem {
127 name = "nixos-manual";
128 desktopName = "NixOS Manual";
129 genericName = "View NixOS documentation in a web browser";
130 icon = "nix-snowflake";
131 exec = "nixos-help";
132 categories = ["System"];
133 };
134
135 in pkgs.symlinkJoin {
136 name = "nixos-help";
137 paths = [
138 helpScript
139 desktopItem
140 ];
141 };
142
143in
144
145{
146 imports = [
147 ./man-db.nix
148 ./mandoc.nix
149 ./assertions.nix
150 ./meta.nix
151 ../config/system-path.nix
152 ../system/etc/etc.nix
153 (mkRenamedOptionModule [ "programs" "info" "enable" ] [ "documentation" "info" "enable" ])
154 (mkRenamedOptionModule [ "programs" "man" "enable" ] [ "documentation" "man" "enable" ])
155 (mkRenamedOptionModule [ "services" "nixosManual" "enable" ] [ "documentation" "nixos" "enable" ])
156 ];
157
158 options = {
159
160 documentation = {
161
162 enable = mkOption {
163 type = types.bool;
164 default = true;
165 description = lib.mdDoc ''
166 Whether to install documentation of packages from
167 {option}`environment.systemPackages` into the generated system path.
168
169 See "Multiple-output packages" chapter in the nixpkgs manual for more info.
170 '';
171 # which is at ../../../doc/multiple-output.chapter.md
172 };
173
174 man.enable = mkOption {
175 type = types.bool;
176 default = true;
177 description = lib.mdDoc ''
178 Whether to install manual pages.
179 This also includes `man` outputs.
180 '';
181 };
182
183 man.generateCaches = mkOption {
184 type = types.bool;
185 default = false;
186 description = mdDoc ''
187 Whether to generate the manual page index caches.
188 This allows searching for a page or
189 keyword using utilities like {manpage}`apropos(1)`
190 and the `-k` option of
191 {manpage}`man(1)`.
192 '';
193 };
194
195 info.enable = mkOption {
196 type = types.bool;
197 default = true;
198 description = lib.mdDoc ''
199 Whether to install info pages and the {command}`info` command.
200 This also includes "info" outputs.
201 '';
202 };
203
204 doc.enable = mkOption {
205 type = types.bool;
206 default = true;
207 description = lib.mdDoc ''
208 Whether to install documentation distributed in packages' `/share/doc`.
209 Usually plain text and/or HTML.
210 This also includes "doc" outputs.
211 '';
212 };
213
214 dev.enable = mkOption {
215 type = types.bool;
216 default = false;
217 description = mdDoc ''
218 Whether to install documentation targeted at developers.
219 * This includes man pages targeted at developers if {option}`documentation.man.enable` is
220 set (this also includes "devman" outputs).
221 * This includes info pages targeted at developers if {option}`documentation.info.enable`
222 is set (this also includes "devinfo" outputs).
223 * This includes other pages targeted at developers if {option}`documentation.doc.enable`
224 is set (this also includes "devdoc" outputs).
225 '';
226 };
227
228 nixos.enable = mkOption {
229 type = types.bool;
230 default = true;
231 description = lib.mdDoc ''
232 Whether to install NixOS's own documentation.
233
234 - This includes man pages like
235 {manpage}`configuration.nix(5)` if {option}`documentation.man.enable` is
236 set.
237 - This includes the HTML manual and the {command}`nixos-help` command if
238 {option}`documentation.doc.enable` is set.
239 '';
240 };
241
242 nixos.extraModules = mkOption {
243 type = types.listOf types.raw;
244 default = [];
245 description = lib.mdDoc ''
246 Modules for which to show options even when not imported.
247 '';
248 };
249
250 nixos.options.splitBuild = mkOption {
251 type = types.bool;
252 default = true;
253 description = lib.mdDoc ''
254 Whether to split the option docs build into a cacheable and an uncacheable part.
255 Splitting the build can substantially decrease the amount of time needed to build
256 the manual, but some user modules may be incompatible with this splitting.
257 '';
258 };
259
260 nixos.options.allowDocBook = mkOption {
261 type = types.bool;
262 default = true;
263 description = lib.mdDoc ''
264 Whether to allow DocBook option docs. When set to `false` all option using
265 DocBook documentation will cause a manual build error; additionally a new
266 renderer may be used.
267
268 ::: {.note}
269 The `false` setting for this option is not yet fully supported. While it
270 should work fine and produce the same output as the previous toolchain
271 using DocBook it may not work in all circumstances. Whether markdown option
272 documentation is allowed is independent of this option.
273 :::
274 '';
275 };
276
277 nixos.options.warningsAreErrors = mkOption {
278 type = types.bool;
279 default = true;
280 description = lib.mdDoc ''
281 Treat warning emitted during the option documentation build (eg for missing option
282 descriptions) as errors.
283 '';
284 };
285
286 nixos.includeAllModules = mkOption {
287 type = types.bool;
288 default = false;
289 description = lib.mdDoc ''
290 Whether the generated NixOS's documentation should include documentation for all
291 the options from all the NixOS modules included in the current
292 `configuration.nix`. Disabling this will make the manual
293 generator to ignore options defined outside of `baseModules`.
294 '';
295 };
296
297 nixos.extraModuleSources = mkOption {
298 type = types.listOf (types.either types.path types.str);
299 default = [ ];
300 description = lib.mdDoc ''
301 Which extra NixOS module paths the generated NixOS's documentation should strip
302 from options.
303 '';
304 example = literalExpression ''
305 # e.g. with options from modules in ''${pkgs.customModules}/nix:
306 [ pkgs.customModules ]
307 '';
308 };
309
310 };
311
312 };
313
314 config = mkIf cfg.enable (mkMerge [
315 {
316 assertions = [
317 {
318 assertion = !(cfg.man.man-db.enable && cfg.man.mandoc.enable);
319 message = ''
320 man-db and mandoc can't be used as the default man page viewer at the same time!
321 '';
322 }
323 ];
324 }
325
326 # The actual implementation for this lives in man-db.nix or mandoc.nix,
327 # depending on which backend is active.
328 (mkIf cfg.man.enable {
329 environment.pathsToLink = [ "/share/man" ];
330 environment.extraOutputsToInstall = [ "man" ] ++ optional cfg.dev.enable "devman";
331 })
332
333 (mkIf cfg.info.enable {
334 environment.systemPackages = [ pkgs.texinfoInteractive ];
335 environment.pathsToLink = [ "/share/info" ];
336 environment.extraOutputsToInstall = [ "info" ] ++ optional cfg.dev.enable "devinfo";
337 environment.extraSetup = ''
338 if [ -w $out/share/info ]; then
339 shopt -s nullglob
340 for i in $out/share/info/*.info $out/share/info/*.info.gz; do
341 ${pkgs.buildPackages.texinfo}/bin/install-info $i $out/share/info/dir
342 done
343 fi
344 '';
345 })
346
347 (mkIf cfg.doc.enable {
348 environment.pathsToLink = [ "/share/doc" ];
349 environment.extraOutputsToInstall = [ "doc" ] ++ optional cfg.dev.enable "devdoc";
350 })
351
352 (mkIf cfg.nixos.enable {
353 system.build.manual = manual;
354
355 environment.systemPackages = []
356 ++ optional cfg.man.enable manual.manpages
357 ++ optionals cfg.doc.enable [ manual.manualHTML nixos-help ];
358 })
359
360 ]);
361
362}