Clone of https://github.com/NixOS/nixpkgs.git (to stress-test knotserver)
at master 24 kB view raw
1/** 2 Module System option handling. 3*/ 4{ lib }: 5 6let 7 inherit (lib) 8 all 9 collect 10 concatLists 11 concatMap 12 concatMapStringsSep 13 filter 14 foldl' 15 head 16 tail 17 isAttrs 18 isBool 19 isDerivation 20 isFunction 21 isInt 22 isList 23 isString 24 length 25 mapAttrs 26 optional 27 optionals 28 take 29 ; 30 inherit (lib.attrsets) 31 attrByPath 32 optionalAttrs 33 showAttrPath 34 ; 35 inherit (lib.strings) 36 concatMapStrings 37 concatStringsSep 38 ; 39 inherit (lib.types) 40 mkOptionType 41 ; 42 inherit (lib.lists) 43 last 44 toList 45 ; 46 prioritySuggestion = '' 47 Use `lib.mkForce value` or `lib.mkDefault value` to change the priority on any of these definitions. 48 ''; 49in 50rec { 51 52 /** 53 Returns true when the given argument `a` is an option 54 55 # Inputs 56 57 `a` 58 : Any value to check whether it is an option 59 60 # Examples 61 :::{.example} 62 ## `lib.options.isOption` usage example 63 64 ```nix 65 isOption 1 // => false 66 isOption (mkOption {}) // => true 67 ``` 68 69 ::: 70 71 # Type 72 73 ``` 74 isOption :: a -> Bool 75 ``` 76 */ 77 isOption = lib.isType "option"; 78 79 /** 80 Creates an Option attribute set. mkOption accepts an attribute set with the following keys: 81 82 # Inputs 83 84 Structured attribute set 85 : Attribute set containing none or some of the following attributes. 86 87 `default` 88 : Optional default value used when no definition is given in the configuration. 89 90 `defaultText` 91 : Substitute for documenting the `default`, if evaluating the default value during documentation rendering is not possible. 92 : Can be any nix value that evaluates. 93 : Usage with `lib.literalMD` or `lib.literalExpression` is supported 94 95 `example` 96 : Optional example value used in the manual. 97 : Can be any nix value that evaluates. 98 : Usage with `lib.literalMD` or `lib.literalExpression` is supported 99 100 `description` 101 : Optional string describing the option. This is required if option documentation is generated. 102 103 `relatedPackages` 104 : Optional related packages used in the manual (see `genRelatedPackages` in `../nixos/lib/make-options-doc/default.nix`). 105 106 `type` 107 : Optional option type, providing type-checking and value merging. 108 109 `apply` 110 : Optional function that converts the option value to something else. 111 112 `internal` 113 : Optional boolean indicating whether the option is for NixOS developers only. 114 115 `visible` 116 : Optional, whether the option and/or sub-options show up in the manual. 117 Use false to hide the option and any sub-options from submodules. 118 Use "shallow" to hide only sub-options. 119 Use "transparent" to hide this option, but not its sub-options. 120 Default: true. 121 122 `readOnly` 123 : Optional boolean indicating whether the option can be set only once. 124 125 `...` (any other attribute) 126 : Any other attribute is passed through to the resulting option attribute set. 127 128 # Examples 129 :::{.example} 130 ## `lib.options.mkOption` usage example 131 132 ```nix 133 mkOption { } // => { _type = "option"; } 134 mkOption { default = "foo"; } // => { _type = "option"; default = "foo"; } 135 ``` 136 137 ::: 138 */ 139 mkOption = 140 { 141 default ? null, 142 defaultText ? null, 143 example ? null, 144 description ? null, 145 relatedPackages ? null, 146 type ? null, 147 apply ? null, 148 internal ? null, 149 visible ? null, 150 readOnly ? null, 151 }@attrs: 152 attrs // { _type = "option"; }; 153 154 /** 155 Creates an option declaration with a default value of ´false´, and can be defined to ´true´. 156 157 # Inputs 158 159 `name` 160 161 : Name for the created option 162 163 # Examples 164 :::{.example} 165 ## `lib.options.mkEnableOption` usage example 166 167 ```nix 168 # module 169 let 170 eval = lib.evalModules { 171 modules = [ 172 { 173 options.foo.enable = mkEnableOption "foo"; 174 175 config.foo.enable = true; 176 } 177 ]; 178 }; 179 in 180 eval.config 181 => { foo.enable = true; } 182 ``` 183 184 ::: 185 */ 186 mkEnableOption = 187 name: 188 mkOption { 189 default = false; 190 example = true; 191 description = "Whether to enable ${name}."; 192 type = lib.types.bool; 193 }; 194 195 /** 196 Creates an Option attribute set for an option that specifies the 197 package a module should use for some purpose. 198 199 The package is specified in the third argument under `default` as a list of strings 200 representing its attribute path in nixpkgs (or another package set). 201 Because of this, you need to pass nixpkgs itself (usually `pkgs` in a module; 202 alternatively to nixpkgs itself, another package set) as the first argument. 203 204 If you pass another package set you should set the `pkgsText` option. 205 This option is used to display the expression for the package set. It is `"pkgs"` by default. 206 If your expression is complex you should parenthesize it, as the `pkgsText` argument 207 is usually immediately followed by an attribute lookup (`.`). 208 209 The second argument may be either a string or a list of strings. 210 It provides the display name of the package in the description of the generated option 211 (using only the last element if the passed value is a list) 212 and serves as the fallback value for the `default` argument. 213 214 To include extra information in the description, pass `extraDescription` to 215 append arbitrary text to the generated description. 216 217 You can also pass an `example` value, either a literal string or an attribute path. 218 219 The `default` argument can be omitted if the provided name is 220 an attribute of pkgs (if `name` is a string) or a valid attribute path in pkgs (if `name` is a list). 221 You can also set `default` to just a string in which case it is interpreted as an attribute name 222 (a singleton attribute path, if you will). 223 224 If you wish to explicitly provide no default, pass `null` as `default`. 225 226 If you want users to be able to set no package, pass `nullable = true`. 227 In this mode a `default = null` will not be interpreted as no default and is interpreted literally. 228 229 # Inputs 230 231 `pkgs` 232 233 : Package set (an instantiation of nixpkgs such as pkgs in modules or another package set) 234 235 `name` 236 237 : Name for the package, shown in option description 238 239 Structured function argument 240 : Attribute set containing the following attributes. 241 242 `nullable` 243 : Optional whether the package can be null, for example to disable installing a package altogether. Default: `false` 244 245 `default` 246 : Optional attribute path where the default package is located. Default: `name` 247 If omitted will be copied from `name` 248 249 `example` 250 : Optional string or an attribute path to use as an example. Default: `null` 251 252 `extraDescription` 253 : Optional additional text to include in the option description. Default: `""` 254 255 `pkgsText` 256 : Optional representation of the package set passed as pkgs. Default: `"pkgs"` 257 258 # Type 259 260 ``` 261 mkPackageOption :: pkgs -> (string|[string]) -> { nullable? :: bool, default? :: string|[string], example? :: null|string|[string], extraDescription? :: string, pkgsText? :: string } -> option 262 ``` 263 264 # Examples 265 :::{.example} 266 ## `lib.options.mkPackageOption` usage example 267 268 ```nix 269 mkPackageOption pkgs "hello" { } 270 => { ...; default = pkgs.hello; defaultText = literalExpression "pkgs.hello"; description = "The hello package to use."; type = package; } 271 272 mkPackageOption pkgs "GHC" { 273 default = [ "ghc" ]; 274 example = "pkgs.haskellPackages.ghc.withPackages (hkgs: [ hkgs.primes ])"; 275 } 276 => { ...; default = pkgs.ghc; defaultText = literalExpression "pkgs.ghc"; description = "The GHC package to use."; example = literalExpression "pkgs.haskellPackages.ghc.withPackages (hkgs: [ hkgs.primes ])"; type = package; } 277 278 mkPackageOption pkgs [ "python3Packages" "pytorch" ] { 279 extraDescription = "This is an example and doesn't actually do anything."; 280 } 281 => { ...; default = pkgs.python3Packages.pytorch; defaultText = literalExpression "pkgs.python3Packages.pytorch"; description = "The pytorch package to use. This is an example and doesn't actually do anything."; type = package; } 282 283 mkPackageOption pkgs "nushell" { 284 nullable = true; 285 } 286 => { ...; default = pkgs.nushell; defaultText = literalExpression "pkgs.nushell"; description = "The nushell package to use."; type = nullOr package; } 287 288 mkPackageOption pkgs "coreutils" { 289 default = null; 290 } 291 => { ...; description = "The coreutils package to use."; type = package; } 292 293 mkPackageOption pkgs "dbus" { 294 nullable = true; 295 default = null; 296 } 297 => { ...; default = null; description = "The dbus package to use."; type = nullOr package; } 298 299 mkPackageOption pkgs.javaPackages "OpenJFX" { 300 default = "openjfx20"; 301 pkgsText = "pkgs.javaPackages"; 302 } 303 => { ...; default = pkgs.javaPackages.openjfx20; defaultText = literalExpression "pkgs.javaPackages.openjfx20"; description = "The OpenJFX package to use."; type = package; } 304 ``` 305 306 ::: 307 */ 308 mkPackageOption = 309 pkgs: name: 310 { 311 nullable ? false, 312 default ? name, 313 example ? null, 314 extraDescription ? "", 315 pkgsText ? "pkgs", 316 }: 317 let 318 name' = if isList name then last name else name; 319 default' = toList default; 320 defaultText = showAttrPath default'; 321 defaultValue = attrByPath default' (throw "${defaultText} cannot be found in ${pkgsText}") pkgs; 322 defaults = 323 if default != null then 324 { 325 default = defaultValue; 326 defaultText = literalExpression "${pkgsText}.${defaultText}"; 327 } 328 else 329 optionalAttrs nullable { 330 default = null; 331 }; 332 in 333 mkOption ( 334 defaults 335 // { 336 description = 337 "The ${name'} package to use." + (if extraDescription == "" then "" else " ") + extraDescription; 338 type = with lib.types; (if nullable then nullOr else lib.id) package; 339 } 340 // optionalAttrs (example != null) { 341 example = literalExpression ( 342 if isList example then "${pkgsText}.${showAttrPath example}" else example 343 ); 344 } 345 ); 346 347 /** 348 This option accepts arbitrary definitions, but it does not produce an option value. 349 350 This is useful for sharing a module across different module sets 351 without having to implement similar features as long as the 352 values of the options are not accessed. 353 354 # Inputs 355 356 `attrs` 357 358 : Attribute set whose attributes override the argument to `mkOption`. 359 */ 360 mkSinkUndeclaredOptions = 361 attrs: 362 mkOption ( 363 { 364 internal = true; 365 visible = false; 366 default = false; 367 description = "Sink for option definitions."; 368 type = mkOptionType { 369 name = "sink"; 370 check = x: true; 371 merge = loc: defs: false; 372 }; 373 apply = x: throw "Option value is not readable because the option is not declared."; 374 } 375 // attrs 376 ); 377 378 /** 379 A merge function that merges multiple definitions of an option into a single value 380 381 :::{.caution} 382 This function is used as the default merge operation in `lib.types.mkOptionType`. In most cases, explicit usage of this function is unnecessary. 383 ::: 384 385 # Inputs 386 387 `loc` 388 : location of the option in the configuration as a list of strings. 389 390 e.g. `["boot" "loader "grub" "enable"]` 391 392 `defs` 393 : list of definition values and locations. 394 395 e.g. `[ { file = "/foo.nix"; value = 1; } { file = "/bar.nix"; value = 2 } ]` 396 397 # Example 398 :::{.example} 399 ## `lib.options.mergeDefaultOption` usage example 400 401 ```nix 402 myType = mkOptionType { 403 name = "myType"; 404 merge = mergeDefaultOption; # <- This line is redundant. It is the default already. 405 }; 406 ``` 407 408 ::: 409 410 # Merge behavior 411 412 Merging requires all definition values to have the same type. 413 414 - If all definitions are booleans, the result of a `foldl'` with the `or` operation is returned. 415 - If all definitions are strings, they are concatenated. (`lib.concatStrings`) 416 - If all definitions are integers and all are equal, the first one is returned. 417 - If all definitions are lists, they are concatenated. (`++`) 418 - If all definitions are attribute sets, they are merged. (`lib.mergeAttrs`) 419 - If all definitions are functions, the first function is applied to the result of the second function. (`f -> x: f x`) 420 - Otherwise, an error is thrown. 421 */ 422 mergeDefaultOption = 423 loc: defs: 424 let 425 list = getValues defs; 426 in 427 if length list == 1 then 428 head list 429 else if all isFunction list then 430 x: mergeDefaultOption loc (map (f: f x) list) 431 else if all isList list then 432 concatLists list 433 else if all isAttrs list then 434 foldl' lib.mergeAttrs { } list 435 else if all isBool list then 436 foldl' lib.or false list 437 else if all isString list then 438 lib.concatStrings list 439 else if all isInt list && all (x: x == head list) list then 440 head list 441 else 442 throw "Cannot merge definitions of `${showOption loc}'. Definition values:${showDefs defs}"; 443 444 /** 445 Require a single definition. 446 447 WARNING: Does not perform nested checks, as this does not run the merge function! 448 */ 449 mergeOneOption = mergeUniqueOption { message = ""; }; 450 451 /** 452 Require a single definition. 453 454 NOTE: When the type is not checked completely by check, pass a merge function for further checking (of sub-attributes, etc). 455 456 # Inputs 457 458 `loc` 459 460 : 2\. Function argument 461 462 `defs` 463 464 : 3\. Function argument 465 */ 466 mergeUniqueOption = 467 args@{ 468 message, 469 # WARNING: the default merge function assumes that the definition is a valid (option) value. You MUST pass a merge function if the return value needs to be 470 # - type checked beyond what .check does (which should be very little; only on the value head; not attribute values, etc) 471 # - if you want attribute values to be checked, or list items 472 # - if you want coercedTo-like behavior to work 473 merge ? loc: defs: (head defs).value, 474 }: 475 loc: defs: 476 if length defs == 1 then 477 merge loc defs 478 else 479 assert length defs > 1; 480 throw "The option `${showOption loc}' is defined multiple times while it's expected to be unique.\n${message}\nDefinition values:${showDefs defs}\n${prioritySuggestion}"; 481 482 /** 483 "Merge" option definitions by checking that they all have the same value. 484 485 # Inputs 486 487 `loc` 488 489 : 1\. Function argument 490 491 `defs` 492 493 : 2\. Function argument 494 */ 495 mergeEqualOption = 496 loc: defs: 497 if defs == [ ] then 498 abort "This case should never happen." 499 # Returns early if we only have one element 500 # This also makes it work for functions, because the foldl' below would try 501 # to compare the first element with itself, which is false for functions 502 else if length defs == 1 then 503 (head defs).value 504 else 505 (foldl' ( 506 first: def: 507 if def.value != first.value then 508 throw "The option `${showOption loc}' has conflicting definition values:${ 509 showDefs [ 510 first 511 def 512 ] 513 }\n${prioritySuggestion}" 514 else 515 first 516 ) (head defs) (tail defs)).value; 517 518 /** 519 Extracts values of all "value" keys of the given list. 520 521 # Type 522 523 ``` 524 getValues :: [ { value :: a; } ] -> [a] 525 ``` 526 527 # Examples 528 :::{.example} 529 ## `getValues` usage example 530 531 ```nix 532 getValues [ { value = 1; } { value = 2; } ] // => [ 1 2 ] 533 getValues [ ] // => [ ] 534 ``` 535 536 ::: 537 */ 538 getValues = map (x: x.value); 539 540 /** 541 Extracts values of all "file" keys of the given list 542 543 # Type 544 545 ``` 546 getFiles :: [ { file :: a; } ] -> [a] 547 ``` 548 549 # Examples 550 :::{.example} 551 ## `getFiles` usage example 552 553 ```nix 554 getFiles [ { file = "file1"; } { file = "file2"; } ] // => [ "file1" "file2" ] 555 getFiles [ ] // => [ ] 556 ``` 557 558 ::: 559 */ 560 getFiles = map (x: x.file); 561 562 # Generate documentation template from the list of option declaration like 563 # the set generated with filterOptionSets. 564 optionAttrSetToDocList = optionAttrSetToDocList' [ ]; 565 566 optionAttrSetToDocList' = 567 _: options: 568 concatMap ( 569 opt: 570 let 571 name = showOption opt.loc; 572 visible = opt.visible or true; 573 docOption = { 574 loc = opt.loc; 575 inherit name; 576 description = opt.description or null; 577 declarations = filter (x: x != unknownModule) opt.declarations; 578 internal = opt.internal or false; 579 visible = if isBool visible then visible else visible == "shallow"; 580 readOnly = opt.readOnly or false; 581 type = opt.type.description or "unspecified"; 582 } 583 // optionalAttrs (opt ? example) { 584 example = builtins.addErrorContext "while evaluating the example of option `${name}`" ( 585 renderOptionValue opt.example 586 ); 587 } 588 // optionalAttrs (opt ? defaultText || opt ? default) { 589 default = builtins.addErrorContext "while evaluating the ${ 590 if opt ? defaultText then "defaultText" else "default value" 591 } of option `${name}`" (renderOptionValue (opt.defaultText or opt.default)); 592 } 593 // optionalAttrs (opt ? relatedPackages && opt.relatedPackages != null) { 594 inherit (opt) relatedPackages; 595 }; 596 597 subOptions = 598 let 599 ss = opt.type.getSubOptions opt.loc; 600 in 601 if ss != { } then optionAttrSetToDocList' opt.loc ss else [ ]; 602 subOptionsVisible = if isBool visible then visible else visible == "transparent"; 603 in 604 # To find infinite recursion in NixOS option docs: 605 # builtins.trace opt.loc 606 [ docOption ] ++ optionals subOptionsVisible subOptions 607 ) (collect isOption options); 608 609 /** 610 This function recursively removes all derivation attributes from 611 `x` except for the `name` attribute. 612 613 This is to make the generation of `options.xml` much more 614 efficient: the XML representation of derivations is very large 615 (on the order of megabytes) and is not actually used by the 616 manual generator. 617 618 This function was made obsolete by renderOptionValue and is kept for 619 compatibility with out-of-tree code. 620 621 # Inputs 622 623 `x` 624 625 : 1\. Function argument 626 */ 627 scrubOptionValue = 628 x: 629 if isDerivation x then 630 { 631 type = "derivation"; 632 drvPath = x.name; 633 outPath = x.name; 634 name = x.name; 635 } 636 else if isList x then 637 map scrubOptionValue x 638 else if isAttrs x then 639 mapAttrs (n: v: scrubOptionValue v) (removeAttrs x [ "_args" ]) 640 else 641 x; 642 643 /** 644 Ensures that the given option value (default or example) is a `_type`d string 645 by rendering Nix values to `literalExpression`s. 646 647 # Inputs 648 649 `v` 650 651 : 1\. Function argument 652 */ 653 renderOptionValue = 654 v: 655 if v ? _type && v ? text then 656 v 657 else 658 literalExpression ( 659 lib.generators.toPretty { 660 multiline = true; 661 allowPrettyValues = true; 662 } v 663 ); 664 665 /** 666 For use in the `defaultText` and `example` option attributes. Causes the 667 given string to be rendered verbatim in the documentation as Nix code. This 668 is necessary for complex values, e.g. functions, or values that depend on 669 other values or packages. 670 671 # Inputs 672 673 `text` 674 675 : 1\. Function argument 676 */ 677 literalExpression = 678 text: 679 if !isString text then 680 throw "literalExpression expects a string." 681 else 682 { 683 _type = "literalExpression"; 684 inherit text; 685 }; 686 687 literalExample = lib.warn "lib.literalExample is deprecated, use lib.literalExpression instead, or use lib.literalMD for a non-Nix description." literalExpression; 688 689 /** 690 For use in the `defaultText` and `example` option attributes. Causes the 691 given MD text to be inserted verbatim in the documentation, for when 692 a `literalExpression` would be too hard to read. 693 694 # Inputs 695 696 `text` 697 698 : 1\. Function argument 699 */ 700 literalMD = 701 text: 702 if !isString text then 703 throw "literalMD expects a string." 704 else 705 { 706 _type = "literalMD"; 707 inherit text; 708 }; 709 710 # Helper functions. 711 712 /** 713 Convert an option, described as a list of the option parts to a 714 human-readable version. 715 716 # Inputs 717 718 `parts` 719 720 : 1\. Function argument 721 722 # Examples 723 :::{.example} 724 ## `showOption` usage example 725 726 ```nix 727 (showOption ["foo" "bar" "baz"]) == "foo.bar.baz" 728 (showOption ["foo" "bar.baz" "tux"]) == "foo.\"bar.baz\".tux" 729 (showOption ["windowManager" "2bwm" "enable"]) == "windowManager.\"2bwm\".enable" 730 731 Placeholders will not be quoted as they are not actual values: 732 (showOption ["foo" "*" "bar"]) == "foo.*.bar" 733 (showOption ["foo" "<name>" "bar"]) == "foo.<name>.bar" 734 (showOption ["foo" "<myPlaceholder>" "bar"]) == "foo.<myPlaceholder>.bar" 735 ``` 736 737 ::: 738 */ 739 showOption = 740 parts: 741 let 742 # If the part is a named placeholder of the form "<...>" don't escape it. 743 # It may cause misleading escaping if somebody uses literally "<...>" in their option names. 744 # This is the trade-off to allow for placeholders in option names. 745 isNamedPlaceholder = builtins.match "<(.*)>"; 746 escapeOptionPart = 747 part: 748 if part == "*" || isNamedPlaceholder part != null then 749 part 750 else 751 lib.strings.escapeNixIdentifier part; 752 in 753 (concatStringsSep ".") (map escapeOptionPart parts); 754 showFiles = files: concatStringsSep " and " (map (f: "`${f}'") files); 755 756 showDefs = 757 defs: 758 concatMapStrings ( 759 def: 760 let 761 # Pretty print the value for display, if successful 762 prettyEval = builtins.tryEval ( 763 lib.generators.toPretty { } ( 764 lib.generators.withRecursion { 765 depthLimit = 10; 766 throwOnDepthLimit = false; 767 } def.value 768 ) 769 ); 770 # Split it into its lines 771 lines = filter (v: !isList v) (builtins.split "\n" prettyEval.value); 772 # Only display the first 5 lines, and indent them for better visibility 773 value = concatStringsSep "\n " (take 5 lines ++ optional (length lines > 5) "..."); 774 result = 775 # Don't print any value if evaluating the value strictly fails 776 if !prettyEval.success then 777 "" 778 # Put it on a new line if it consists of multiple 779 else if length lines > 1 then 780 ":\n " + value 781 else 782 ": " + value; 783 in 784 "\n- In `${def.file}'${result}" 785 ) defs; 786 787 /** 788 Pretty prints all option definition locations 789 790 # Inputs 791 792 `option` 793 : The option to pretty print 794 795 # Examples 796 :::{.example} 797 ## `lib.options.showOptionWithDefLocs` usage example 798 799 ```nix 800 showOptionWithDefLocs { loc = ["x" "y" ]; files = [ "foo.nix" "bar.nix" ]; } 801 "x.y, with values defined in:\n - foo.nix\n - bar.nix\n" 802 ``` 803 804 ```nix 805 nix-repl> eval = lib.evalModules { 806 modules = [ 807 { 808 options = { 809 foo = lib.mkEnableOption "foo"; 810 }; 811 } 812 ]; 813 } 814 815 nix-repl> lib.options.showOptionWithDefLocs eval.options.foo 816 "foo, with values defined in:\n - <unknown-file>\n" 817 ``` 818 819 ::: 820 821 # Type 822 823 ``` 824 showDefsSep :: { files :: [ String ]; loc :: [ String ]; ... } -> string 825 ``` 826 */ 827 showOptionWithDefLocs = opt: '' 828 ${showOption opt.loc}, with values defined in: 829 ${concatMapStringsSep "\n" (defFile: " - ${defFile}") opt.files} 830 ''; 831 832 unknownModule = "<unknown-file>"; 833 834}