Release NixOS 23.11
[NixPkgs.git] / nixos / modules / misc / documentation.nix
blob46462c5abd4354b654775ccf89193c1edce598e6
1 { config, options, lib, pkgs, utils, modules, baseModules, extraModules, modulesPath, specialArgs, ... }:
3 with lib;
5 let
7   cfg = config.documentation;
8   allOpts = options;
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;
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       };
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;
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         '';
110     inherit (cfg.nixos.options) warningsAreErrors;
111   };
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     '';
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     };
142     in pkgs.symlinkJoin {
143       name = "nixos-help";
144       paths = [
145         helpScript
146         desktopItem
147       ];
148     };
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   ];
168   options = {
170     documentation = {
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.
179           See "Multiple-output packages" chapter in the nixpkgs manual for more info.
180         '';
181         # which is at ../../../doc/multiple-output.chapter.md
182       };
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       };
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       };
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       };
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       };
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       };
238       nixos.enable = mkOption {
239         type = types.bool;
240         default = true;
241         description = lib.mdDoc ''
242           Whether to install NixOS's own documentation.
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       };
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       };
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       };
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       };
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       };
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       };
303     };
305   };
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     }
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     })
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     })
340     (mkIf cfg.doc.enable {
341       environment.pathsToLink = [ "/share/doc" ];
342       environment.extraOutputsToInstall = [ "doc" ] ++ optional cfg.dev.enable "devdoc";
343     })
345     (mkIf cfg.nixos.enable {
346       system.build.manual = manual;
348       environment.systemPackages = []
349         ++ optional cfg.man.enable manual.nixos-configuration-reference-manpage
350         ++ optionals cfg.doc.enable [ manual.manualHTML nixos-help ];
351     })
353   ]);