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