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