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