Clone of https://github.com/NixOS/nixpkgs.git (to stress-test knotserver)
1{ lib }: 2 3let 4 inherit (lib) 5 addErrorContext 6 all 7 any 8 attrByPath 9 attrNames 10 catAttrs 11 concatLists 12 concatMap 13 concatStringsSep 14 elem 15 filter 16 foldl' 17 functionArgs 18 getAttrFromPath 19 genericClosure 20 head 21 id 22 imap1 23 isAttrs 24 isBool 25 isFunction 26 oldestSupportedReleaseIsAtLeast 27 isList 28 isString 29 length 30 mapAttrs 31 mapAttrsToList 32 mapAttrsRecursiveCond 33 min 34 optional 35 optionalAttrs 36 optionalString 37 recursiveUpdate 38 reverseList 39 sort 40 seq 41 setAttrByPath 42 substring 43 throwIfNot 44 trace 45 typeOf 46 types 47 unsafeGetAttrPos 48 warn 49 warnIf 50 zipAttrs 51 zipAttrsWith 52 ; 53 inherit (lib.options) 54 isOption 55 mkOption 56 showDefs 57 showFiles 58 showOption 59 unknownModule 60 ; 61 inherit (lib.strings) 62 isConvertibleWithToString 63 ; 64 65 showDeclPrefix = 66 loc: decl: prefix: 67 " - option(s) with prefix `${showOption (loc ++ [ prefix ])}' in module `${decl._file}'"; 68 showRawDecls = 69 loc: decls: 70 concatStringsSep "\n" ( 71 sort (a: b: a < b) (concatMap (decl: map (showDeclPrefix loc decl) (attrNames decl.options)) decls) 72 ); 73 74 /** 75 See https://nixos.org/manual/nixpkgs/unstable/#module-system-lib-evalModules 76 or file://./../doc/module-system/module-system.chapter.md 77 78 !!! Please think twice before adding to this argument list! The more 79 that is specified here instead of in the modules themselves the harder 80 it is to transparently move a set of modules to be a submodule of another 81 config (as the proper arguments need to be replicated at each call to 82 evalModules) and the less declarative the module set is. 83 */ 84 evalModules = 85 evalModulesArgs@{ 86 modules, 87 prefix ? [ ], 88 # This should only be used for special arguments that need to be evaluated 89 # when resolving module structure (like in imports). For everything else, 90 # there's _module.args. If specialArgs.modulesPath is defined it will be 91 # used as the base path for disabledModules. 92 specialArgs ? { }, 93 # `class`: 94 # A nominal type for modules. When set and non-null, this adds a check to 95 # make sure that only compatible modules are imported. 96 class ? null, 97 # This would be remove in the future, Prefer _module.args option instead. 98 args ? { }, 99 # This would be remove in the future, Prefer _module.check option instead. 100 check ? true, 101 }: 102 let 103 withWarnings = 104 x: 105 warnIf (evalModulesArgs ? args) 106 "The args argument to evalModules is deprecated. Please set config._module.args instead." 107 warnIf 108 (evalModulesArgs ? check) 109 "The check argument to evalModules is deprecated. Please set config._module.check instead." 110 x; 111 112 legacyModules = 113 optional (evalModulesArgs ? args) { 114 config = { 115 _module.args = args; 116 }; 117 } 118 ++ optional (evalModulesArgs ? check) { 119 config = { 120 _module.check = mkDefault check; 121 }; 122 }; 123 regularModules = modules ++ legacyModules; 124 125 # This internal module declare internal options under the `_module' 126 # attribute. These options are fragile, as they are used by the 127 # module system to change the interpretation of modules. 128 # 129 # When extended with extendModules or moduleType, a fresh instance of 130 # this module is used, to avoid conflicts and allow chaining of 131 # extendModules. 132 internalModule = rec { 133 _file = "lib/modules.nix"; 134 135 key = _file; 136 137 options = { 138 _module.args = mkOption { 139 # Because things like `mkIf` are entirely useless for 140 # `_module.args` (because there's no way modules can check which 141 # arguments were passed), we'll use `lazyAttrsOf` which drops 142 # support for that, in turn it's lazy in its values. This means e.g. 143 # a `_module.args.pkgs = import (fetchTarball { ... }) {}` won't 144 # start a download when `pkgs` wasn't evaluated. 145 type = types.lazyAttrsOf types.raw; 146 # Only render documentation once at the root of the option tree, 147 # not for all individual submodules. 148 # Allow merging option decls to make this internal regardless. 149 ${ 150 if prefix == [ ] then 151 null # unset => visible 152 else 153 "internal" 154 } = 155 true; 156 # TODO: Change the type of this option to a submodule with a 157 # freeformType, so that individual arguments can be documented 158 # separately 159 description = '' 160 Additional arguments passed to each module in addition to ones 161 like `lib`, `config`, 162 and `pkgs`, `modulesPath`. 163 164 This option is also available to all submodules. Submodules do not 165 inherit args from their parent module, nor do they provide args to 166 their parent module or sibling submodules. The sole exception to 167 this is the argument `name` which is provided by 168 parent modules to a submodule and contains the attribute name 169 the submodule is bound to, or a unique generated name if it is 170 not bound to an attribute. 171 172 Some arguments are already passed by default, of which the 173 following *cannot* be changed with this option: 174 - {var}`lib`: The nixpkgs library. 175 - {var}`config`: The results of all options after merging the values from all modules together. 176 - {var}`options`: The options declared in all modules. 177 - {var}`specialArgs`: The `specialArgs` argument passed to `evalModules`. 178 - All attributes of {var}`specialArgs` 179 180 Whereas option values can generally depend on other option values 181 thanks to laziness, this does not apply to `imports`, which 182 must be computed statically before anything else. 183 184 For this reason, callers of the module system can provide `specialArgs` 185 which are available during import resolution. 186 187 For NixOS, `specialArgs` includes 188 {var}`modulesPath`, which allows you to import 189 extra modules from the nixpkgs package tree without having to 190 somehow make the module aware of the location of the 191 `nixpkgs` or NixOS directories. 192 ``` 193 { modulesPath, ... }: { 194 imports = [ 195 (modulesPath + "/profiles/minimal.nix") 196 ]; 197 } 198 ``` 199 200 For NixOS, the default value for this option includes at least this argument: 201 - {var}`pkgs`: The nixpkgs package set according to 202 the {option}`nixpkgs.pkgs` option. 203 ''; 204 }; 205 206 _module.check = mkOption { 207 type = types.bool; 208 internal = true; 209 default = true; 210 description = "Whether to check whether all option definitions have matching declarations."; 211 }; 212 213 _module.freeformType = mkOption { 214 type = types.nullOr types.optionType; 215 internal = true; 216 default = null; 217 description = '' 218 If set, merge all definitions that don't have an associated option 219 together using this type. The result then gets combined with the 220 values of all declared options to produce the final ` 221 config` value. 222 223 If this is `null`, definitions without an option 224 will throw an error unless {option}`_module.check` is 225 turned off. 226 ''; 227 }; 228 229 _module.specialArgs = mkOption { 230 readOnly = true; 231 internal = true; 232 description = '' 233 Externally provided module arguments that can't be modified from 234 within a configuration, but can be used in module imports. 235 ''; 236 }; 237 }; 238 239 config = { 240 _module.args = { 241 inherit extendModules; 242 moduleType = type; 243 }; 244 _module.specialArgs = specialArgs; 245 }; 246 }; 247 248 merged = 249 let 250 collected = 251 collectModules class (specialArgs.modulesPath or "") (regularModules ++ [ internalModule ]) 252 ( 253 { 254 inherit 255 lib 256 options 257 config 258 specialArgs 259 ; 260 _class = class; 261 _prefix = prefix; 262 } 263 // specialArgs 264 ); 265 in 266 mergeModules prefix (reverseList collected); 267 268 options = merged.matchedOptions; 269 270 config = 271 let 272 273 # For definitions that have an associated option 274 declaredConfig = mapAttrsRecursiveCond (v: !isOption v) (_: v: v.value) options; 275 276 # If freeformType is set, this is for definitions that don't have an associated option 277 freeformConfig = 278 let 279 defs = map (def: { 280 file = def.file; 281 value = setAttrByPath def.prefix def.value; 282 }) merged.unmatchedDefns; 283 in 284 if defs == [ ] then { } else declaredConfig._module.freeformType.merge prefix defs; 285 286 in 287 if declaredConfig._module.freeformType == null then 288 declaredConfig 289 # Because all definitions that had an associated option ended in 290 # declaredConfig, freeformConfig can only contain the non-option 291 # paths, meaning recursiveUpdate will never override any value 292 else 293 recursiveUpdate freeformConfig declaredConfig; 294 295 checkUnmatched = 296 if config._module.check && config._module.freeformType == null && merged.unmatchedDefns != [ ] then 297 let 298 firstDef = head merged.unmatchedDefns; 299 baseMsg = 300 let 301 optText = showOption (prefix ++ firstDef.prefix); 302 defText = 303 addErrorContext 304 "while evaluating the error message for definitions for `${optText}', which is an option that does not exist" 305 (addErrorContext "while evaluating a definition from `${firstDef.file}'" (showDefs [ firstDef ])); 306 in 307 "The option `${optText}' does not exist. Definition values:${defText}"; 308 in 309 if 310 attrNames options == [ "_module" ] 311 # No options were declared at all (`_module` is built in) 312 # but we do have unmatched definitions, and no freeformType (earlier conditions) 313 then 314 let 315 optionName = showOption prefix; 316 in 317 if optionName == "" then 318 throw '' 319 ${baseMsg} 320 321 It seems as if you're trying to declare an option by placing it into `config' rather than `options'! 322 '' 323 else 324 throw '' 325 ${baseMsg} 326 327 However there are no options defined in `${showOption prefix}'. Are you sure you've 328 declared your options properly? This can happen if you e.g. declared your options in `types.submodule' 329 under `config' rather than `options'. 330 '' 331 else 332 throw baseMsg 333 else 334 null; 335 336 checked = seq checkUnmatched; 337 338 extendModules = 339 extendArgs@{ 340 modules ? [ ], 341 specialArgs ? { }, 342 prefix ? [ ], 343 }: 344 evalModules ( 345 evalModulesArgs 346 // { 347 modules = regularModules ++ modules; 348 specialArgs = evalModulesArgs.specialArgs or { } // specialArgs; 349 prefix = extendArgs.prefix or evalModulesArgs.prefix or [ ]; 350 } 351 ); 352 353 type = types.submoduleWith { 354 inherit modules specialArgs class; 355 }; 356 357 result = withWarnings { 358 _type = "configuration"; 359 options = checked options; 360 config = checked (removeAttrs config [ "_module" ]); 361 _module = checked (config._module); 362 inherit extendModules type class; 363 }; 364 in 365 result; 366 367 # collectModules :: (class: String) -> (modulesPath: String) -> (modules: [ Module ]) -> (args: Attrs) -> [ Module ] 368 # 369 # Collects all modules recursively through `import` statements, filtering out 370 # all modules in disabledModules. 371 collectModules = 372 class: 373 let 374 375 # Like unifyModuleSyntax, but also imports paths and calls functions if necessary 376 loadModule = 377 args: fallbackFile: fallbackKey: m: 378 if isFunction m then 379 unifyModuleSyntax fallbackFile fallbackKey (applyModuleArgs fallbackKey m args) 380 else if isAttrs m then 381 if m._type or "module" == "module" then 382 unifyModuleSyntax fallbackFile fallbackKey m 383 else if m._type == "if" || m._type == "override" then 384 loadModule args fallbackFile fallbackKey { config = m; } 385 else 386 throw ( 387 messages.not_a_module { 388 inherit fallbackFile; 389 value = m; 390 _type = m._type; 391 expectedClass = class; 392 prefix = args._prefix; 393 } 394 ) 395 else if isList m then 396 let 397 defs = [ 398 { 399 file = fallbackFile; 400 value = m; 401 } 402 ]; 403 in 404 throw "Module imports can't be nested lists. Perhaps you meant to remove one level of lists? Definitions: ${showDefs defs}" 405 else 406 unifyModuleSyntax (toString m) (toString m) ( 407 applyModuleArgsIfFunction (toString m) (import m) args 408 ); 409 410 checkModule = 411 if class != null then 412 m: 413 if m._class == null || m._class == class then 414 m 415 else 416 throw '' 417 The module `${m._file or m.key}` (class: ${lib.strings.escapeNixString m._class}) cannot be imported into a module evaluation that expects class ${lib.strings.escapeNixString class}. 418 419 Help: 420 - Ensure that you are importing the correct module. 421 - Verify that the module's `_class`, ${lib.strings.escapeNixString m._class} matches the expected `class` ${lib.strings.escapeNixString class}. 422 - If you are using a custom class, make sure it is correctly defined and used consistently across your modules. 423 '' 424 else 425 m: m; 426 427 /** 428 Collects all modules recursively into the form 429 430 { 431 disabled = [ <list of disabled modules> ]; 432 # All modules of the main module list 433 modules = [ 434 { 435 key = <key1>; 436 module = <module for key1>; 437 # All modules imported by the module for key1 438 modules = [ 439 { 440 key = <key1-1>; 441 module = <module for key1-1>; 442 # All modules imported by the module for key1-1 443 modules = [ ... ]; 444 } 445 ... 446 ]; 447 } 448 ... 449 ]; 450 } 451 */ 452 collectStructuredModules = 453 let 454 collectResults = modules: { 455 disabled = concatLists (catAttrs "disabled" modules); 456 inherit modules; 457 }; 458 in 459 parentFile: parentKey: initialModules: args: 460 collectResults ( 461 imap1 ( 462 n: x: 463 let 464 module = checkModule (loadModule args parentFile "${parentKey}:anon-${toString n}" x); 465 collectedImports = collectStructuredModules module._file module.key module.imports args; 466 in 467 { 468 key = module.key; 469 module = module; 470 modules = collectedImports.modules; 471 disabled = 472 ( 473 if module.disabledModules != [ ] then 474 [ 475 { 476 file = module._file; 477 disabled = module.disabledModules; 478 } 479 ] 480 else 481 [ ] 482 ) 483 ++ collectedImports.disabled; 484 } 485 ) initialModules 486 ); 487 488 # filterModules :: String -> { disabled, modules } -> [ Module ] 489 # 490 # Filters a structure as emitted by collectStructuredModules by removing all disabled 491 # modules recursively. It returns the final list of unique-by-key modules 492 filterModules = 493 modulesPath: 494 { disabled, modules }: 495 let 496 moduleKey = 497 file: m: 498 if isString m then 499 if substring 0 1 m == "/" then m else toString modulesPath + "/" + m 500 501 else if isConvertibleWithToString m then 502 if m ? key && m.key != toString m then 503 throw "Module `${file}` contains a disabledModules item that is an attribute set that can be converted to a string (${toString m}) but also has a `.key` attribute (${m.key}) with a different value. This makes it ambiguous which module should be disabled." 504 else 505 toString m 506 507 else if m ? key then 508 m.key 509 510 else if isAttrs m then 511 throw "Module `${file}` contains a disabledModules item that is an attribute set, presumably a module, that does not have a `key` attribute. This means that the module system doesn't have any means to identify the module that should be disabled. Make sure that you've put the correct value in disabledModules: a string path relative to modulesPath, a path value, or an attribute set with a `key` attribute." 512 else 513 throw "Each disabledModules item must be a path, string, or a attribute set with a key attribute, or a value supported by toString. However, one of the disabledModules items in `${toString file}` is none of that, but is of type ${typeOf m}."; 514 515 disabledKeys = concatMap ({ file, disabled }: map (moduleKey file) disabled) disabled; 516 keyFilter = filter (attrs: !elem attrs.key disabledKeys); 517 in 518 map (attrs: attrs.module) (genericClosure { 519 startSet = keyFilter modules; 520 operator = attrs: keyFilter attrs.modules; 521 }); 522 523 in 524 modulesPath: initialModules: args: 525 filterModules modulesPath (collectStructuredModules unknownModule "" initialModules args); 526 527 /** 528 Wrap a module with a default location for reporting errors. 529 530 # Inputs 531 532 `file` 533 534 : 1\. Function argument 535 536 `m` 537 538 : 2\. Function argument 539 */ 540 setDefaultModuleLocation = file: m: { 541 _file = file; 542 imports = [ m ]; 543 }; 544 545 /** 546 Massage a module into canonical form, that is, a set consisting 547 of options, config and imports attributes. 548 549 # Inputs 550 551 `file` 552 553 : 1\. Function argument 554 555 `key` 556 557 : 2\. Function argument 558 559 `m` 560 561 : 3\. Function argument 562 */ 563 unifyModuleSyntax = 564 file: key: m: 565 let 566 addMeta = 567 config: 568 if m ? meta then 569 mkMerge [ 570 config 571 { meta = m.meta; } 572 ] 573 else 574 config; 575 addFreeformType = 576 config: 577 if m ? freeformType then 578 mkMerge [ 579 config 580 { _module.freeformType = m.freeformType; } 581 ] 582 else 583 config; 584 in 585 if m ? config || m ? options then 586 let 587 badAttrs = removeAttrs m [ 588 "_class" 589 "_file" 590 "key" 591 "disabledModules" 592 "imports" 593 "options" 594 "config" 595 "meta" 596 "freeformType" 597 ]; 598 in 599 if badAttrs != { } then 600 throw "Module `${key}' has an unsupported attribute `${head (attrNames badAttrs)}'. This is caused by introducing a top-level `config' or `options' attribute. Add configuration attributes immediately on the top level instead, or move all of them (namely: ${toString (attrNames badAttrs)}) into the explicit `config' attribute." 601 else 602 { 603 _file = toString m._file or file; 604 _class = m._class or null; 605 key = toString m.key or key; 606 disabledModules = m.disabledModules or [ ]; 607 imports = m.imports or [ ]; 608 options = m.options or { }; 609 config = addFreeformType (addMeta (m.config or { })); 610 } 611 else 612 # shorthand syntax 613 throwIfNot (isAttrs m) "module ${file} (${key}) does not look like a module." { 614 _file = toString m._file or file; 615 _class = m._class or null; 616 key = toString m.key or key; 617 disabledModules = m.disabledModules or [ ]; 618 imports = m.require or [ ] ++ m.imports or [ ]; 619 options = { }; 620 config = addFreeformType ( 621 removeAttrs m [ 622 "_class" 623 "_file" 624 "key" 625 "disabledModules" 626 "require" 627 "imports" 628 "freeformType" 629 ] 630 ); 631 }; 632 633 applyModuleArgsIfFunction = 634 key: f: args@{ config, ... }: if isFunction f then applyModuleArgs key f args else f; 635 636 applyModuleArgs = 637 key: f: 638 args@{ config, ... }: 639 let 640 # Module arguments are resolved in a strict manner when attribute set 641 # deconstruction is used. As the arguments are now defined with the 642 # config._module.args option, the strictness used on the attribute 643 # set argument would cause an infinite loop, if the result of the 644 # option is given as argument. 645 # 646 # To work-around the strictness issue on the deconstruction of the 647 # attributes set argument, we create a new attribute set which is 648 # constructed to satisfy the expected set of attributes. Thus calling 649 # a module will resolve strictly the attributes used as argument but 650 # not their values. The values are forwarding the result of the 651 # evaluation of the option. 652 context = name: ''while evaluating the module argument `${name}' in "${key}":''; 653 extraArgs = mapAttrs ( 654 name: _: addErrorContext (context name) (args.${name} or config._module.args.${name}) 655 ) (functionArgs f); 656 657 # Note: we append in the opposite order such that we can add an error 658 # context on the explicit arguments of "args" too. This update 659 # operator is used to make the "args@{ ... }: with args.lib;" notation 660 # works. 661 in 662 f (args // extraArgs); 663 664 /** 665 Merge a list of modules. This will recurse over the option 666 declarations in all modules, combining them into a single set. 667 At the same time, for each option declaration, it will merge the 668 corresponding option definitions in all machines, returning them 669 in the value attribute of each option. 670 671 This returns a set like 672 { 673 # A recursive set of options along with their final values 674 matchedOptions = { 675 foo = { _type = "option"; value = "option value of foo"; ... }; 676 bar.baz = { _type = "option"; value = "option value of bar.baz"; ... }; 677 ... 678 }; 679 # A list of definitions that weren't matched by any option 680 unmatchedDefns = [ 681 { file = "file.nix"; prefix = [ "qux" ]; value = "qux"; } 682 ... 683 ]; 684 } 685 686 # Inputs 687 688 `prefix` 689 690 : 1\. Function argument 691 692 `modules` 693 694 : 2\. Function argument 695 */ 696 mergeModules = 697 prefix: modules: 698 mergeModules' prefix modules ( 699 concatMap ( 700 m: 701 map (config: { 702 file = m._file; 703 inherit config; 704 }) (pushDownProperties m.config) 705 ) modules 706 ); 707 708 mergeModules' = 709 prefix: modules: configs: 710 let 711 # an attrset 'name' => list of submodules that declare ‘name’. 712 declsByName = zipAttrsWith (n: v: v) ( 713 map ( 714 module: 715 let 716 subtree = module.options; 717 in 718 if !(isAttrs subtree) then 719 throw '' 720 An option declaration for `${concatStringsSep "." prefix}' has type 721 `${typeOf subtree}' rather than an attribute set. 722 Did you mean to define this outside of `options'? 723 '' 724 else 725 mapAttrs (n: option: { 726 inherit (module) _file; 727 pos = unsafeGetAttrPos n subtree; 728 options = option; 729 }) subtree 730 ) modules 731 ); 732 733 # The root of any module definition must be an attrset. 734 checkedConfigs = 735 assert all ( 736 c: 737 # TODO: I have my doubts that this error would occur when option definitions are not matched. 738 # The implementation of this check used to be tied to a superficially similar check for 739 # options, so maybe that's why this is here. 740 isAttrs c.config 741 || throw '' 742 In module `${c.file}', you're trying to define a value of type `${typeOf c.config}' 743 rather than an attribute set for the option 744 `${concatStringsSep "." prefix}'! 745 746 This usually happens if `${concatStringsSep "." prefix}' has option 747 definitions inside that are not matched. Please check how to properly define 748 this option by e.g. referring to `man 5 configuration.nix'! 749 '' 750 ) configs; 751 configs; 752 753 # an attrset 'name' => list of submodules that define ‘name’. 754 pushedDownDefinitionsByName = zipAttrsWith (n: concatLists) ( 755 map ( 756 module: 757 mapAttrs ( 758 n: value: 759 map (config: { 760 inherit (module) file; 761 inherit config; 762 }) (pushDownProperties value) 763 ) module.config 764 ) checkedConfigs 765 ); 766 # extract the definitions for each loc 767 rawDefinitionsByName = zipAttrsWith (n: v: v) ( 768 map ( 769 module: 770 mapAttrs (n: value: { 771 inherit (module) file; 772 inherit value; 773 }) module.config 774 ) checkedConfigs 775 ); 776 777 # Convert an option tree decl to a submodule option decl 778 optionTreeToOption = 779 decl: 780 if isOption decl.options then 781 decl 782 else 783 decl 784 // { 785 options = mkOption { 786 type = types.submoduleWith { 787 modules = [ { options = decl.options; } ]; 788 # `null` is not intended for use by modules. It is an internal 789 # value that means "whatever the user has declared elsewhere". 790 # This might become obsolete with https://github.com/NixOS/nixpkgs/issues/162398 791 shorthandOnlyDefinesConfig = null; 792 }; 793 }; 794 }; 795 796 resultsByName = mapAttrs ( 797 name: decls: 798 # We're descending into attribute ‘name’. 799 let 800 loc = prefix ++ [ name ]; 801 defns = pushedDownDefinitionsByName.${name} or [ ]; 802 defns' = rawDefinitionsByName.${name} or [ ]; 803 optionDecls = filter ( 804 m: 805 m.options ? _type 806 && (m.options._type == "option" || throwDeclarationTypeError loc m.options._type m._file) 807 ) decls; 808 in 809 if length optionDecls == length decls then 810 let 811 opt = fixupOptionType loc (mergeOptionDecls loc decls); 812 in 813 { 814 matchedOptions = evalOptionValue loc opt defns'; 815 unmatchedDefns = [ ]; 816 } 817 else if optionDecls != [ ] then 818 if 819 all (x: x.options.type.name or null == "submodule") optionDecls 820 # Raw options can only be merged into submodules. Merging into 821 # attrsets might be nice, but ambiguous. Suppose we have 822 # attrset as a `attrsOf submodule`. User declares option 823 # attrset.foo.bar, this could mean: 824 # a. option `bar` is only available in `attrset.foo` 825 # b. option `foo.bar` is available in all `attrset.*` 826 # c. reject and require "<name>" as a reminder that it behaves like (b). 827 # d. magically combine (a) and (c). 828 # All of the above are merely syntax sugar though. 829 then 830 let 831 opt = fixupOptionType loc (mergeOptionDecls loc (map optionTreeToOption decls)); 832 in 833 { 834 matchedOptions = evalOptionValue loc opt defns'; 835 unmatchedDefns = [ ]; 836 } 837 else 838 let 839 nonOptions = filter (m: !isOption m.options) decls; 840 in 841 throw "The option `${showOption loc}' in module `${(head optionDecls)._file}' would be a parent of the following options, but its type `${ 842 (head optionDecls).options.type.description or "<no description>" 843 }' does not support nested options.\n${showRawDecls loc nonOptions}" 844 else 845 mergeModules' loc decls defns 846 ) declsByName; 847 848 matchedOptions = mapAttrs (n: v: v.matchedOptions) resultsByName; 849 850 # an attrset 'name' => list of unmatched definitions for 'name' 851 unmatchedDefnsByName = 852 # Propagate all unmatched definitions from nested option sets 853 mapAttrs (n: v: v.unmatchedDefns) resultsByName 854 # Plus the definitions for the current prefix that don't have a matching option 855 // removeAttrs rawDefinitionsByName (attrNames matchedOptions); 856 in 857 { 858 inherit matchedOptions; 859 860 # Transforms unmatchedDefnsByName into a list of definitions 861 unmatchedDefns = 862 if configs == [ ] then 863 # When no config values exist, there can be no unmatched config, so 864 # we short circuit and avoid evaluating more _options_ than necessary. 865 [ ] 866 else 867 concatLists ( 868 mapAttrsToList ( 869 name: defs: 870 map ( 871 def: 872 def 873 // { 874 # Set this so we know when the definition first left unmatched territory 875 prefix = [ name ] ++ (def.prefix or [ ]); 876 } 877 ) defs 878 ) unmatchedDefnsByName 879 ); 880 }; 881 882 throwDeclarationTypeError = 883 loc: actualTag: file: 884 let 885 name = lib.strings.escapeNixIdentifier (lib.lists.last loc); 886 path = showOption loc; 887 depth = length loc; 888 889 paragraphs = [ 890 "In module ${file}: expected an option declaration at option path `${path}` but got an attribute set with type ${actualTag}" 891 ] 892 ++ optional (actualTag == "option-type") '' 893 When declaring an option, you must wrap the type in a `mkOption` call. It should look somewhat like: 894 ${comment} 895 ${name} = lib.mkOption { 896 description = ...; 897 type = <the type you wrote for ${name}>; 898 ... 899 }; 900 ''; 901 902 # Ideally we'd know the exact syntax they used, but short of that, 903 # we can only reliably repeat the last. However, we repeat the 904 # full path in a non-misleading way here, in case they overlook 905 # the start of the message. Examples attract attention. 906 comment = optionalString (depth > 1) "\n # ${showOption loc}"; 907 in 908 throw (concatStringsSep "\n\n" paragraphs); 909 910 /** 911 Merge multiple option declarations into a single declaration. In 912 general, there should be only one declaration of each option. 913 The exception is the options attribute, which specifies 914 sub-options. These can be specified multiple times to allow one 915 module to add sub-options to an option declared somewhere else 916 (e.g. multiple modules define sub-options for fileSystems). 917 918 'loc' is the list of attribute names where the option is located. 919 920 'opts' is a list of modules. Each module has an options attribute which 921 correspond to the definition of 'loc' in 'opt.file'. 922 923 # Inputs 924 925 `loc` 926 927 : 1\. Function argument 928 929 `opts` 930 931 : 2\. Function argument 932 */ 933 mergeOptionDecls = 934 loc: opts: 935 foldl' 936 ( 937 res: opt: 938 let 939 t = res.type; 940 t' = opt.options.type; 941 mergedType = t.typeMerge t'.functor; 942 typesMergeable = mergedType != null; 943 944 # TODO: Remove this when all downstream reliances of internals: 'functor.wrapped' are sufficiently migrated. 945 # A function that adds the deprecated wrapped message to a type. 946 addDeprecatedWrapped = 947 t: 948 t 949 // { 950 functor = t.functor // { 951 wrapped = t.functor.wrappedDeprecationMessage { 952 inherit loc; 953 }; 954 }; 955 }; 956 957 typeSet = 958 if opt.options ? type then 959 if res ? type then 960 if typesMergeable then 961 { 962 type = 963 if mergedType ? functor.wrappedDeprecationMessage then 964 addDeprecatedWrapped mergedType 965 else 966 mergedType; 967 } 968 else 969 # Keep in sync with the same error below! 970 throw 971 "The option `${showOption loc}' in `${opt._file}' is already declared in ${showFiles res.declarations}." 972 else if opt.options.type ? functor.wrappedDeprecationMessage then 973 { type = addDeprecatedWrapped opt.options.type; } 974 else 975 { } 976 else 977 { }; 978 979 bothHave = k: opt.options ? ${k} && res ? ${k}; 980 in 981 if bothHave "default" || bothHave "example" || bothHave "description" || bothHave "apply" then 982 # Keep in sync with the same error above! 983 throw 984 "The option `${showOption loc}' in `${opt._file}' is already declared in ${showFiles res.declarations}." 985 else 986 let 987 getSubModules = opt.options.type.getSubModules or null; 988 submodules = 989 if getSubModules != null then 990 map (setDefaultModuleLocation opt._file) getSubModules ++ res.options 991 else 992 res.options; 993 in 994 opt.options 995 // res 996 // { 997 declarations = res.declarations ++ [ opt._file ]; 998 # In the case of modules that are generated dynamically, we won't 999 # have exact declaration lines; fall back to just the file being 1000 # evaluated. 1001 declarationPositions = 1002 res.declarationPositions 1003 ++ ( 1004 if opt.pos != null then 1005 [ opt.pos ] 1006 else 1007 [ 1008 { 1009 file = opt._file; 1010 line = null; 1011 column = null; 1012 } 1013 ] 1014 ); 1015 options = submodules; 1016 } 1017 // typeSet 1018 ) 1019 { 1020 inherit loc; 1021 declarations = [ ]; 1022 declarationPositions = [ ]; 1023 options = [ ]; 1024 } 1025 opts; 1026 1027 /** 1028 Merge all the definitions of an option to produce the final 1029 config value. 1030 1031 # Inputs 1032 1033 `loc` 1034 1035 : 1\. Function argument 1036 1037 `opt` 1038 1039 : 2\. Function argument 1040 1041 `defs` 1042 1043 : 3\. Function argument 1044 */ 1045 evalOptionValue = 1046 loc: opt: defs: 1047 let 1048 # Add in the default value for this option, if any. 1049 defs' = 1050 (optional (opt ? default) { 1051 file = head opt.declarations; 1052 value = mkOptionDefault opt.default; 1053 }) 1054 ++ defs; 1055 1056 # Handle properties, check types, and merge everything together. 1057 res = 1058 if opt.readOnly or false && length defs' > 1 then 1059 let 1060 # For a better error message, evaluate all readOnly definitions as 1061 # if they were the only definition. 1062 separateDefs = map ( 1063 def: 1064 def 1065 // { 1066 value = (mergeDefinitions loc opt.type [ def ]).mergedValue; 1067 } 1068 ) defs'; 1069 in 1070 throw "The option `${showOption loc}' is read-only, but it's set multiple times. Definition values:${showDefs separateDefs}" 1071 else 1072 mergeDefinitions loc opt.type defs'; 1073 1074 # Apply the 'apply' function to the merged value. This allows options to 1075 # yield a value computed from the definitions 1076 value = if opt ? apply then opt.apply res.mergedValue else res.mergedValue; 1077 1078 warnDeprecation = 1079 warnIf (opt.type.deprecationMessage != null) 1080 "The type `types.${opt.type.name}' of option `${showOption loc}' defined in ${showFiles opt.declarations} is deprecated. ${opt.type.deprecationMessage}"; 1081 1082 in 1083 warnDeprecation opt 1084 // { 1085 value = addErrorContext "while evaluating the option `${showOption loc}':" value; 1086 inherit (res.defsFinal') highestPrio; 1087 definitions = map (def: def.value) res.defsFinal; 1088 files = map (def: def.file) res.defsFinal; 1089 definitionsWithLocations = res.defsFinal; 1090 inherit (res) isDefined; 1091 # This allows options to be correctly displayed using `${options.path.to.it}` 1092 __toString = _: showOption loc; 1093 }; 1094 1095 # Merge definitions of a value of a given type. 1096 mergeDefinitions = loc: type: defs: rec { 1097 defsFinal' = 1098 let 1099 # Process mkMerge and mkIf properties. 1100 defs' = concatMap ( 1101 m: 1102 map ( 1103 value: 1104 if value._type or null == "definition" then 1105 value 1106 else 1107 { 1108 inherit (m) file; 1109 inherit value; 1110 } 1111 ) (addErrorContext "while evaluating definitions from `${m.file}':" (dischargeProperties m.value)) 1112 ) defs; 1113 1114 # Process mkOverride properties. 1115 defs'' = filterOverrides' defs'; 1116 1117 # Sort mkOrder properties. 1118 defs''' = 1119 # Avoid sorting if we don't have to. 1120 if any (def: def.value._type or "" == "order") defs''.values then 1121 sortProperties defs''.values 1122 else 1123 defs''.values; 1124 in 1125 { 1126 values = defs'''; 1127 inherit (defs'') highestPrio; 1128 }; 1129 defsFinal = defsFinal'.values; 1130 1131 # Type-check the remaining definitions, and merge them. Or throw if no definitions. 1132 mergedValue = 1133 if isDefined then 1134 if all (def: type.check def.value) defsFinal then 1135 type.merge loc defsFinal 1136 else 1137 let 1138 allInvalid = filter (def: !type.check def.value) defsFinal; 1139 in 1140 throw "A definition for option `${showOption loc}' is not of type `${type.description}'. Definition values:${showDefs allInvalid}" 1141 else 1142 # (nixos-option detects this specific error message and gives it special 1143 # handling. If changed here, please change it there too.) 1144 throw 1145 "The option `${showOption loc}' was accessed but has no value defined. Try setting the option."; 1146 1147 isDefined = defsFinal != [ ]; 1148 1149 optionalValue = if isDefined then { value = mergedValue; } else { }; 1150 }; 1151 1152 /** 1153 Given a config set, expand mkMerge properties, and push down the 1154 other properties into the children. The result is a list of 1155 config sets that do not have properties at top-level. For 1156 example, 1157 1158 mkMerge [ { boot = set1; } (mkIf cond { boot = set2; services = set3; }) ] 1159 1160 is transformed into 1161 1162 [ { boot = set1; } { boot = mkIf cond set2; services = mkIf cond set3; } ]. 1163 1164 This transform is the critical step that allows mkIf conditions 1165 to refer to the full configuration without creating an infinite 1166 recursion. 1167 1168 # Inputs 1169 1170 `cfg` 1171 1172 : 1\. Function argument 1173 */ 1174 pushDownProperties = 1175 cfg: 1176 if cfg._type or "" == "merge" then 1177 concatMap pushDownProperties cfg.contents 1178 else if cfg._type or "" == "if" then 1179 map (mapAttrs (n: v: mkIf cfg.condition v)) (pushDownProperties cfg.content) 1180 else if cfg._type or "" == "override" then 1181 map (mapAttrs (n: v: mkOverride cfg.priority v)) (pushDownProperties cfg.content) 1182 # FIXME: handle mkOrder? 1183 else 1184 [ cfg ]; 1185 1186 /** 1187 Given a config value, expand mkMerge properties, and discharge 1188 any mkIf conditions. That is, this is the place where mkIf 1189 conditions are actually evaluated. The result is a list of 1190 config values. For example, mkIf false x yields [], 1191 mkIf true x yields [x], and 1192 1193 mkMerge [ 1 (mkIf true 2) (mkIf true (mkIf false 3)) ] 1194 1195 yields [ 1 2 ]. 1196 1197 # Inputs 1198 1199 `def` 1200 1201 : 1\. Function argument 1202 */ 1203 dischargeProperties = 1204 def: 1205 if def._type or "" == "merge" then 1206 concatMap dischargeProperties def.contents 1207 else if def._type or "" == "if" then 1208 if isBool def.condition then 1209 if def.condition then dischargeProperties def.content else [ ] 1210 else 1211 throw "mkIf called with a non-Boolean condition" 1212 else 1213 [ def ]; 1214 1215 /** 1216 Given a list of config values, process the mkOverride properties, 1217 that is, return the values that have the highest (that is, 1218 numerically lowest) priority, and strip the mkOverride 1219 properties. For example, 1220 1221 [ { file = "/1"; value = mkOverride 10 "a"; } 1222 { file = "/2"; value = mkOverride 20 "b"; } 1223 { file = "/3"; value = "z"; } 1224 { file = "/4"; value = mkOverride 10 "d"; } 1225 ] 1226 1227 yields 1228 1229 [ { file = "/1"; value = "a"; } 1230 { file = "/4"; value = "d"; } 1231 ] 1232 1233 Note that "z" has the default priority 100. 1234 1235 # Inputs 1236 1237 `defs` 1238 1239 : 1\. Function argument 1240 */ 1241 filterOverrides = defs: (filterOverrides' defs).values; 1242 1243 filterOverrides' = 1244 defs: 1245 let 1246 getPrio = 1247 def: if def.value._type or "" == "override" then def.value.priority else defaultOverridePriority; 1248 highestPrio = foldl' (prio: def: min (getPrio def) prio) 9999 defs; 1249 strip = 1250 def: if def.value._type or "" == "override" then def // { value = def.value.content; } else def; 1251 in 1252 { 1253 values = concatMap (def: if getPrio def == highestPrio then [ (strip def) ] else [ ]) defs; 1254 inherit highestPrio; 1255 }; 1256 1257 /** 1258 Sort a list of properties. The sort priority of a property is 1259 defaultOrderPriority by default, but can be overridden by wrapping the property 1260 using mkOrder. 1261 1262 # Inputs 1263 1264 `defs` 1265 1266 : 1\. Function argument 1267 */ 1268 sortProperties = 1269 defs: 1270 let 1271 strip = 1272 def: 1273 if def.value._type or "" == "order" then 1274 def 1275 // { 1276 value = def.value.content; 1277 inherit (def.value) priority; 1278 } 1279 else 1280 def; 1281 defs' = map strip defs; 1282 compare = a: b: (a.priority or defaultOrderPriority) < (b.priority or defaultOrderPriority); 1283 in 1284 sort compare defs'; 1285 1286 # This calls substSubModules, whose entire purpose is only to ensure that 1287 # option declarations in submodules have accurate position information. 1288 # TODO: Merge this into mergeOptionDecls 1289 fixupOptionType = 1290 loc: opt: 1291 if opt.type.getSubModules or null == null then 1292 opt // { type = opt.type or types.unspecified; } 1293 else 1294 opt 1295 // { 1296 type = opt.type.substSubModules opt.options; 1297 options = [ ]; 1298 }; 1299 1300 /** 1301 Merge an option's definitions in a way that preserves the priority of the 1302 individual attributes in the option value. 1303 1304 This does not account for all option semantics, such as readOnly. 1305 1306 # Inputs 1307 1308 `opt` 1309 1310 : 1\. Function argument 1311 1312 # Type 1313 1314 ``` 1315 option -> attrsOf { highestPrio, value } 1316 ``` 1317 */ 1318 mergeAttrDefinitionsWithPrio = 1319 opt: 1320 let 1321 defsByAttr = zipAttrs ( 1322 concatLists ( 1323 concatMap ( 1324 { value, ... }@def: 1325 map (mapAttrsToList ( 1326 k: value: { 1327 ${k} = def // { 1328 inherit value; 1329 }; 1330 } 1331 )) (pushDownProperties value) 1332 ) opt.definitionsWithLocations 1333 ) 1334 ); 1335 in 1336 assert opt.type.name == "attrsOf" || opt.type.name == "lazyAttrsOf"; 1337 mapAttrs ( 1338 k: v: 1339 let 1340 merging = mergeDefinitions (opt.loc ++ [ k ]) opt.type.nestedTypes.elemType v; 1341 in 1342 { 1343 value = merging.mergedValue; 1344 inherit (merging.defsFinal') highestPrio; 1345 } 1346 ) defsByAttr; 1347 1348 /** 1349 Properties. 1350 1351 # Inputs 1352 1353 `condition` 1354 1355 : 1\. Function argument 1356 1357 `content` 1358 1359 : 2\. Function argument 1360 */ 1361 1362 mkIf = condition: content: { 1363 _type = "if"; 1364 inherit condition content; 1365 }; 1366 1367 mkAssert = 1368 assertion: message: content: 1369 mkIf (if assertion then true else throw "\nFailed assertion: ${message}") content; 1370 1371 mkMerge = contents: { 1372 _type = "merge"; 1373 inherit contents; 1374 }; 1375 1376 /** 1377 Return a definition with file location information. 1378 */ 1379 mkDefinition = args@{ file, value, ... }: args // { _type = "definition"; }; 1380 1381 mkOverride = priority: content: { 1382 _type = "override"; 1383 inherit priority content; 1384 }; 1385 1386 mkOptionDefault = mkOverride 1500; # priority of option defaults 1387 mkDefault = mkOverride 1000; # used in config sections of non-user modules to set a default 1388 defaultOverridePriority = 100; 1389 mkImageMediaOverride = mkOverride 60; # image media profiles can be derived by inclusion into host config, hence needing to override host config, but do allow user to mkForce 1390 mkForce = mkOverride 50; 1391 mkVMOverride = mkOverride 10; # used by ‘nixos-rebuild build-vm’ 1392 1393 defaultPriority = 1394 warnIf (oldestSupportedReleaseIsAtLeast 2305) 1395 "lib.modules.defaultPriority is deprecated, please use lib.modules.defaultOverridePriority instead." 1396 defaultOverridePriority; 1397 1398 mkFixStrictness = warn "lib.mkFixStrictness has no effect and will be removed. It returns its argument unmodified, so you can just remove any calls." id; 1399 1400 mkOrder = priority: content: { 1401 _type = "order"; 1402 inherit priority content; 1403 }; 1404 1405 mkBefore = mkOrder 500; 1406 defaultOrderPriority = 1000; 1407 mkAfter = mkOrder 1500; 1408 1409 # Convenient property used to transfer all definitions and their 1410 # properties from one option to another. This property is useful for 1411 # renaming options, and also for including properties from another module 1412 # system, including sub-modules. 1413 # 1414 # { config, options, ... }: 1415 # 1416 # { 1417 # # 'bar' might not always be defined in the current module-set. 1418 # config.foo.enable = mkAliasDefinitions (options.bar.enable or {}); 1419 # 1420 # # 'barbaz' has to be defined in the current module-set. 1421 # config.foobar.paths = mkAliasDefinitions options.barbaz.paths; 1422 # } 1423 # 1424 # Note, this is different than taking the value of the option and using it 1425 # as a definition, as the new definition will not keep the mkOverride / 1426 # mkDefault properties of the previous option. 1427 # 1428 mkAliasDefinitions = mkAliasAndWrapDefinitions id; 1429 mkAliasAndWrapDefinitions = wrap: option: mkAliasIfDef option (wrap (mkMerge option.definitions)); 1430 1431 # Similar to mkAliasAndWrapDefinitions but copies over the priority from the 1432 # option as well. 1433 # 1434 # If a priority is not set, it assumes a priority of defaultOverridePriority. 1435 mkAliasAndWrapDefsWithPriority = 1436 wrap: option: 1437 let 1438 prio = option.highestPrio or defaultOverridePriority; 1439 defsWithPrio = map (mkOverride prio) option.definitions; 1440 in 1441 mkAliasIfDef option (wrap (mkMerge defsWithPrio)); 1442 1443 mkAliasIfDef = option: mkIf (isOption option && option.isDefined); 1444 1445 /** 1446 Compatibility. 1447 1448 # Inputs 1449 1450 `modules` 1451 1452 : 1\. Function argument 1453 1454 `args` 1455 1456 : 2\. Function argument 1457 */ 1458 fixMergeModules = 1459 modules: args: 1460 evalModules { 1461 inherit modules args; 1462 check = false; 1463 }; 1464 1465 /** 1466 Return a module that causes a warning to be shown if the 1467 specified option is defined. For example, 1468 1469 mkRemovedOptionModule [ "boot" "loader" "grub" "bootDevice" ] "<replacement instructions>" 1470 1471 causes a assertion if the user defines boot.loader.grub.bootDevice. 1472 1473 replacementInstructions is a string that provides instructions on 1474 how to achieve the same functionality without the removed option, 1475 or alternatively a reasoning why the functionality is not needed. 1476 replacementInstructions SHOULD be provided! 1477 1478 # Inputs 1479 1480 `optionName` 1481 1482 : 1\. Function argument 1483 1484 `replacementInstructions` 1485 1486 : 2\. Function argument 1487 */ 1488 mkRemovedOptionModule = 1489 optionName: replacementInstructions: 1490 { options, ... }: 1491 { 1492 options = setAttrByPath optionName (mkOption { 1493 visible = false; 1494 apply = 1495 x: 1496 throw "The option `${showOption optionName}' can no longer be used since it's been removed. ${replacementInstructions}"; 1497 }); 1498 config.assertions = 1499 let 1500 opt = getAttrFromPath optionName options; 1501 in 1502 [ 1503 { 1504 assertion = !opt.isDefined; 1505 message = '' 1506 The option definition `${showOption optionName}' in ${showFiles opt.files} no longer has any effect; please remove it. 1507 ${replacementInstructions} 1508 ''; 1509 } 1510 ]; 1511 }; 1512 1513 /** 1514 Return a module that causes a warning to be shown if the 1515 specified "from" option is defined; the defined value is however 1516 forwarded to the "to" option. This can be used to rename options 1517 while providing backward compatibility. For example, 1518 1519 mkRenamedOptionModule [ "boot" "copyKernels" ] [ "boot" "loader" "grub" "copyKernels" ] 1520 1521 forwards any definitions of boot.copyKernels to 1522 boot.loader.grub.copyKernels while printing a warning. 1523 1524 This also copies over the priority from the aliased option to the 1525 non-aliased option. 1526 1527 # Inputs 1528 1529 `from` 1530 1531 : 1\. Function argument 1532 1533 `to` 1534 1535 : 2\. Function argument 1536 */ 1537 mkRenamedOptionModule = 1538 from: to: 1539 doRename { 1540 inherit from to; 1541 visible = false; 1542 warn = true; 1543 use = trace "Obsolete option `${showOption from}' is used. It was renamed to `${showOption to}'."; 1544 }; 1545 1546 mkRenamedOptionModuleWith = 1547 { 1548 /** 1549 Old option path as list of strings. 1550 */ 1551 from, 1552 /** 1553 New option path as list of strings. 1554 */ 1555 to, 1556 1557 /** 1558 Release number of the first release that contains the rename, ignoring backports. 1559 Set it to the upcoming release, matching the nixpkgs/.version file. 1560 */ 1561 sinceRelease, 1562 1563 }: 1564 doRename { 1565 inherit from to; 1566 visible = false; 1567 warn = oldestSupportedReleaseIsAtLeast sinceRelease; 1568 use = warnIf (oldestSupportedReleaseIsAtLeast sinceRelease) "Obsolete option `${showOption from}' is used. It was renamed to `${showOption to}'."; 1569 }; 1570 1571 /** 1572 Return a module that causes a warning to be shown if any of the "from" 1573 option is defined; the defined values can be used in the "mergeFn" to set 1574 the "to" value. 1575 This function can be used to merge multiple options into one that has a 1576 different type. 1577 1578 "mergeFn" takes the module "config" as a parameter and must return a value 1579 of "to" option type. 1580 1581 mkMergedOptionModule 1582 [ [ "a" "b" "c" ] 1583 [ "d" "e" "f" ] ] 1584 [ "x" "y" "z" ] 1585 (config: 1586 let value = p: getAttrFromPath p config; 1587 in 1588 if (value [ "a" "b" "c" ]) == true then "foo" 1589 else if (value [ "d" "e" "f" ]) == true then "bar" 1590 else "baz") 1591 1592 - options.a.b.c is a removed boolean option 1593 - options.d.e.f is a removed boolean option 1594 - options.x.y.z is a new str option that combines a.b.c and d.e.f 1595 functionality 1596 1597 This show a warning if any a.b.c or d.e.f is set, and set the value of 1598 x.y.z to the result of the merge function 1599 1600 # Inputs 1601 1602 `from` 1603 1604 : 1\. Function argument 1605 1606 `to` 1607 1608 : 2\. Function argument 1609 1610 `mergeFn` 1611 1612 : 3\. Function argument 1613 */ 1614 mkMergedOptionModule = 1615 from: to: mergeFn: 1616 { config, options, ... }: 1617 { 1618 options = foldl' recursiveUpdate { } ( 1619 map ( 1620 path: 1621 setAttrByPath path (mkOption { 1622 visible = false; 1623 # To use the value in mergeFn without triggering errors 1624 default = "_mkMergedOptionModule"; 1625 }) 1626 ) from 1627 ); 1628 1629 config = { 1630 warnings = filter (x: x != "") ( 1631 map ( 1632 f: 1633 let 1634 val = getAttrFromPath f config; 1635 opt = getAttrFromPath f options; 1636 in 1637 optionalString (val != "_mkMergedOptionModule") 1638 "The option `${showOption f}' defined in ${showFiles opt.files} has been changed to `${showOption to}' that has a different type. Please read `${showOption to}' documentation and update your configuration accordingly." 1639 ) from 1640 ); 1641 } 1642 // setAttrByPath to ( 1643 mkMerge ( 1644 optional (any (f: (getAttrFromPath f config) != "_mkMergedOptionModule") from) (mergeFn config) 1645 ) 1646 ); 1647 }; 1648 1649 /** 1650 Single "from" version of mkMergedOptionModule. 1651 Return a module that causes a warning to be shown if the "from" option is 1652 defined; the defined value can be used in the "mergeFn" to set the "to" 1653 value. 1654 This function can be used to change an option into another that has a 1655 different type. 1656 1657 "mergeFn" takes the module "config" as a parameter and must return a value of 1658 "to" option type. 1659 1660 mkChangedOptionModule [ "a" "b" "c" ] [ "x" "y" "z" ] 1661 (config: 1662 let value = getAttrFromPath [ "a" "b" "c" ] config; 1663 in 1664 if value > 100 then "high" 1665 else "normal") 1666 1667 - options.a.b.c is a removed int option 1668 - options.x.y.z is a new str option that supersedes a.b.c 1669 1670 This show a warning if a.b.c is set, and set the value of x.y.z to the 1671 result of the change function 1672 1673 # Inputs 1674 1675 `from` 1676 1677 : 1\. Function argument 1678 1679 `to` 1680 1681 : 2\. Function argument 1682 1683 `changeFn` 1684 1685 : 3\. Function argument 1686 */ 1687 mkChangedOptionModule = 1688 from: to: changeFn: 1689 mkMergedOptionModule [ from ] to changeFn; 1690 1691 /** 1692 Like mkRenamedOptionModule, but doesn't show a warning. 1693 1694 # Inputs 1695 1696 `from` 1697 1698 : 1\. Function argument 1699 1700 `to` 1701 1702 : 2\. Function argument 1703 */ 1704 mkAliasOptionModule = 1705 from: to: 1706 doRename { 1707 inherit from to; 1708 visible = true; 1709 warn = false; 1710 use = id; 1711 }; 1712 1713 /** 1714 Transitional version of mkAliasOptionModule that uses MD docs. 1715 1716 This function is no longer necessary and merely an alias of `mkAliasOptionModule`. 1717 */ 1718 mkAliasOptionModuleMD = mkAliasOptionModule; 1719 1720 /** 1721 mkDerivedConfig : Option a -> (a -> Definition b) -> Definition b 1722 1723 Create config definitions with the same priority as the definition of another option. 1724 This should be used for option definitions where one option sets the value of another as a convenience. 1725 For instance a config file could be set with a `text` or `source` option, where text translates to a `source` 1726 value using `mkDerivedConfig options.text (pkgs.writeText "filename.conf")`. 1727 1728 It takes care of setting the right priority using `mkOverride`. 1729 1730 # Inputs 1731 1732 `opt` 1733 1734 : 1\. Function argument 1735 1736 `f` 1737 1738 : 2\. Function argument 1739 */ 1740 # TODO: make the module system error message include information about `opt` in 1741 # error messages about conflicts. E.g. introduce a variation of `mkOverride` which 1742 # adds extra location context to the definition object. This will allow context to be added 1743 # to all messages that report option locations "this value was derived from <full option name> 1744 # which was defined in <locations>". It can provide a trace of options that contributed 1745 # to definitions. 1746 mkDerivedConfig = opt: f: mkOverride (opt.highestPrio or defaultOverridePriority) (f opt.value); 1747 1748 /** 1749 Return a module that help declares an option that has been renamed. 1750 When a value is defined for the old option, it is forwarded to the `to` option. 1751 */ 1752 doRename = 1753 { 1754 # List of strings representing the attribute path of the old option. 1755 from, 1756 # List of strings representing the attribute path of the new option. 1757 to, 1758 # Boolean, whether the old option is to be included in documentation. 1759 visible, 1760 # Whether to warn when a value is defined for the old option. 1761 # NOTE: This requires the NixOS assertions module to be imported, so 1762 # - this generally does not work in submodules 1763 # - this may or may not work outside NixOS 1764 warn, 1765 # A function that is applied to the option value, to form the value 1766 # of the old `from` option. 1767 # 1768 # For example, the identity function can be passed, to return the option value unchanged. 1769 # ```nix 1770 # use = x: x; 1771 # ``` 1772 # 1773 # To add a warning, you can pass the partially applied `warn` function. 1774 # ```nix 1775 # use = lib.warn "Obsolete option `${opt.old}' is used. Use `${opt.to}' instead."; 1776 # ``` 1777 use, 1778 # Legacy option, enabled by default: whether to preserve the priority of definitions in `old`. 1779 withPriority ? true, 1780 # A boolean that defines the `mkIf` condition for `to`. 1781 # If the condition evaluates to `true`, and the `to` path points into an 1782 # `attrsOf (submodule ...)`, then `doRename` would cause an empty module to 1783 # be created, even if the `from` option is undefined. 1784 # By setting this to an expression that may return `false`, you can inhibit 1785 # this undesired behavior. 1786 # 1787 # Example: 1788 # 1789 # ```nix 1790 # { config, lib, ... }: 1791 # let 1792 # inherit (lib) mkOption mkEnableOption types doRename; 1793 # in 1794 # { 1795 # options = { 1796 # 1797 # # Old service 1798 # services.foo.enable = mkEnableOption "foo"; 1799 # 1800 # # New multi-instance service 1801 # services.foos = mkOption { 1802 # type = types.attrsOf (types.submodule …); 1803 # }; 1804 # }; 1805 # imports = [ 1806 # (doRename { 1807 # from = [ "services" "foo" "bar" ]; 1808 # to = [ "services" "foos" "" "bar" ]; 1809 # visible = true; 1810 # warn = false; 1811 # use = x: x; 1812 # withPriority = true; 1813 # # Only define services.foos."" if needed. (It's not just about `bar`) 1814 # condition = config.services.foo.enable; 1815 # }) 1816 # ]; 1817 # } 1818 # ``` 1819 condition ? true, 1820 }: 1821 { config, options, ... }: 1822 let 1823 fromOpt = getAttrFromPath from options; 1824 toOf = attrByPath to (abort "Renaming error: option `${showOption to}' does not exist."); 1825 toType = 1826 let 1827 opt = attrByPath to { } options; 1828 in 1829 opt.type or (types.submodule { }); 1830 in 1831 { 1832 options = setAttrByPath from ( 1833 mkOption { 1834 inherit visible; 1835 description = "Alias of {option}`${showOption to}`."; 1836 apply = x: use (toOf config); 1837 } 1838 // optionalAttrs (toType != null) { 1839 type = toType; 1840 } 1841 ); 1842 config = mkIf condition (mkMerge [ 1843 (optionalAttrs (options ? warnings) { 1844 warnings = 1845 optional (warn && fromOpt.isDefined) 1846 "The option `${showOption from}' defined in ${showFiles fromOpt.files} has been renamed to `${showOption to}'."; 1847 }) 1848 ( 1849 if withPriority then 1850 mkAliasAndWrapDefsWithPriority (setAttrByPath to) fromOpt 1851 else 1852 mkAliasAndWrapDefinitions (setAttrByPath to) fromOpt 1853 ) 1854 ]); 1855 }; 1856 1857 /** 1858 `importApply file arg :: Path -> a -> Module`, where `import file :: a -> Module` 1859 1860 `importApply` imports a Nix expression file much like the module system would, 1861 after passing an extra positional argument to the function in the file. 1862 1863 This function should be used when declaring a module in a file that refers to 1864 values from a different scope, such as that in a flake. 1865 1866 It solves the problems of alternative solutions: 1867 1868 - While `importApply file arg` is _mostly_ equivalent to 1869 `import file arg`, the latter returns a module without a location, 1870 as `import` only returns the contained expression. This leads to worse 1871 error messages. 1872 1873 - Using `specialArgs` to provide arguments to all modules. This effectively 1874 creates an incomplete module, and requires the user of the module to 1875 manually pass the `specialArgs` to the configuration, which is error-prone, 1876 verbose, and unnecessary. 1877 1878 The nix file must contain a function that returns a module. 1879 A module may itself be a function, so the file is often a function with two 1880 positional arguments instead of one. See the example below. 1881 1882 This function does not add support for deduplication and `disabledModules`, 1883 although that could be achieved by wrapping the returned module and setting 1884 the `key` module attribute. 1885 The reason for this omission is that the file path is not guaranteed to be 1886 a unique identifier for the module, as two instances of the module may 1887 reference different `arg`s in their closures. 1888 1889 Example 1890 1891 # lib.nix 1892 imports = [ 1893 (lib.modules.importApply ./module.nix { bar = bar; }) 1894 ]; 1895 1896 # module.nix 1897 { bar }: 1898 { lib, config, ... }: 1899 { 1900 options = ...; 1901 config = ... bar ...; 1902 } 1903 */ 1904 importApply = 1905 modulePath: staticArg: lib.setDefaultModuleLocation modulePath (import modulePath staticArg); 1906 1907 /** 1908 Use this function to import a JSON file as NixOS configuration. 1909 1910 modules.importJSON :: path -> attrs 1911 1912 # Inputs 1913 1914 `file` 1915 1916 : 1\. Function argument 1917 */ 1918 importJSON = file: { 1919 _file = file; 1920 config = lib.importJSON file; 1921 }; 1922 1923 /** 1924 Use this function to import a TOML file as NixOS configuration. 1925 1926 modules.importTOML :: path -> attrs 1927 1928 # Inputs 1929 1930 `file` 1931 1932 : 1\. Function argument 1933 */ 1934 importTOML = file: { 1935 _file = file; 1936 config = lib.importTOML file; 1937 }; 1938 1939 private = 1940 mapAttrs 1941 ( 1942 k: 1943 warn "External use of `lib.modules.${k}` is deprecated. If your use case isn't covered by non-deprecated functions, we'd like to know more and perhaps support your use case well, instead of providing access to these low level functions. In this case please open an issue in https://github.com/nixos/nixpkgs/issues/." 1944 ) 1945 { 1946 inherit 1947 applyModuleArgsIfFunction 1948 dischargeProperties 1949 mergeModules 1950 mergeModules' 1951 pushDownProperties 1952 unifyModuleSyntax 1953 ; 1954 collectModules = collectModules null; 1955 }; 1956 1957 /** 1958 Error messages produced by the module system. 1959 1960 We factor these out to improve the flow when reading the code. 1961 1962 Functions in `messages` that produce error messages are spelled in 1963 lower_snake_case. This goes against the convention in order to make the 1964 error message implementation more readable, and to visually distinguish 1965 them from other functions in the module system. 1966 */ 1967 messages = 1968 let 1969 inherit (lib.strings) concatMapStringsSep escapeNixString trim; 1970 /** 1971 "" or ", in file FOO" 1972 */ 1973 into_fallback_file_maybe = 1974 file: 1975 optionalString ( 1976 file != null && file != unknownModule 1977 ) ", while trying to load a module into ${toString file}"; 1978 1979 into_prefix_maybe = 1980 prefix: 1981 optionalString (prefix != [ ]) ", while trying to load a module into ${code (showOption prefix)}"; 1982 1983 /** 1984 Format text with one line break between each list item. 1985 */ 1986 lines = concatMapStringsSep "\n" trim; 1987 1988 /** 1989 Format text with two line break between each list item. 1990 */ 1991 paragraphs = concatMapStringsSep "\n\n" trim; 1992 1993 /** 1994 ``` 1995 optionalMatch 1996 { foo = "Foo result"; 1997 bar = "Bar result"; 1998 } "foo" 1999 == [ "Foo result" ] 2000 2001 optionalMatch { foo = "Foo"; } "baz" == [ ] 2002 2003 optionalMatch { foo = "Foo"; } true == [ ] 2004 ``` 2005 */ 2006 optionalMatch = 2007 cases: value: if isString value && cases ? ${value} then [ cases.${value} ] else [ ]; 2008 2009 # esc = builtins.fromJSON "\"\\u001b\""; 2010 esc = builtins.fromJSON "\"\\u001b\""; 2011 # Bold purple for warnings 2012 warn = s: "${esc}[1;35m${s}${esc}[0m"; 2013 # Bold green for suggestions 2014 good = s: "${esc}[1;32m${s}${esc}[0m"; 2015 # Bold, default color for code 2016 code = s: "${esc}[1m${s}${esc}[0m"; 2017 2018 in 2019 { 2020 2021 /** 2022 When load a value with a (wrong) _type as a module 2023 */ 2024 not_a_module = 2025 { 2026 fallbackFile, 2027 value, 2028 _type, 2029 expectedClass ? null, 2030 prefix, 2031 }: 2032 paragraphs ( 2033 [ 2034 '' 2035 Expected a module, but found a value of type ${warn (escapeNixString _type)}${into_fallback_file_maybe fallbackFile}${into_prefix_maybe prefix}. 2036 A module is typically loaded by adding it to the ${code "imports = [ ... ];"} attribute of an existing module, or in the ${code "modules = [ ... ];"} argument of various functions. 2037 Please make sure that each of the list items is a module, and not a different kind of value. 2038 '' 2039 ] 2040 ++ (optionalMatch { 2041 "configuration" = trim '' 2042 If you really mean to import this configuration, instead please only import the modules that make up the configuration. 2043 You may have to create a `let` binding, file or attribute to give yourself access to the relevant modules. 2044 While loading a configuration into the module system is a very sensible idea, it can not be done cleanly in practice. 2045 ''; 2046 # ^^ Extended explanation: That's because a finalized configuration is more than just a set of modules. For instance, it has its own `specialArgs` that, by the nature of `specialArgs` can't be loaded through `imports` or the the `modules` argument. So instead, we have to ask you to extract the relevant modules and use those instead. This way, we keep the module system comparatively simple, and hopefully avoid a bad surprise down the line. 2047 2048 "flake" = lines ( 2049 [ 2050 (trim '' 2051 Perhaps you forgot to select an attribute name? 2052 Instead of, for example, 2053 ${warn "inputs.someflake"} 2054 you need to write something like 2055 ${warn "inputs.someflake"}${ 2056 if expectedClass == null then 2057 good ".modules.someApp.default" 2058 else 2059 good ".modules.${expectedClass}.default" 2060 2061 } 2062 '') 2063 ] 2064 ++ optionalMatch { 2065 # We'll add no more than 5 custom suggestions here. 2066 # Please switch to `.modules.${class}` in your Module System application. 2067 "nixos" = trim '' 2068 or 2069 ${warn "inputs.someflake"}${good ".nixosModules.default"} 2070 ''; 2071 "darwin" = trim '' 2072 or 2073 ${warn "inputs.someflake"}${good ".darwinModules.default"} 2074 ''; 2075 } expectedClass 2076 ); 2077 } _type) 2078 ); 2079 }; 2080 2081in 2082private 2083// { 2084 # NOTE: not all of these functions are necessarily public interfaces; some 2085 # are just needed by types.nix, but are not meant to be consumed 2086 # externally. 2087 inherit 2088 defaultOrderPriority 2089 defaultOverridePriority 2090 defaultPriority 2091 doRename 2092 evalModules 2093 evalOptionValue # for use by lib.types 2094 filterOverrides 2095 filterOverrides' 2096 fixMergeModules 2097 fixupOptionType # should be private? 2098 importApply 2099 importJSON 2100 importTOML 2101 mergeDefinitions 2102 mergeAttrDefinitionsWithPrio 2103 mergeOptionDecls # should be private? 2104 mkAfter 2105 mkAliasAndWrapDefinitions 2106 mkAliasAndWrapDefsWithPriority 2107 mkAliasDefinitions 2108 mkAliasIfDef 2109 mkAliasOptionModule 2110 mkAliasOptionModuleMD 2111 mkAssert 2112 mkBefore 2113 mkChangedOptionModule 2114 mkDefault 2115 mkDefinition 2116 mkDerivedConfig 2117 mkFixStrictness 2118 mkForce 2119 mkIf 2120 mkImageMediaOverride 2121 mkMerge 2122 mkMergedOptionModule 2123 mkOptionDefault 2124 mkOrder 2125 mkOverride 2126 mkRemovedOptionModule 2127 mkRenamedOptionModule 2128 mkRenamedOptionModuleWith 2129 mkVMOverride 2130 setDefaultModuleLocation 2131 sortProperties 2132 ; 2133}