at 25.11-pre 69 kB view raw
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 } 262 // specialArgs 263 ); 264 in 265 mergeModules prefix (reverseList collected); 266 267 options = merged.matchedOptions; 268 269 config = 270 let 271 272 # For definitions that have an associated option 273 declaredConfig = mapAttrsRecursiveCond (v: !isOption v) (_: v: v.value) options; 274 275 # If freeformType is set, this is for definitions that don't have an associated option 276 freeformConfig = 277 let 278 defs = map (def: { 279 file = def.file; 280 value = setAttrByPath def.prefix def.value; 281 }) merged.unmatchedDefns; 282 in 283 if defs == [ ] then { } else declaredConfig._module.freeformType.merge prefix defs; 284 285 in 286 if declaredConfig._module.freeformType == null then 287 declaredConfig 288 # Because all definitions that had an associated option ended in 289 # declaredConfig, freeformConfig can only contain the non-option 290 # paths, meaning recursiveUpdate will never override any value 291 else 292 recursiveUpdate freeformConfig declaredConfig; 293 294 checkUnmatched = 295 if config._module.check && config._module.freeformType == null && merged.unmatchedDefns != [ ] then 296 let 297 firstDef = head merged.unmatchedDefns; 298 baseMsg = 299 let 300 optText = showOption (prefix ++ firstDef.prefix); 301 defText = 302 addErrorContext 303 "while evaluating the error message for definitions for `${optText}', which is an option that does not exist" 304 (addErrorContext "while evaluating a definition from `${firstDef.file}'" (showDefs [ firstDef ])); 305 in 306 "The option `${optText}' does not exist. Definition values:${defText}"; 307 in 308 if 309 attrNames options == [ "_module" ] 310 # No options were declared at all (`_module` is built in) 311 # but we do have unmatched definitions, and no freeformType (earlier conditions) 312 then 313 let 314 optionName = showOption prefix; 315 in 316 if optionName == "" then 317 throw '' 318 ${baseMsg} 319 320 It seems as if you're trying to declare an option by placing it into `config' rather than `options'! 321 '' 322 else 323 throw '' 324 ${baseMsg} 325 326 However there are no options defined in `${showOption prefix}'. Are you sure you've 327 declared your options properly? This can happen if you e.g. declared your options in `types.submodule' 328 under `config' rather than `options'. 329 '' 330 else 331 throw baseMsg 332 else 333 null; 334 335 checked = seq checkUnmatched; 336 337 extendModules = 338 extendArgs@{ 339 modules ? [ ], 340 specialArgs ? { }, 341 prefix ? [ ], 342 }: 343 evalModules ( 344 evalModulesArgs 345 // { 346 modules = regularModules ++ modules; 347 specialArgs = evalModulesArgs.specialArgs or { } // specialArgs; 348 prefix = extendArgs.prefix or evalModulesArgs.prefix or [ ]; 349 } 350 ); 351 352 type = types.submoduleWith { 353 inherit modules specialArgs class; 354 }; 355 356 result = withWarnings { 357 _type = "configuration"; 358 options = checked options; 359 config = checked (removeAttrs config [ "_module" ]); 360 _module = checked (config._module); 361 inherit extendModules type class; 362 }; 363 in 364 result; 365 366 # collectModules :: (class: String) -> (modulesPath: String) -> (modules: [ Module ]) -> (args: Attrs) -> [ Module ] 367 # 368 # Collects all modules recursively through `import` statements, filtering out 369 # all modules in disabledModules. 370 collectModules = 371 class: 372 let 373 374 # Like unifyModuleSyntax, but also imports paths and calls functions if necessary 375 loadModule = 376 args: fallbackFile: fallbackKey: m: 377 if isFunction m then 378 unifyModuleSyntax fallbackFile fallbackKey (applyModuleArgs fallbackKey m args) 379 else if isAttrs m then 380 if m._type or "module" == "module" then 381 unifyModuleSyntax fallbackFile fallbackKey m 382 else if m._type == "if" || m._type == "override" then 383 loadModule args fallbackFile fallbackKey { config = m; } 384 else 385 throw ( 386 messages.not_a_module { 387 inherit fallbackFile; 388 value = m; 389 _type = m._type; 390 expectedClass = class; 391 } 392 ) 393 else if isList m then 394 let 395 defs = [ 396 { 397 file = fallbackFile; 398 value = m; 399 } 400 ]; 401 in 402 throw "Module imports can't be nested lists. Perhaps you meant to remove one level of lists? Definitions: ${showDefs defs}" 403 else 404 unifyModuleSyntax (toString m) (toString m) ( 405 applyModuleArgsIfFunction (toString m) (import m) args 406 ); 407 408 checkModule = 409 if class != null then 410 m: 411 if m._class == null || m._class == class then 412 m 413 else 414 throw '' 415 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}. 416 417 Help: 418 - Ensure that you are importing the correct module. 419 - Verify that the module's `_class`, ${lib.strings.escapeNixString m._class} matches the expected `class` ${lib.strings.escapeNixString class}. 420 - If you are using a custom class, make sure it is correctly defined and used consistently across your modules. 421 '' 422 else 423 m: m; 424 425 /** 426 Collects all modules recursively into the form 427 428 { 429 disabled = [ <list of disabled modules> ]; 430 # All modules of the main module list 431 modules = [ 432 { 433 key = <key1>; 434 module = <module for key1>; 435 # All modules imported by the module for key1 436 modules = [ 437 { 438 key = <key1-1>; 439 module = <module for key1-1>; 440 # All modules imported by the module for key1-1 441 modules = [ ... ]; 442 } 443 ... 444 ]; 445 } 446 ... 447 ]; 448 } 449 */ 450 collectStructuredModules = 451 let 452 collectResults = modules: { 453 disabled = concatLists (catAttrs "disabled" modules); 454 inherit modules; 455 }; 456 in 457 parentFile: parentKey: initialModules: args: 458 collectResults ( 459 imap1 ( 460 n: x: 461 let 462 module = checkModule (loadModule args parentFile "${parentKey}:anon-${toString n}" x); 463 collectedImports = collectStructuredModules module._file module.key module.imports args; 464 in 465 { 466 key = module.key; 467 module = module; 468 modules = collectedImports.modules; 469 disabled = 470 ( 471 if module.disabledModules != [ ] then 472 [ 473 { 474 file = module._file; 475 disabled = module.disabledModules; 476 } 477 ] 478 else 479 [ ] 480 ) 481 ++ collectedImports.disabled; 482 } 483 ) initialModules 484 ); 485 486 # filterModules :: String -> { disabled, modules } -> [ Module ] 487 # 488 # Filters a structure as emitted by collectStructuredModules by removing all disabled 489 # modules recursively. It returns the final list of unique-by-key modules 490 filterModules = 491 modulesPath: 492 { disabled, modules }: 493 let 494 moduleKey = 495 file: m: 496 if isString m then 497 if substring 0 1 m == "/" then m else toString modulesPath + "/" + m 498 499 else if isConvertibleWithToString m then 500 if m ? key && m.key != toString m then 501 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." 502 else 503 toString m 504 505 else if m ? key then 506 m.key 507 508 else if isAttrs m then 509 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." 510 else 511 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}."; 512 513 disabledKeys = concatMap ({ file, disabled }: map (moduleKey file) disabled) disabled; 514 keyFilter = filter (attrs: !elem attrs.key disabledKeys); 515 in 516 map (attrs: attrs.module) (genericClosure { 517 startSet = keyFilter modules; 518 operator = attrs: keyFilter attrs.modules; 519 }); 520 521 in 522 modulesPath: initialModules: args: 523 filterModules modulesPath (collectStructuredModules unknownModule "" initialModules args); 524 525 /** 526 Wrap a module with a default location for reporting errors. 527 528 # Inputs 529 530 `file` 531 532 : 1\. Function argument 533 534 `m` 535 536 : 2\. Function argument 537 */ 538 setDefaultModuleLocation = file: m: { 539 _file = file; 540 imports = [ m ]; 541 }; 542 543 /** 544 Massage a module into canonical form, that is, a set consisting 545 of options, config and imports attributes. 546 547 # Inputs 548 549 `file` 550 551 : 1\. Function argument 552 553 `key` 554 555 : 2\. Function argument 556 557 `m` 558 559 : 3\. Function argument 560 */ 561 unifyModuleSyntax = 562 file: key: m: 563 let 564 addMeta = 565 config: 566 if m ? meta then 567 mkMerge [ 568 config 569 { meta = m.meta; } 570 ] 571 else 572 config; 573 addFreeformType = 574 config: 575 if m ? freeformType then 576 mkMerge [ 577 config 578 { _module.freeformType = m.freeformType; } 579 ] 580 else 581 config; 582 in 583 if m ? config || m ? options then 584 let 585 badAttrs = removeAttrs m [ 586 "_class" 587 "_file" 588 "key" 589 "disabledModules" 590 "imports" 591 "options" 592 "config" 593 "meta" 594 "freeformType" 595 ]; 596 in 597 if badAttrs != { } then 598 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." 599 else 600 { 601 _file = toString m._file or file; 602 _class = m._class or null; 603 key = toString m.key or key; 604 disabledModules = m.disabledModules or [ ]; 605 imports = m.imports or [ ]; 606 options = m.options or { }; 607 config = addFreeformType (addMeta (m.config or { })); 608 } 609 else 610 # shorthand syntax 611 throwIfNot (isAttrs m) "module ${file} (${key}) does not look like a module." { 612 _file = toString m._file or file; 613 _class = m._class or null; 614 key = toString m.key or key; 615 disabledModules = m.disabledModules or [ ]; 616 imports = m.require or [ ] ++ m.imports or [ ]; 617 options = { }; 618 config = addFreeformType ( 619 removeAttrs m [ 620 "_class" 621 "_file" 622 "key" 623 "disabledModules" 624 "require" 625 "imports" 626 "freeformType" 627 ] 628 ); 629 }; 630 631 applyModuleArgsIfFunction = 632 key: f: args@{ config, ... }: if isFunction f then applyModuleArgs key f args else f; 633 634 applyModuleArgs = 635 key: f: 636 args@{ config, ... }: 637 let 638 # Module arguments are resolved in a strict manner when attribute set 639 # deconstruction is used. As the arguments are now defined with the 640 # config._module.args option, the strictness used on the attribute 641 # set argument would cause an infinite loop, if the result of the 642 # option is given as argument. 643 # 644 # To work-around the strictness issue on the deconstruction of the 645 # attributes set argument, we create a new attribute set which is 646 # constructed to satisfy the expected set of attributes. Thus calling 647 # a module will resolve strictly the attributes used as argument but 648 # not their values. The values are forwarding the result of the 649 # evaluation of the option. 650 context = name: ''while evaluating the module argument `${name}' in "${key}":''; 651 extraArgs = mapAttrs ( 652 name: _: addErrorContext (context name) (args.${name} or config._module.args.${name}) 653 ) (functionArgs f); 654 655 # Note: we append in the opposite order such that we can add an error 656 # context on the explicit arguments of "args" too. This update 657 # operator is used to make the "args@{ ... }: with args.lib;" notation 658 # works. 659 in 660 f (args // extraArgs); 661 662 /** 663 Merge a list of modules. This will recurse over the option 664 declarations in all modules, combining them into a single set. 665 At the same time, for each option declaration, it will merge the 666 corresponding option definitions in all machines, returning them 667 in the value attribute of each option. 668 669 This returns a set like 670 { 671 # A recursive set of options along with their final values 672 matchedOptions = { 673 foo = { _type = "option"; value = "option value of foo"; ... }; 674 bar.baz = { _type = "option"; value = "option value of bar.baz"; ... }; 675 ... 676 }; 677 # A list of definitions that weren't matched by any option 678 unmatchedDefns = [ 679 { file = "file.nix"; prefix = [ "qux" ]; value = "qux"; } 680 ... 681 ]; 682 } 683 684 # Inputs 685 686 `prefix` 687 688 : 1\. Function argument 689 690 `modules` 691 692 : 2\. Function argument 693 */ 694 mergeModules = 695 prefix: modules: 696 mergeModules' prefix modules ( 697 concatMap ( 698 m: 699 map (config: { 700 file = m._file; 701 inherit config; 702 }) (pushDownProperties m.config) 703 ) modules 704 ); 705 706 mergeModules' = 707 prefix: modules: configs: 708 let 709 # an attrset 'name' => list of submodules that declare ‘name’. 710 declsByName = zipAttrsWith (n: v: v) ( 711 map ( 712 module: 713 let 714 subtree = module.options; 715 in 716 if !(isAttrs subtree) then 717 throw '' 718 An option declaration for `${concatStringsSep "." prefix}' has type 719 `${typeOf subtree}' rather than an attribute set. 720 Did you mean to define this outside of `options'? 721 '' 722 else 723 mapAttrs (n: option: { 724 inherit (module) _file; 725 pos = unsafeGetAttrPos n subtree; 726 options = option; 727 }) subtree 728 ) modules 729 ); 730 731 # The root of any module definition must be an attrset. 732 checkedConfigs = 733 assert all ( 734 c: 735 # TODO: I have my doubts that this error would occur when option definitions are not matched. 736 # The implementation of this check used to be tied to a superficially similar check for 737 # options, so maybe that's why this is here. 738 isAttrs c.config 739 || throw '' 740 In module `${c.file}', you're trying to define a value of type `${typeOf c.config}' 741 rather than an attribute set for the option 742 `${concatStringsSep "." prefix}'! 743 744 This usually happens if `${concatStringsSep "." prefix}' has option 745 definitions inside that are not matched. Please check how to properly define 746 this option by e.g. referring to `man 5 configuration.nix'! 747 '' 748 ) configs; 749 configs; 750 751 # an attrset 'name' => list of submodules that define ‘name’. 752 pushedDownDefinitionsByName = zipAttrsWith (n: concatLists) ( 753 map ( 754 module: 755 mapAttrs ( 756 n: value: 757 map (config: { 758 inherit (module) file; 759 inherit config; 760 }) (pushDownProperties value) 761 ) module.config 762 ) checkedConfigs 763 ); 764 # extract the definitions for each loc 765 rawDefinitionsByName = zipAttrsWith (n: v: v) ( 766 map ( 767 module: 768 mapAttrs (n: value: { 769 inherit (module) file; 770 inherit value; 771 }) module.config 772 ) checkedConfigs 773 ); 774 775 # Convert an option tree decl to a submodule option decl 776 optionTreeToOption = 777 decl: 778 if isOption decl.options then 779 decl 780 else 781 decl 782 // { 783 options = mkOption { 784 type = types.submoduleWith { 785 modules = [ { options = decl.options; } ]; 786 # `null` is not intended for use by modules. It is an internal 787 # value that means "whatever the user has declared elsewhere". 788 # This might become obsolete with https://github.com/NixOS/nixpkgs/issues/162398 789 shorthandOnlyDefinesConfig = null; 790 }; 791 }; 792 }; 793 794 resultsByName = mapAttrs ( 795 name: decls: 796 # We're descending into attribute ‘name’. 797 let 798 loc = prefix ++ [ name ]; 799 defns = pushedDownDefinitionsByName.${name} or [ ]; 800 defns' = rawDefinitionsByName.${name} or [ ]; 801 optionDecls = filter ( 802 m: 803 m.options ? _type 804 && (m.options._type == "option" || throwDeclarationTypeError loc m.options._type m._file) 805 ) decls; 806 in 807 if length optionDecls == length decls then 808 let 809 opt = fixupOptionType loc (mergeOptionDecls loc decls); 810 in 811 { 812 matchedOptions = evalOptionValue loc opt defns'; 813 unmatchedDefns = [ ]; 814 } 815 else if optionDecls != [ ] then 816 if 817 all (x: x.options.type.name or null == "submodule") optionDecls 818 # Raw options can only be merged into submodules. Merging into 819 # attrsets might be nice, but ambiguous. Suppose we have 820 # attrset as a `attrsOf submodule`. User declares option 821 # attrset.foo.bar, this could mean: 822 # a. option `bar` is only available in `attrset.foo` 823 # b. option `foo.bar` is available in all `attrset.*` 824 # c. reject and require "<name>" as a reminder that it behaves like (b). 825 # d. magically combine (a) and (c). 826 # All of the above are merely syntax sugar though. 827 then 828 let 829 opt = fixupOptionType loc (mergeOptionDecls loc (map optionTreeToOption decls)); 830 in 831 { 832 matchedOptions = evalOptionValue loc opt defns'; 833 unmatchedDefns = [ ]; 834 } 835 else 836 let 837 nonOptions = filter (m: !isOption m.options) decls; 838 in 839 throw "The option `${showOption loc}' in module `${(head optionDecls)._file}' would be a parent of the following options, but its type `${ 840 (head optionDecls).options.type.description or "<no description>" 841 }' does not support nested options.\n${showRawDecls loc nonOptions}" 842 else 843 mergeModules' loc decls defns 844 ) declsByName; 845 846 matchedOptions = mapAttrs (n: v: v.matchedOptions) resultsByName; 847 848 # an attrset 'name' => list of unmatched definitions for 'name' 849 unmatchedDefnsByName = 850 # Propagate all unmatched definitions from nested option sets 851 mapAttrs (n: v: v.unmatchedDefns) resultsByName 852 # Plus the definitions for the current prefix that don't have a matching option 853 // removeAttrs rawDefinitionsByName (attrNames matchedOptions); 854 in 855 { 856 inherit matchedOptions; 857 858 # Transforms unmatchedDefnsByName into a list of definitions 859 unmatchedDefns = 860 if configs == [ ] then 861 # When no config values exist, there can be no unmatched config, so 862 # we short circuit and avoid evaluating more _options_ than necessary. 863 [ ] 864 else 865 concatLists ( 866 mapAttrsToList ( 867 name: defs: 868 map ( 869 def: 870 def 871 // { 872 # Set this so we know when the definition first left unmatched territory 873 prefix = [ name ] ++ (def.prefix or [ ]); 874 } 875 ) defs 876 ) unmatchedDefnsByName 877 ); 878 }; 879 880 throwDeclarationTypeError = 881 loc: actualTag: file: 882 let 883 name = lib.strings.escapeNixIdentifier (lib.lists.last loc); 884 path = showOption loc; 885 depth = length loc; 886 887 paragraphs = 888 [ 889 "In module ${file}: expected an option declaration at option path `${path}` but got an attribute set with type ${actualTag}" 890 ] 891 ++ optional (actualTag == "option-type") '' 892 When declaring an option, you must wrap the type in a `mkOption` call. It should look somewhat like: 893 ${comment} 894 ${name} = lib.mkOption { 895 description = ...; 896 type = <the type you wrote for ${name}>; 897 ... 898 }; 899 ''; 900 901 # Ideally we'd know the exact syntax they used, but short of that, 902 # we can only reliably repeat the last. However, we repeat the 903 # full path in a non-misleading way here, in case they overlook 904 # the start of the message. Examples attract attention. 905 comment = optionalString (depth > 1) "\n # ${showOption loc}"; 906 in 907 throw (concatStringsSep "\n\n" paragraphs); 908 909 /** 910 Merge multiple option declarations into a single declaration. In 911 general, there should be only one declaration of each option. 912 The exception is the options attribute, which specifies 913 sub-options. These can be specified multiple times to allow one 914 module to add sub-options to an option declared somewhere else 915 (e.g. multiple modules define sub-options for fileSystems). 916 917 'loc' is the list of attribute names where the option is located. 918 919 'opts' is a list of modules. Each module has an options attribute which 920 correspond to the definition of 'loc' in 'opt.file'. 921 922 # Inputs 923 924 `loc` 925 926 : 1\. Function argument 927 928 `opts` 929 930 : 2\. Function argument 931 */ 932 mergeOptionDecls = 933 loc: opts: 934 foldl' 935 ( 936 res: opt: 937 let 938 t = res.type; 939 t' = opt.options.type; 940 mergedType = t.typeMerge t'.functor; 941 typesMergeable = mergedType != null; 942 943 # TODO: Remove this when all downstream reliances of internals: 'functor.wrapped' are sufficiently migrated. 944 # A function that adds the deprecated wrapped message to a type. 945 addDeprecatedWrapped = 946 t: 947 t 948 // { 949 functor = t.functor // { 950 wrapped = t.functor.wrappedDeprecationMessage { 951 inherit loc; 952 }; 953 }; 954 }; 955 956 typeSet = 957 if opt.options ? type then 958 if res ? type then 959 if typesMergeable then 960 { 961 type = 962 if mergedType ? functor.wrappedDeprecationMessage then 963 addDeprecatedWrapped mergedType 964 else 965 mergedType; 966 } 967 else 968 # Keep in sync with the same error below! 969 throw 970 "The option `${showOption loc}' in `${opt._file}' is already declared in ${showFiles res.declarations}." 971 else if opt.options.type ? functor.wrappedDeprecationMessage then 972 { type = addDeprecatedWrapped opt.options.type; } 973 else 974 { } 975 else 976 { }; 977 978 bothHave = k: opt.options ? ${k} && res ? ${k}; 979 in 980 if bothHave "default" || bothHave "example" || bothHave "description" || bothHave "apply" then 981 # Keep in sync with the same error above! 982 throw 983 "The option `${showOption loc}' in `${opt._file}' is already declared in ${showFiles res.declarations}." 984 else 985 let 986 getSubModules = opt.options.type.getSubModules or null; 987 submodules = 988 if getSubModules != null then 989 map (setDefaultModuleLocation opt._file) getSubModules ++ res.options 990 else 991 res.options; 992 in 993 opt.options 994 // res 995 // { 996 declarations = res.declarations ++ [ opt._file ]; 997 # In the case of modules that are generated dynamically, we won't 998 # have exact declaration lines; fall back to just the file being 999 # evaluated. 1000 declarationPositions = 1001 res.declarationPositions 1002 ++ ( 1003 if opt.pos != null then 1004 [ opt.pos ] 1005 else 1006 [ 1007 { 1008 file = opt._file; 1009 line = null; 1010 column = null; 1011 } 1012 ] 1013 ); 1014 options = submodules; 1015 } 1016 // typeSet 1017 ) 1018 { 1019 inherit loc; 1020 declarations = [ ]; 1021 declarationPositions = [ ]; 1022 options = [ ]; 1023 } 1024 opts; 1025 1026 /** 1027 Merge all the definitions of an option to produce the final 1028 config value. 1029 1030 # Inputs 1031 1032 `loc` 1033 1034 : 1\. Function argument 1035 1036 `opt` 1037 1038 : 2\. Function argument 1039 1040 `defs` 1041 1042 : 3\. Function argument 1043 */ 1044 evalOptionValue = 1045 loc: opt: defs: 1046 let 1047 # Add in the default value for this option, if any. 1048 defs' = 1049 (optional (opt ? default) { 1050 file = head opt.declarations; 1051 value = mkOptionDefault opt.default; 1052 }) 1053 ++ defs; 1054 1055 # Handle properties, check types, and merge everything together. 1056 res = 1057 if opt.readOnly or false && length defs' > 1 then 1058 let 1059 # For a better error message, evaluate all readOnly definitions as 1060 # if they were the only definition. 1061 separateDefs = map ( 1062 def: 1063 def 1064 // { 1065 value = (mergeDefinitions loc opt.type [ def ]).mergedValue; 1066 } 1067 ) defs'; 1068 in 1069 throw "The option `${showOption loc}' is read-only, but it's set multiple times. Definition values:${showDefs separateDefs}" 1070 else 1071 mergeDefinitions loc opt.type defs'; 1072 1073 # Apply the 'apply' function to the merged value. This allows options to 1074 # yield a value computed from the definitions 1075 value = if opt ? apply then opt.apply res.mergedValue else res.mergedValue; 1076 1077 warnDeprecation = 1078 warnIf (opt.type.deprecationMessage != null) 1079 "The type `types.${opt.type.name}' of option `${showOption loc}' defined in ${showFiles opt.declarations} is deprecated. ${opt.type.deprecationMessage}"; 1080 1081 in 1082 warnDeprecation opt 1083 // { 1084 value = addErrorContext "while evaluating the option `${showOption loc}':" value; 1085 inherit (res.defsFinal') highestPrio; 1086 definitions = map (def: def.value) res.defsFinal; 1087 files = map (def: def.file) res.defsFinal; 1088 definitionsWithLocations = res.defsFinal; 1089 inherit (res) isDefined; 1090 # This allows options to be correctly displayed using `${options.path.to.it}` 1091 __toString = _: showOption loc; 1092 }; 1093 1094 # Merge definitions of a value of a given type. 1095 mergeDefinitions = loc: type: defs: rec { 1096 defsFinal' = 1097 let 1098 # Process mkMerge and mkIf properties. 1099 defs' = concatMap ( 1100 m: 1101 map ( 1102 value: 1103 if value._type or null == "definition" then 1104 value 1105 else 1106 { 1107 inherit (m) file; 1108 inherit value; 1109 } 1110 ) (addErrorContext "while evaluating definitions from `${m.file}':" (dischargeProperties m.value)) 1111 ) defs; 1112 1113 # Process mkOverride properties. 1114 defs'' = filterOverrides' defs'; 1115 1116 # Sort mkOrder properties. 1117 defs''' = 1118 # Avoid sorting if we don't have to. 1119 if any (def: def.value._type or "" == "order") defs''.values then 1120 sortProperties defs''.values 1121 else 1122 defs''.values; 1123 in 1124 { 1125 values = defs'''; 1126 inherit (defs'') highestPrio; 1127 }; 1128 defsFinal = defsFinal'.values; 1129 1130 # Type-check the remaining definitions, and merge them. Or throw if no definitions. 1131 mergedValue = 1132 if isDefined then 1133 if all (def: type.check def.value) defsFinal then 1134 type.merge loc defsFinal 1135 else 1136 let 1137 allInvalid = filter (def: !type.check def.value) defsFinal; 1138 in 1139 throw "A definition for option `${showOption loc}' is not of type `${type.description}'. Definition values:${showDefs allInvalid}" 1140 else 1141 # (nixos-option detects this specific error message and gives it special 1142 # handling. If changed here, please change it there too.) 1143 throw 1144 "The option `${showOption loc}' was accessed but has no value defined. Try setting the option."; 1145 1146 isDefined = defsFinal != [ ]; 1147 1148 optionalValue = if isDefined then { value = mergedValue; } else { }; 1149 }; 1150 1151 /** 1152 Given a config set, expand mkMerge properties, and push down the 1153 other properties into the children. The result is a list of 1154 config sets that do not have properties at top-level. For 1155 example, 1156 1157 mkMerge [ { boot = set1; } (mkIf cond { boot = set2; services = set3; }) ] 1158 1159 is transformed into 1160 1161 [ { boot = set1; } { boot = mkIf cond set2; services = mkIf cond set3; } ]. 1162 1163 This transform is the critical step that allows mkIf conditions 1164 to refer to the full configuration without creating an infinite 1165 recursion. 1166 1167 # Inputs 1168 1169 `cfg` 1170 1171 : 1\. Function argument 1172 */ 1173 pushDownProperties = 1174 cfg: 1175 if cfg._type or "" == "merge" then 1176 concatMap pushDownProperties cfg.contents 1177 else if cfg._type or "" == "if" then 1178 map (mapAttrs (n: v: mkIf cfg.condition v)) (pushDownProperties cfg.content) 1179 else if cfg._type or "" == "override" then 1180 map (mapAttrs (n: v: mkOverride cfg.priority v)) (pushDownProperties cfg.content) 1181 # FIXME: handle mkOrder? 1182 else 1183 [ cfg ]; 1184 1185 /** 1186 Given a config value, expand mkMerge properties, and discharge 1187 any mkIf conditions. That is, this is the place where mkIf 1188 conditions are actually evaluated. The result is a list of 1189 config values. For example, mkIf false x yields [], 1190 mkIf true x yields [x], and 1191 1192 mkMerge [ 1 (mkIf true 2) (mkIf true (mkIf false 3)) ] 1193 1194 yields [ 1 2 ]. 1195 1196 # Inputs 1197 1198 `def` 1199 1200 : 1\. Function argument 1201 */ 1202 dischargeProperties = 1203 def: 1204 if def._type or "" == "merge" then 1205 concatMap dischargeProperties def.contents 1206 else if def._type or "" == "if" then 1207 if isBool def.condition then 1208 if def.condition then dischargeProperties def.content else [ ] 1209 else 1210 throw "mkIf called with a non-Boolean condition" 1211 else 1212 [ def ]; 1213 1214 /** 1215 Given a list of config values, process the mkOverride properties, 1216 that is, return the values that have the highest (that is, 1217 numerically lowest) priority, and strip the mkOverride 1218 properties. For example, 1219 1220 [ { file = "/1"; value = mkOverride 10 "a"; } 1221 { file = "/2"; value = mkOverride 20 "b"; } 1222 { file = "/3"; value = "z"; } 1223 { file = "/4"; value = mkOverride 10 "d"; } 1224 ] 1225 1226 yields 1227 1228 [ { file = "/1"; value = "a"; } 1229 { file = "/4"; value = "d"; } 1230 ] 1231 1232 Note that "z" has the default priority 100. 1233 1234 # Inputs 1235 1236 `defs` 1237 1238 : 1\. Function argument 1239 */ 1240 filterOverrides = defs: (filterOverrides' defs).values; 1241 1242 filterOverrides' = 1243 defs: 1244 let 1245 getPrio = 1246 def: if def.value._type or "" == "override" then def.value.priority else defaultOverridePriority; 1247 highestPrio = foldl' (prio: def: min (getPrio def) prio) 9999 defs; 1248 strip = 1249 def: if def.value._type or "" == "override" then def // { value = def.value.content; } else def; 1250 in 1251 { 1252 values = concatMap (def: if getPrio def == highestPrio then [ (strip def) ] else [ ]) defs; 1253 inherit highestPrio; 1254 }; 1255 1256 /** 1257 Sort a list of properties. The sort priority of a property is 1258 defaultOrderPriority by default, but can be overridden by wrapping the property 1259 using mkOrder. 1260 1261 # Inputs 1262 1263 `defs` 1264 1265 : 1\. Function argument 1266 */ 1267 sortProperties = 1268 defs: 1269 let 1270 strip = 1271 def: 1272 if def.value._type or "" == "order" then 1273 def 1274 // { 1275 value = def.value.content; 1276 inherit (def.value) priority; 1277 } 1278 else 1279 def; 1280 defs' = map strip defs; 1281 compare = a: b: (a.priority or defaultOrderPriority) < (b.priority or defaultOrderPriority); 1282 in 1283 sort compare defs'; 1284 1285 # This calls substSubModules, whose entire purpose is only to ensure that 1286 # option declarations in submodules have accurate position information. 1287 # TODO: Merge this into mergeOptionDecls 1288 fixupOptionType = 1289 loc: opt: 1290 if opt.type.getSubModules or null == null then 1291 opt // { type = opt.type or types.unspecified; } 1292 else 1293 opt 1294 // { 1295 type = opt.type.substSubModules opt.options; 1296 options = [ ]; 1297 }; 1298 1299 /** 1300 Merge an option's definitions in a way that preserves the priority of the 1301 individual attributes in the option value. 1302 1303 This does not account for all option semantics, such as readOnly. 1304 1305 # Inputs 1306 1307 `opt` 1308 1309 : 1\. Function argument 1310 1311 # Type 1312 1313 ``` 1314 option -> attrsOf { highestPrio, value } 1315 ``` 1316 */ 1317 mergeAttrDefinitionsWithPrio = 1318 opt: 1319 let 1320 defsByAttr = zipAttrs ( 1321 concatLists ( 1322 concatMap ( 1323 { value, ... }@def: 1324 map (mapAttrsToList ( 1325 k: value: { 1326 ${k} = def // { 1327 inherit value; 1328 }; 1329 } 1330 )) (pushDownProperties value) 1331 ) opt.definitionsWithLocations 1332 ) 1333 ); 1334 in 1335 assert opt.type.name == "attrsOf" || opt.type.name == "lazyAttrsOf"; 1336 mapAttrs ( 1337 k: v: 1338 let 1339 merging = mergeDefinitions (opt.loc ++ [ k ]) opt.type.nestedTypes.elemType v; 1340 in 1341 { 1342 value = merging.mergedValue; 1343 inherit (merging.defsFinal') highestPrio; 1344 } 1345 ) defsByAttr; 1346 1347 /** 1348 Properties. 1349 1350 # Inputs 1351 1352 `condition` 1353 1354 : 1\. Function argument 1355 1356 `content` 1357 1358 : 2\. Function argument 1359 */ 1360 1361 mkIf = condition: content: { 1362 _type = "if"; 1363 inherit condition content; 1364 }; 1365 1366 mkAssert = 1367 assertion: message: content: 1368 mkIf (if assertion then true else throw "\nFailed assertion: ${message}") content; 1369 1370 mkMerge = contents: { 1371 _type = "merge"; 1372 inherit contents; 1373 }; 1374 1375 /** 1376 Return a definition with file location information. 1377 */ 1378 mkDefinition = args@{ file, value, ... }: args // { _type = "definition"; }; 1379 1380 mkOverride = priority: content: { 1381 _type = "override"; 1382 inherit priority content; 1383 }; 1384 1385 mkOptionDefault = mkOverride 1500; # priority of option defaults 1386 mkDefault = mkOverride 1000; # used in config sections of non-user modules to set a default 1387 defaultOverridePriority = 100; 1388 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 1389 mkForce = mkOverride 50; 1390 mkVMOverride = mkOverride 10; # used by ‘nixos-rebuild build-vm’ 1391 1392 defaultPriority = 1393 warnIf (oldestSupportedReleaseIsAtLeast 2305) 1394 "lib.modules.defaultPriority is deprecated, please use lib.modules.defaultOverridePriority instead." 1395 defaultOverridePriority; 1396 1397 mkFixStrictness = warn "lib.mkFixStrictness has no effect and will be removed. It returns its argument unmodified, so you can just remove any calls." id; 1398 1399 mkOrder = priority: content: { 1400 _type = "order"; 1401 inherit priority content; 1402 }; 1403 1404 mkBefore = mkOrder 500; 1405 defaultOrderPriority = 1000; 1406 mkAfter = mkOrder 1500; 1407 1408 # Convenient property used to transfer all definitions and their 1409 # properties from one option to another. This property is useful for 1410 # renaming options, and also for including properties from another module 1411 # system, including sub-modules. 1412 # 1413 # { config, options, ... }: 1414 # 1415 # { 1416 # # 'bar' might not always be defined in the current module-set. 1417 # config.foo.enable = mkAliasDefinitions (options.bar.enable or {}); 1418 # 1419 # # 'barbaz' has to be defined in the current module-set. 1420 # config.foobar.paths = mkAliasDefinitions options.barbaz.paths; 1421 # } 1422 # 1423 # Note, this is different than taking the value of the option and using it 1424 # as a definition, as the new definition will not keep the mkOverride / 1425 # mkDefault properties of the previous option. 1426 # 1427 mkAliasDefinitions = mkAliasAndWrapDefinitions id; 1428 mkAliasAndWrapDefinitions = wrap: option: mkAliasIfDef option (wrap (mkMerge option.definitions)); 1429 1430 # Similar to mkAliasAndWrapDefinitions but copies over the priority from the 1431 # option as well. 1432 # 1433 # If a priority is not set, it assumes a priority of defaultOverridePriority. 1434 mkAliasAndWrapDefsWithPriority = 1435 wrap: option: 1436 let 1437 prio = option.highestPrio or defaultOverridePriority; 1438 defsWithPrio = map (mkOverride prio) option.definitions; 1439 in 1440 mkAliasIfDef option (wrap (mkMerge defsWithPrio)); 1441 1442 mkAliasIfDef = option: mkIf (isOption option && option.isDefined); 1443 1444 /** 1445 Compatibility. 1446 1447 # Inputs 1448 1449 `modules` 1450 1451 : 1\. Function argument 1452 1453 `args` 1454 1455 : 2\. Function argument 1456 */ 1457 fixMergeModules = 1458 modules: args: 1459 evalModules { 1460 inherit modules args; 1461 check = false; 1462 }; 1463 1464 /** 1465 Return a module that causes a warning to be shown if the 1466 specified option is defined. For example, 1467 1468 mkRemovedOptionModule [ "boot" "loader" "grub" "bootDevice" ] "<replacement instructions>" 1469 1470 causes a assertion if the user defines boot.loader.grub.bootDevice. 1471 1472 replacementInstructions is a string that provides instructions on 1473 how to achieve the same functionality without the removed option, 1474 or alternatively a reasoning why the functionality is not needed. 1475 replacementInstructions SHOULD be provided! 1476 1477 # Inputs 1478 1479 `optionName` 1480 1481 : 1\. Function argument 1482 1483 `replacementInstructions` 1484 1485 : 2\. Function argument 1486 */ 1487 mkRemovedOptionModule = 1488 optionName: replacementInstructions: 1489 { options, ... }: 1490 { 1491 options = setAttrByPath optionName (mkOption { 1492 visible = false; 1493 apply = 1494 x: 1495 throw "The option `${showOption optionName}' can no longer be used since it's been removed. ${replacementInstructions}"; 1496 }); 1497 config.assertions = 1498 let 1499 opt = getAttrFromPath optionName options; 1500 in 1501 [ 1502 { 1503 assertion = !opt.isDefined; 1504 message = '' 1505 The option definition `${showOption optionName}' in ${showFiles opt.files} no longer has any effect; please remove it. 1506 ${replacementInstructions} 1507 ''; 1508 } 1509 ]; 1510 }; 1511 1512 /** 1513 Return a module that causes a warning to be shown if the 1514 specified "from" option is defined; the defined value is however 1515 forwarded to the "to" option. This can be used to rename options 1516 while providing backward compatibility. For example, 1517 1518 mkRenamedOptionModule [ "boot" "copyKernels" ] [ "boot" "loader" "grub" "copyKernels" ] 1519 1520 forwards any definitions of boot.copyKernels to 1521 boot.loader.grub.copyKernels while printing a warning. 1522 1523 This also copies over the priority from the aliased option to the 1524 non-aliased option. 1525 1526 # Inputs 1527 1528 `from` 1529 1530 : 1\. Function argument 1531 1532 `to` 1533 1534 : 2\. Function argument 1535 */ 1536 mkRenamedOptionModule = 1537 from: to: 1538 doRename { 1539 inherit from to; 1540 visible = false; 1541 warn = true; 1542 use = trace "Obsolete option `${showOption from}' is used. It was renamed to `${showOption to}'."; 1543 }; 1544 1545 mkRenamedOptionModuleWith = 1546 { 1547 /** 1548 Old option path as list of strings. 1549 */ 1550 from, 1551 /** 1552 New option path as list of strings. 1553 */ 1554 to, 1555 1556 /** 1557 Release number of the first release that contains the rename, ignoring backports. 1558 Set it to the upcoming release, matching the nixpkgs/.version file. 1559 */ 1560 sinceRelease, 1561 1562 }: 1563 doRename { 1564 inherit from to; 1565 visible = false; 1566 warn = oldestSupportedReleaseIsAtLeast sinceRelease; 1567 use = warnIf (oldestSupportedReleaseIsAtLeast sinceRelease) "Obsolete option `${showOption from}' is used. It was renamed to `${showOption to}'."; 1568 }; 1569 1570 /** 1571 Return a module that causes a warning to be shown if any of the "from" 1572 option is defined; the defined values can be used in the "mergeFn" to set 1573 the "to" value. 1574 This function can be used to merge multiple options into one that has a 1575 different type. 1576 1577 "mergeFn" takes the module "config" as a parameter and must return a value 1578 of "to" option type. 1579 1580 mkMergedOptionModule 1581 [ [ "a" "b" "c" ] 1582 [ "d" "e" "f" ] ] 1583 [ "x" "y" "z" ] 1584 (config: 1585 let value = p: getAttrFromPath p config; 1586 in 1587 if (value [ "a" "b" "c" ]) == true then "foo" 1588 else if (value [ "d" "e" "f" ]) == true then "bar" 1589 else "baz") 1590 1591 - options.a.b.c is a removed boolean option 1592 - options.d.e.f is a removed boolean option 1593 - options.x.y.z is a new str option that combines a.b.c and d.e.f 1594 functionality 1595 1596 This show a warning if any a.b.c or d.e.f is set, and set the value of 1597 x.y.z to the result of the merge function 1598 1599 # Inputs 1600 1601 `from` 1602 1603 : 1\. Function argument 1604 1605 `to` 1606 1607 : 2\. Function argument 1608 1609 `mergeFn` 1610 1611 : 3\. Function argument 1612 */ 1613 mkMergedOptionModule = 1614 from: to: mergeFn: 1615 { config, options, ... }: 1616 { 1617 options = foldl' recursiveUpdate { } ( 1618 map ( 1619 path: 1620 setAttrByPath path (mkOption { 1621 visible = false; 1622 # To use the value in mergeFn without triggering errors 1623 default = "_mkMergedOptionModule"; 1624 }) 1625 ) from 1626 ); 1627 1628 config = 1629 { 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 /** 1980 Format text with one line break between each list item. 1981 */ 1982 lines = concatMapStringsSep "\n" trim; 1983 1984 /** 1985 Format text with two line break between each list item. 1986 */ 1987 paragraphs = concatMapStringsSep "\n\n" trim; 1988 1989 /** 1990 ``` 1991 optionalMatch 1992 { foo = "Foo result"; 1993 bar = "Bar result"; 1994 } "foo" 1995 == [ "Foo result" ] 1996 1997 optionalMatch { foo = "Foo"; } "baz" == [ ] 1998 1999 optionalMatch { foo = "Foo"; } true == [ ] 2000 ``` 2001 */ 2002 optionalMatch = 2003 cases: value: if isString value && cases ? ${value} then [ cases.${value} ] else [ ]; 2004 2005 # esc = builtins.fromJSON "\"\\u001b\""; 2006 esc = builtins.fromJSON "\"\\u001b\""; 2007 # Bold purple for warnings 2008 warn = s: "${esc}[1;35m${s}${esc}[0m"; 2009 # Bold green for suggestions 2010 good = s: "${esc}[1;32m${s}${esc}[0m"; 2011 # Bold, default color for code 2012 code = s: "${esc}[1m${s}${esc}[0m"; 2013 2014 in 2015 { 2016 2017 /** 2018 When load a value with a (wrong) _type as a module 2019 */ 2020 not_a_module = 2021 { 2022 fallbackFile, 2023 value, 2024 _type, 2025 expectedClass ? null, 2026 }: 2027 paragraphs ( 2028 [ 2029 '' 2030 Expected a module, but found a value of type ${warn (escapeNixString _type)}${into_fallback_file_maybe fallbackFile}. 2031 A module is typically loaded by adding it the ${code "imports = [ ... ];"} attribute of an existing module, or in the ${code "modules = [ ... ];"} argument of various functions. 2032 Please make sure that each of the list items is a module, and not a different kind of value. 2033 '' 2034 ] 2035 ++ (optionalMatch { 2036 "configuration" = trim '' 2037 If you really mean to import this configuration, instead please only import the modules that make up the configuration. 2038 You may have to create a `let` binding, file or attribute to give yourself access to the relevant modules. 2039 While loading a configuration into the module system is a very sensible idea, it can not be done cleanly in practice. 2040 ''; 2041 # ^^ 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. 2042 2043 "flake" = lines ( 2044 [ 2045 (trim '' 2046 Perhaps you forgot to select an attribute name? 2047 Instead of, for example, 2048 ${warn "inputs.someflake"} 2049 you need to write something like 2050 ${warn "inputs.someflake"}${ 2051 if expectedClass == null then 2052 good ".modules.someApp.default" 2053 else 2054 good ".modules.${expectedClass}.default" 2055 2056 } 2057 '') 2058 ] 2059 ++ optionalMatch { 2060 # We'll no more than 5 custom suggestions here. 2061 # Please switch to `.modules.${class}` in your Module System application. 2062 "nixos" = trim '' 2063 or 2064 ${warn "inputs.someflake"}${good ".nixosModules.default"} 2065 ''; 2066 "darwin" = trim '' 2067 or 2068 ${warn "inputs.someflake"}${good ".darwinModules.default"} 2069 ''; 2070 } expectedClass 2071 ); 2072 } _type) 2073 ); 2074 }; 2075 2076in 2077private 2078// { 2079 # NOTE: not all of these functions are necessarily public interfaces; some 2080 # are just needed by types.nix, but are not meant to be consumed 2081 # externally. 2082 inherit 2083 defaultOrderPriority 2084 defaultOverridePriority 2085 defaultPriority 2086 doRename 2087 evalModules 2088 evalOptionValue # for use by lib.types 2089 filterOverrides 2090 filterOverrides' 2091 fixMergeModules 2092 fixupOptionType # should be private? 2093 importApply 2094 importJSON 2095 importTOML 2096 mergeDefinitions 2097 mergeAttrDefinitionsWithPrio 2098 mergeOptionDecls # should be private? 2099 mkAfter 2100 mkAliasAndWrapDefinitions 2101 mkAliasAndWrapDefsWithPriority 2102 mkAliasDefinitions 2103 mkAliasIfDef 2104 mkAliasOptionModule 2105 mkAliasOptionModuleMD 2106 mkAssert 2107 mkBefore 2108 mkChangedOptionModule 2109 mkDefault 2110 mkDefinition 2111 mkDerivedConfig 2112 mkFixStrictness 2113 mkForce 2114 mkIf 2115 mkImageMediaOverride 2116 mkMerge 2117 mkMergedOptionModule 2118 mkOptionDefault 2119 mkOrder 2120 mkOverride 2121 mkRemovedOptionModule 2122 mkRenamedOptionModule 2123 mkRenamedOptionModuleWith 2124 mkVMOverride 2125 setDefaultModuleLocation 2126 sortProperties 2127 ; 2128}