at 24.11-pre 58 kB view raw
1{ lib }: 2 3let 4 inherit (lib) 5 all 6 any 7 attrByPath 8 attrNames 9 catAttrs 10 concatLists 11 concatMap 12 concatStringsSep 13 elem 14 filter 15 foldl' 16 getAttrFromPath 17 head 18 id 19 imap1 20 isAttrs 21 isBool 22 isFunction 23 isList 24 isString 25 length 26 mapAttrs 27 mapAttrsToList 28 mapAttrsRecursiveCond 29 min 30 optional 31 optionalAttrs 32 optionalString 33 recursiveUpdate 34 reverseList sort 35 setAttrByPath 36 types 37 warnIf 38 zipAttrsWith 39 ; 40 inherit (lib.options) 41 isOption 42 mkOption 43 showDefs 44 showFiles 45 showOption 46 unknownModule 47 ; 48 inherit (lib.strings) 49 isConvertibleWithToString 50 ; 51 52 showDeclPrefix = loc: decl: prefix: 53 " - option(s) with prefix `${showOption (loc ++ [prefix])}' in module `${decl._file}'"; 54 showRawDecls = loc: decls: 55 concatStringsSep "\n" 56 (sort (a: b: a < b) 57 (concatMap 58 (decl: map 59 (showDeclPrefix loc decl) 60 (attrNames decl.options) 61 ) 62 decls 63 )); 64 65 /* See https://nixos.org/manual/nixpkgs/unstable/#module-system-lib-evalModules 66 or file://./../doc/module-system/module-system.chapter.md 67 68 !!! Please think twice before adding to this argument list! The more 69 that is specified here instead of in the modules themselves the harder 70 it is to transparently move a set of modules to be a submodule of another 71 config (as the proper arguments need to be replicated at each call to 72 evalModules) and the less declarative the module set is. */ 73 evalModules = evalModulesArgs@ 74 { modules 75 , prefix ? [] 76 , # This should only be used for special arguments that need to be evaluated 77 # when resolving module structure (like in imports). For everything else, 78 # there's _module.args. If specialArgs.modulesPath is defined it will be 79 # used as the base path for disabledModules. 80 specialArgs ? {} 81 , # `class`: 82 # A nominal type for modules. When set and non-null, this adds a check to 83 # make sure that only compatible modules are imported. 84 class ? null 85 , # This would be remove in the future, Prefer _module.args option instead. 86 args ? {} 87 , # This would be remove in the future, Prefer _module.check option instead. 88 check ? true 89 }: 90 let 91 withWarnings = x: 92 lib.warnIf (evalModulesArgs?args) "The args argument to evalModules is deprecated. Please set config._module.args instead." 93 lib.warnIf (evalModulesArgs?check) "The check argument to evalModules is deprecated. Please set config._module.check instead." 94 x; 95 96 legacyModules = 97 optional (evalModulesArgs?args) { 98 config = { 99 _module.args = args; 100 }; 101 } 102 ++ optional (evalModulesArgs?check) { 103 config = { 104 _module.check = mkDefault check; 105 }; 106 }; 107 regularModules = modules ++ legacyModules; 108 109 # This internal module declare internal options under the `_module' 110 # attribute. These options are fragile, as they are used by the 111 # module system to change the interpretation of modules. 112 # 113 # When extended with extendModules or moduleType, a fresh instance of 114 # this module is used, to avoid conflicts and allow chaining of 115 # extendModules. 116 internalModule = rec { 117 _file = "lib/modules.nix"; 118 119 key = _file; 120 121 options = { 122 _module.args = mkOption { 123 # Because things like `mkIf` are entirely useless for 124 # `_module.args` (because there's no way modules can check which 125 # arguments were passed), we'll use `lazyAttrsOf` which drops 126 # support for that, in turn it's lazy in its values. This means e.g. 127 # a `_module.args.pkgs = import (fetchTarball { ... }) {}` won't 128 # start a download when `pkgs` wasn't evaluated. 129 type = types.lazyAttrsOf types.raw; 130 # Only render documentation once at the root of the option tree, 131 # not for all individual submodules. 132 # Allow merging option decls to make this internal regardless. 133 ${if prefix == [] 134 then null # unset => visible 135 else "internal"} = true; 136 # TODO: Change the type of this option to a submodule with a 137 # freeformType, so that individual arguments can be documented 138 # separately 139 description = '' 140 Additional arguments passed to each module in addition to ones 141 like `lib`, `config`, 142 and `pkgs`, `modulesPath`. 143 144 This option is also available to all submodules. Submodules do not 145 inherit args from their parent module, nor do they provide args to 146 their parent module or sibling submodules. The sole exception to 147 this is the argument `name` which is provided by 148 parent modules to a submodule and contains the attribute name 149 the submodule is bound to, or a unique generated name if it is 150 not bound to an attribute. 151 152 Some arguments are already passed by default, of which the 153 following *cannot* be changed with this option: 154 - {var}`lib`: The nixpkgs library. 155 - {var}`config`: The results of all options after merging the values from all modules together. 156 - {var}`options`: The options declared in all modules. 157 - {var}`specialArgs`: The `specialArgs` argument passed to `evalModules`. 158 - All attributes of {var}`specialArgs` 159 160 Whereas option values can generally depend on other option values 161 thanks to laziness, this does not apply to `imports`, which 162 must be computed statically before anything else. 163 164 For this reason, callers of the module system can provide `specialArgs` 165 which are available during import resolution. 166 167 For NixOS, `specialArgs` includes 168 {var}`modulesPath`, which allows you to import 169 extra modules from the nixpkgs package tree without having to 170 somehow make the module aware of the location of the 171 `nixpkgs` or NixOS directories. 172 ``` 173 { modulesPath, ... }: { 174 imports = [ 175 (modulesPath + "/profiles/minimal.nix") 176 ]; 177 } 178 ``` 179 180 For NixOS, the default value for this option includes at least this argument: 181 - {var}`pkgs`: The nixpkgs package set according to 182 the {option}`nixpkgs.pkgs` option. 183 ''; 184 }; 185 186 _module.check = mkOption { 187 type = types.bool; 188 internal = true; 189 default = true; 190 description = "Whether to check whether all option definitions have matching declarations."; 191 }; 192 193 _module.freeformType = mkOption { 194 type = types.nullOr types.optionType; 195 internal = true; 196 default = null; 197 description = '' 198 If set, merge all definitions that don't have an associated option 199 together using this type. The result then gets combined with the 200 values of all declared options to produce the final ` 201 config` value. 202 203 If this is `null`, definitions without an option 204 will throw an error unless {option}`_module.check` is 205 turned off. 206 ''; 207 }; 208 209 _module.specialArgs = mkOption { 210 readOnly = true; 211 internal = true; 212 description = '' 213 Externally provided module arguments that can't be modified from 214 within a configuration, but can be used in module imports. 215 ''; 216 }; 217 }; 218 219 config = { 220 _module.args = { 221 inherit extendModules; 222 moduleType = type; 223 }; 224 _module.specialArgs = specialArgs; 225 }; 226 }; 227 228 merged = 229 let collected = collectModules 230 class 231 (specialArgs.modulesPath or "") 232 (regularModules ++ [ internalModule ]) 233 ({ inherit lib options config specialArgs; } // specialArgs); 234 in mergeModules prefix (reverseList collected); 235 236 options = merged.matchedOptions; 237 238 config = 239 let 240 241 # For definitions that have an associated option 242 declaredConfig = mapAttrsRecursiveCond (v: ! isOption v) (_: v: v.value) options; 243 244 # If freeformType is set, this is for definitions that don't have an associated option 245 freeformConfig = 246 let 247 defs = map (def: { 248 file = def.file; 249 value = setAttrByPath def.prefix def.value; 250 }) merged.unmatchedDefns; 251 in if defs == [] then {} 252 else declaredConfig._module.freeformType.merge prefix defs; 253 254 in if declaredConfig._module.freeformType == null then declaredConfig 255 # Because all definitions that had an associated option ended in 256 # declaredConfig, freeformConfig can only contain the non-option 257 # paths, meaning recursiveUpdate will never override any value 258 else recursiveUpdate freeformConfig declaredConfig; 259 260 checkUnmatched = 261 if config._module.check && config._module.freeformType == null && merged.unmatchedDefns != [] then 262 let 263 firstDef = head merged.unmatchedDefns; 264 baseMsg = 265 let 266 optText = showOption (prefix ++ firstDef.prefix); 267 defText = 268 builtins.addErrorContext 269 "while evaluating the error message for definitions for `${optText}', which is an option that does not exist" 270 (builtins.addErrorContext 271 "while evaluating a definition from `${firstDef.file}'" 272 ( showDefs [ firstDef ]) 273 ); 274 in 275 "The option `${optText}' does not exist. Definition values:${defText}"; 276 in 277 if attrNames options == [ "_module" ] 278 # No options were declared at all (`_module` is built in) 279 # but we do have unmatched definitions, and no freeformType (earlier conditions) 280 then 281 let 282 optionName = showOption prefix; 283 in 284 if optionName == "" 285 then throw '' 286 ${baseMsg} 287 288 It seems as if you're trying to declare an option by placing it into `config' rather than `options'! 289 '' 290 else 291 throw '' 292 ${baseMsg} 293 294 However there are no options defined in `${showOption prefix}'. Are you sure you've 295 declared your options properly? This can happen if you e.g. declared your options in `types.submodule' 296 under `config' rather than `options'. 297 '' 298 else throw baseMsg 299 else null; 300 301 checked = builtins.seq checkUnmatched; 302 303 extendModules = extendArgs@{ 304 modules ? [], 305 specialArgs ? {}, 306 prefix ? [], 307 }: 308 evalModules (evalModulesArgs // { 309 inherit class; 310 modules = regularModules ++ modules; 311 specialArgs = evalModulesArgs.specialArgs or {} // specialArgs; 312 prefix = extendArgs.prefix or evalModulesArgs.prefix or []; 313 }); 314 315 type = lib.types.submoduleWith { 316 inherit modules specialArgs class; 317 }; 318 319 result = withWarnings { 320 _type = "configuration"; 321 options = checked options; 322 config = checked (removeAttrs config [ "_module" ]); 323 _module = checked (config._module); 324 inherit extendModules type; 325 class = class; 326 }; 327 in result; 328 329 # collectModules :: (class: String) -> (modulesPath: String) -> (modules: [ Module ]) -> (args: Attrs) -> [ Module ] 330 # 331 # Collects all modules recursively through `import` statements, filtering out 332 # all modules in disabledModules. 333 collectModules = class: let 334 335 # Like unifyModuleSyntax, but also imports paths and calls functions if necessary 336 loadModule = args: fallbackFile: fallbackKey: m: 337 if isFunction m then 338 unifyModuleSyntax fallbackFile fallbackKey (applyModuleArgs fallbackKey m args) 339 else if isAttrs m then 340 if m._type or "module" == "module" then 341 unifyModuleSyntax fallbackFile fallbackKey m 342 else if m._type == "if" || m._type == "override" then 343 loadModule args fallbackFile fallbackKey { config = m; } 344 else 345 throw ( 346 "Could not load a value as a module, because it is of type ${lib.strings.escapeNixString m._type}" 347 + lib.optionalString (fallbackFile != unknownModule) ", in file ${toString fallbackFile}." 348 + lib.optionalString (m._type == "configuration") " If you do intend to import this configuration, please only import the modules that make up the configuration. You may have to create a `let` binding, file or attribute to give yourself access to the relevant modules.\nWhile loading a configuration into the module system is a very sensible idea, it can not be done cleanly in practice." 349 # 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. 350 ) 351 else if isList m then 352 let defs = [{ file = fallbackFile; value = m; }]; in 353 throw "Module imports can't be nested lists. Perhaps you meant to remove one level of lists? Definitions: ${showDefs defs}" 354 else unifyModuleSyntax (toString m) (toString m) (applyModuleArgsIfFunction (toString m) (import m) args); 355 356 checkModule = 357 if class != null 358 then 359 m: 360 if m._class != null -> m._class == class 361 then m 362 else 363 throw "The module ${m._file or m.key} was imported into ${class} instead of ${m._class}." 364 else 365 m: m; 366 367 /* 368 Collects all modules recursively into the form 369 370 { 371 disabled = [ <list of disabled modules> ]; 372 # All modules of the main module list 373 modules = [ 374 { 375 key = <key1>; 376 module = <module for key1>; 377 # All modules imported by the module for key1 378 modules = [ 379 { 380 key = <key1-1>; 381 module = <module for key1-1>; 382 # All modules imported by the module for key1-1 383 modules = [ ... ]; 384 } 385 ... 386 ]; 387 } 388 ... 389 ]; 390 } 391 */ 392 collectStructuredModules = 393 let 394 collectResults = modules: { 395 disabled = concatLists (catAttrs "disabled" modules); 396 inherit modules; 397 }; 398 in parentFile: parentKey: initialModules: args: collectResults (imap1 (n: x: 399 let 400 module = checkModule (loadModule args parentFile "${parentKey}:anon-${toString n}" x); 401 collectedImports = collectStructuredModules module._file module.key module.imports args; 402 in { 403 key = module.key; 404 module = module; 405 modules = collectedImports.modules; 406 disabled = (if module.disabledModules != [] then [{ file = module._file; disabled = module.disabledModules; }] else []) ++ collectedImports.disabled; 407 }) initialModules); 408 409 # filterModules :: String -> { disabled, modules } -> [ Module ] 410 # 411 # Filters a structure as emitted by collectStructuredModules by removing all disabled 412 # modules recursively. It returns the final list of unique-by-key modules 413 filterModules = modulesPath: { disabled, modules }: 414 let 415 moduleKey = file: m: 416 if isString m 417 then 418 if builtins.substring 0 1 m == "/" 419 then m 420 else toString modulesPath + "/" + m 421 422 else if isConvertibleWithToString m 423 then 424 if m?key && m.key != toString m 425 then 426 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." 427 else 428 toString m 429 430 else if m?key 431 then 432 m.key 433 434 else if isAttrs m 435 then 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." 436 else 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 ${builtins.typeOf m}."; 437 438 disabledKeys = concatMap ({ file, disabled }: map (moduleKey file) disabled) disabled; 439 keyFilter = filter (attrs: ! elem attrs.key disabledKeys); 440 in map (attrs: attrs.module) (builtins.genericClosure { 441 startSet = keyFilter modules; 442 operator = attrs: keyFilter attrs.modules; 443 }); 444 445 in modulesPath: initialModules: args: 446 filterModules modulesPath (collectStructuredModules unknownModule "" initialModules args); 447 448 /* Wrap a module with a default location for reporting errors. */ 449 setDefaultModuleLocation = file: m: 450 { _file = file; imports = [ m ]; }; 451 452 /* Massage a module into canonical form, that is, a set consisting 453 of options, config and imports attributes. */ 454 unifyModuleSyntax = file: key: m: 455 let 456 addMeta = config: if m ? meta 457 then mkMerge [ config { meta = m.meta; } ] 458 else config; 459 addFreeformType = config: if m ? freeformType 460 then mkMerge [ config { _module.freeformType = m.freeformType; } ] 461 else config; 462 in 463 if m ? config || m ? options then 464 let badAttrs = removeAttrs m ["_class" "_file" "key" "disabledModules" "imports" "options" "config" "meta" "freeformType"]; in 465 if badAttrs != {} then 466 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." 467 else 468 { _file = toString m._file or file; 469 _class = m._class or null; 470 key = toString m.key or key; 471 disabledModules = m.disabledModules or []; 472 imports = m.imports or []; 473 options = m.options or {}; 474 config = addFreeformType (addMeta (m.config or {})); 475 } 476 else 477 # shorthand syntax 478 lib.throwIfNot (isAttrs m) "module ${file} (${key}) does not look like a module." 479 { _file = toString m._file or file; 480 _class = m._class or null; 481 key = toString m.key or key; 482 disabledModules = m.disabledModules or []; 483 imports = m.require or [] ++ m.imports or []; 484 options = {}; 485 config = addFreeformType (removeAttrs m ["_class" "_file" "key" "disabledModules" "require" "imports" "freeformType"]); 486 }; 487 488 applyModuleArgsIfFunction = key: f: args@{ config, options, lib, ... }: 489 if isFunction f then applyModuleArgs key f args else f; 490 491 applyModuleArgs = key: f: args@{ config, options, lib, ... }: 492 let 493 # Module arguments are resolved in a strict manner when attribute set 494 # deconstruction is used. As the arguments are now defined with the 495 # config._module.args option, the strictness used on the attribute 496 # set argument would cause an infinite loop, if the result of the 497 # option is given as argument. 498 # 499 # To work-around the strictness issue on the deconstruction of the 500 # attributes set argument, we create a new attribute set which is 501 # constructed to satisfy the expected set of attributes. Thus calling 502 # a module will resolve strictly the attributes used as argument but 503 # not their values. The values are forwarding the result of the 504 # evaluation of the option. 505 context = name: ''while evaluating the module argument `${name}' in "${key}":''; 506 extraArgs = builtins.mapAttrs (name: _: 507 builtins.addErrorContext (context name) 508 (args.${name} or config._module.args.${name}) 509 ) (lib.functionArgs f); 510 511 # Note: we append in the opposite order such that we can add an error 512 # context on the explicit arguments of "args" too. This update 513 # operator is used to make the "args@{ ... }: with args.lib;" notation 514 # works. 515 in f (args // extraArgs); 516 517 /* Merge a list of modules. This will recurse over the option 518 declarations in all modules, combining them into a single set. 519 At the same time, for each option declaration, it will merge the 520 corresponding option definitions in all machines, returning them 521 in the value attribute of each option. 522 523 This returns a set like 524 { 525 # A recursive set of options along with their final values 526 matchedOptions = { 527 foo = { _type = "option"; value = "option value of foo"; ... }; 528 bar.baz = { _type = "option"; value = "option value of bar.baz"; ... }; 529 ... 530 }; 531 # A list of definitions that weren't matched by any option 532 unmatchedDefns = [ 533 { file = "file.nix"; prefix = [ "qux" ]; value = "qux"; } 534 ... 535 ]; 536 } 537 */ 538 mergeModules = prefix: modules: 539 mergeModules' prefix modules 540 (concatMap (m: map (config: { file = m._file; inherit config; }) (pushDownProperties m.config)) modules); 541 542 mergeModules' = prefix: modules: configs: 543 let 544 # an attrset 'name' => list of submodules that declare ‘name’. 545 declsByName = 546 zipAttrsWith 547 (n: concatLists) 548 (map 549 (module: let subtree = module.options; in 550 if !(builtins.isAttrs subtree) then 551 throw '' 552 An option declaration for `${builtins.concatStringsSep "." prefix}' has type 553 `${builtins.typeOf subtree}' rather than an attribute set. 554 Did you mean to define this outside of `options'? 555 '' 556 else 557 mapAttrs 558 (n: option: 559 [{ inherit (module) _file; pos = builtins.unsafeGetAttrPos n subtree; options = option; }] 560 ) 561 subtree 562 ) 563 modules); 564 565 # The root of any module definition must be an attrset. 566 checkedConfigs = 567 assert 568 lib.all 569 (c: 570 # TODO: I have my doubts that this error would occur when option definitions are not matched. 571 # The implementation of this check used to be tied to a superficially similar check for 572 # options, so maybe that's why this is here. 573 isAttrs c.config || throw '' 574 In module `${c.file}', you're trying to define a value of type `${builtins.typeOf c.config}' 575 rather than an attribute set for the option 576 `${builtins.concatStringsSep "." prefix}'! 577 578 This usually happens if `${builtins.concatStringsSep "." prefix}' has option 579 definitions inside that are not matched. Please check how to properly define 580 this option by e.g. referring to `man 5 configuration.nix'! 581 '' 582 ) 583 configs; 584 configs; 585 586 # an attrset 'name' => list of submodules that define ‘name’. 587 pushedDownDefinitionsByName = 588 zipAttrsWith 589 (n: concatLists) 590 (map 591 (module: 592 mapAttrs 593 (n: value: 594 map (config: { inherit (module) file; inherit config; }) (pushDownProperties value) 595 ) 596 module.config 597 ) 598 checkedConfigs); 599 # extract the definitions for each loc 600 rawDefinitionsByName = 601 zipAttrsWith 602 (n: concatLists) 603 (map 604 (module: 605 mapAttrs 606 (n: value: 607 [{ inherit (module) file; inherit value; }] 608 ) 609 module.config 610 ) 611 checkedConfigs); 612 613 # Convert an option tree decl to a submodule option decl 614 optionTreeToOption = decl: 615 if isOption decl.options 616 then decl 617 else decl // { 618 options = mkOption { 619 type = types.submoduleWith { 620 modules = [ { options = decl.options; } ]; 621 # `null` is not intended for use by modules. It is an internal 622 # value that means "whatever the user has declared elsewhere". 623 # This might become obsolete with https://github.com/NixOS/nixpkgs/issues/162398 624 shorthandOnlyDefinesConfig = null; 625 }; 626 }; 627 }; 628 629 resultsByName = mapAttrs (name: decls: 630 # We're descending into attribute ‘name’. 631 let 632 loc = prefix ++ [name]; 633 defns = pushedDownDefinitionsByName.${name} or []; 634 defns' = rawDefinitionsByName.${name} or []; 635 optionDecls = filter 636 (m: m.options?_type 637 && (m.options._type == "option" 638 || throwDeclarationTypeError loc m.options._type m._file 639 ) 640 ) 641 decls; 642 in 643 if length optionDecls == length decls then 644 let opt = fixupOptionType loc (mergeOptionDecls loc decls); 645 in { 646 matchedOptions = evalOptionValue loc opt defns'; 647 unmatchedDefns = []; 648 } 649 else if optionDecls != [] then 650 if all (x: x.options.type.name or null == "submodule") optionDecls 651 # Raw options can only be merged into submodules. Merging into 652 # attrsets might be nice, but ambiguous. Suppose we have 653 # attrset as a `attrsOf submodule`. User declares option 654 # attrset.foo.bar, this could mean: 655 # a. option `bar` is only available in `attrset.foo` 656 # b. option `foo.bar` is available in all `attrset.*` 657 # c. reject and require "<name>" as a reminder that it behaves like (b). 658 # d. magically combine (a) and (c). 659 # All of the above are merely syntax sugar though. 660 then 661 let opt = fixupOptionType loc (mergeOptionDecls loc (map optionTreeToOption decls)); 662 in { 663 matchedOptions = evalOptionValue loc opt defns'; 664 unmatchedDefns = []; 665 } 666 else 667 let 668 nonOptions = filter (m: !isOption m.options) decls; 669 in 670 throw "The option `${showOption loc}' in module `${(lib.head optionDecls)._file}' would be a parent of the following options, but its type `${(lib.head optionDecls).options.type.description or "<no description>"}' does not support nested options.\n${ 671 showRawDecls loc nonOptions 672 }" 673 else 674 mergeModules' loc decls defns) declsByName; 675 676 matchedOptions = mapAttrs (n: v: v.matchedOptions) resultsByName; 677 678 # an attrset 'name' => list of unmatched definitions for 'name' 679 unmatchedDefnsByName = 680 # Propagate all unmatched definitions from nested option sets 681 mapAttrs (n: v: v.unmatchedDefns) resultsByName 682 # Plus the definitions for the current prefix that don't have a matching option 683 // removeAttrs rawDefinitionsByName (attrNames matchedOptions); 684 in { 685 inherit matchedOptions; 686 687 # Transforms unmatchedDefnsByName into a list of definitions 688 unmatchedDefns = 689 if configs == [] 690 then 691 # When no config values exist, there can be no unmatched config, so 692 # we short circuit and avoid evaluating more _options_ than necessary. 693 [] 694 else 695 concatLists (mapAttrsToList (name: defs: 696 map (def: def // { 697 # Set this so we know when the definition first left unmatched territory 698 prefix = [name] ++ (def.prefix or []); 699 }) defs 700 ) unmatchedDefnsByName); 701 }; 702 703 throwDeclarationTypeError = loc: actualTag: file: 704 let 705 name = lib.strings.escapeNixIdentifier (lib.lists.last loc); 706 path = showOption loc; 707 depth = length loc; 708 709 paragraphs = [ 710 "In module ${file}: expected an option declaration at option path `${path}` but got an attribute set with type ${actualTag}" 711 ] ++ optional (actualTag == "option-type") '' 712 When declaring an option, you must wrap the type in a `mkOption` call. It should look somewhat like: 713 ${comment} 714 ${name} = lib.mkOption { 715 description = ...; 716 type = <the type you wrote for ${name}>; 717 ... 718 }; 719 ''; 720 721 # Ideally we'd know the exact syntax they used, but short of that, 722 # we can only reliably repeat the last. However, we repeat the 723 # full path in a non-misleading way here, in case they overlook 724 # the start of the message. Examples attract attention. 725 comment = optionalString (depth > 1) "\n # ${showOption loc}"; 726 in 727 throw (concatStringsSep "\n\n" paragraphs); 728 729 /* Merge multiple option declarations into a single declaration. In 730 general, there should be only one declaration of each option. 731 The exception is the options attribute, which specifies 732 sub-options. These can be specified multiple times to allow one 733 module to add sub-options to an option declared somewhere else 734 (e.g. multiple modules define sub-options for fileSystems). 735 736 'loc' is the list of attribute names where the option is located. 737 738 'opts' is a list of modules. Each module has an options attribute which 739 correspond to the definition of 'loc' in 'opt.file'. */ 740 mergeOptionDecls = 741 loc: opts: 742 foldl' (res: opt: 743 let t = res.type; 744 t' = opt.options.type; 745 mergedType = t.typeMerge t'.functor; 746 typesMergeable = mergedType != null; 747 typeSet = if (bothHave "type") && typesMergeable 748 then { type = mergedType; } 749 else {}; 750 bothHave = k: opt.options ? ${k} && res ? ${k}; 751 in 752 if bothHave "default" || 753 bothHave "example" || 754 bothHave "description" || 755 bothHave "apply" || 756 (bothHave "type" && (! typesMergeable)) 757 then 758 throw "The option `${showOption loc}' in `${opt._file}' is already declared in ${showFiles res.declarations}." 759 else 760 let 761 getSubModules = opt.options.type.getSubModules or null; 762 submodules = 763 if getSubModules != null then map (setDefaultModuleLocation opt._file) getSubModules ++ res.options 764 else res.options; 765 in opt.options // res // 766 { declarations = res.declarations ++ [opt._file]; 767 # In the case of modules that are generated dynamically, we won't 768 # have exact declaration lines; fall back to just the file being 769 # evaluated. 770 declarationPositions = res.declarationPositions 771 ++ (if opt.pos != null 772 then [opt.pos] 773 else [{ file = opt._file; line = null; column = null; }]); 774 options = submodules; 775 } // typeSet 776 ) { inherit loc; declarations = []; declarationPositions = []; options = []; } opts; 777 778 /* Merge all the definitions of an option to produce the final 779 config value. */ 780 evalOptionValue = loc: opt: defs: 781 let 782 # Add in the default value for this option, if any. 783 defs' = 784 (optional (opt ? default) 785 { file = head opt.declarations; value = mkOptionDefault opt.default; }) ++ defs; 786 787 # Handle properties, check types, and merge everything together. 788 res = 789 if opt.readOnly or false && length defs' > 1 then 790 let 791 # For a better error message, evaluate all readOnly definitions as 792 # if they were the only definition. 793 separateDefs = map (def: def // { 794 value = (mergeDefinitions loc opt.type [ def ]).mergedValue; 795 }) defs'; 796 in throw "The option `${showOption loc}' is read-only, but it's set multiple times. Definition values:${showDefs separateDefs}" 797 else 798 mergeDefinitions loc opt.type defs'; 799 800 # Apply the 'apply' function to the merged value. This allows options to 801 # yield a value computed from the definitions 802 value = if opt ? apply then opt.apply res.mergedValue else res.mergedValue; 803 804 warnDeprecation = 805 warnIf (opt.type.deprecationMessage != null) 806 "The type `types.${opt.type.name}' of option `${showOption loc}' defined in ${showFiles opt.declarations} is deprecated. ${opt.type.deprecationMessage}"; 807 808 in warnDeprecation opt // 809 { value = builtins.addErrorContext "while evaluating the option `${showOption loc}':" value; 810 inherit (res.defsFinal') highestPrio; 811 definitions = map (def: def.value) res.defsFinal; 812 files = map (def: def.file) res.defsFinal; 813 definitionsWithLocations = res.defsFinal; 814 inherit (res) isDefined; 815 # This allows options to be correctly displayed using `${options.path.to.it}` 816 __toString = _: showOption loc; 817 }; 818 819 # Merge definitions of a value of a given type. 820 mergeDefinitions = loc: type: defs: rec { 821 defsFinal' = 822 let 823 # Process mkMerge and mkIf properties. 824 defs' = concatMap (m: 825 map (value: { inherit (m) file; inherit value; }) (builtins.addErrorContext "while evaluating definitions from `${m.file}':" (dischargeProperties m.value)) 826 ) defs; 827 828 # Process mkOverride properties. 829 defs'' = filterOverrides' defs'; 830 831 # Sort mkOrder properties. 832 defs''' = 833 # Avoid sorting if we don't have to. 834 if any (def: def.value._type or "" == "order") defs''.values 835 then sortProperties defs''.values 836 else defs''.values; 837 in { 838 values = defs'''; 839 inherit (defs'') highestPrio; 840 }; 841 defsFinal = defsFinal'.values; 842 843 # Type-check the remaining definitions, and merge them. Or throw if no definitions. 844 mergedValue = 845 if isDefined then 846 if all (def: type.check def.value) defsFinal then type.merge loc defsFinal 847 else let allInvalid = filter (def: ! type.check def.value) defsFinal; 848 in throw "A definition for option `${showOption loc}' is not of type `${type.description}'. Definition values:${showDefs allInvalid}" 849 else 850 # (nixos-option detects this specific error message and gives it special 851 # handling. If changed here, please change it there too.) 852 throw "The option `${showOption loc}' is used but not defined."; 853 854 isDefined = defsFinal != []; 855 856 optionalValue = 857 if isDefined then { value = mergedValue; } 858 else {}; 859 }; 860 861 /* Given a config set, expand mkMerge properties, and push down the 862 other properties into the children. The result is a list of 863 config sets that do not have properties at top-level. For 864 example, 865 866 mkMerge [ { boot = set1; } (mkIf cond { boot = set2; services = set3; }) ] 867 868 is transformed into 869 870 [ { boot = set1; } { boot = mkIf cond set2; services = mkIf cond set3; } ]. 871 872 This transform is the critical step that allows mkIf conditions 873 to refer to the full configuration without creating an infinite 874 recursion. 875 */ 876 pushDownProperties = cfg: 877 if cfg._type or "" == "merge" then 878 concatMap pushDownProperties cfg.contents 879 else if cfg._type or "" == "if" then 880 map (mapAttrs (n: v: mkIf cfg.condition v)) (pushDownProperties cfg.content) 881 else if cfg._type or "" == "override" then 882 map (mapAttrs (n: v: mkOverride cfg.priority v)) (pushDownProperties cfg.content) 883 else # FIXME: handle mkOrder? 884 [ cfg ]; 885 886 /* Given a config value, expand mkMerge properties, and discharge 887 any mkIf conditions. That is, this is the place where mkIf 888 conditions are actually evaluated. The result is a list of 889 config values. For example, mkIf false x yields [], 890 mkIf true x yields [x], and 891 892 mkMerge [ 1 (mkIf true 2) (mkIf true (mkIf false 3)) ] 893 894 yields [ 1 2 ]. 895 */ 896 dischargeProperties = def: 897 if def._type or "" == "merge" then 898 concatMap dischargeProperties def.contents 899 else if def._type or "" == "if" then 900 if isBool def.condition then 901 if def.condition then 902 dischargeProperties def.content 903 else 904 [ ] 905 else 906 throw "mkIf called with a non-Boolean condition" 907 else 908 [ def ]; 909 910 /* Given a list of config values, process the mkOverride properties, 911 that is, return the values that have the highest (that is, 912 numerically lowest) priority, and strip the mkOverride 913 properties. For example, 914 915 [ { file = "/1"; value = mkOverride 10 "a"; } 916 { file = "/2"; value = mkOverride 20 "b"; } 917 { file = "/3"; value = "z"; } 918 { file = "/4"; value = mkOverride 10 "d"; } 919 ] 920 921 yields 922 923 [ { file = "/1"; value = "a"; } 924 { file = "/4"; value = "d"; } 925 ] 926 927 Note that "z" has the default priority 100. 928 */ 929 filterOverrides = defs: (filterOverrides' defs).values; 930 931 filterOverrides' = defs: 932 let 933 getPrio = def: if def.value._type or "" == "override" then def.value.priority else defaultOverridePriority; 934 highestPrio = foldl' (prio: def: min (getPrio def) prio) 9999 defs; 935 strip = def: if def.value._type or "" == "override" then def // { value = def.value.content; } else def; 936 in { 937 values = concatMap (def: if getPrio def == highestPrio then [(strip def)] else []) defs; 938 inherit highestPrio; 939 }; 940 941 /* Sort a list of properties. The sort priority of a property is 942 defaultOrderPriority by default, but can be overridden by wrapping the property 943 using mkOrder. */ 944 sortProperties = defs: 945 let 946 strip = def: 947 if def.value._type or "" == "order" 948 then def // { value = def.value.content; inherit (def.value) priority; } 949 else def; 950 defs' = map strip defs; 951 compare = a: b: (a.priority or defaultOrderPriority) < (b.priority or defaultOrderPriority); 952 in sort compare defs'; 953 954 # This calls substSubModules, whose entire purpose is only to ensure that 955 # option declarations in submodules have accurate position information. 956 # TODO: Merge this into mergeOptionDecls 957 fixupOptionType = loc: opt: 958 if opt.type.getSubModules or null == null 959 then opt // { type = opt.type or types.unspecified; } 960 else opt // { type = opt.type.substSubModules opt.options; options = []; }; 961 962 963 /* 964 Merge an option's definitions in a way that preserves the priority of the 965 individual attributes in the option value. 966 967 This does not account for all option semantics, such as readOnly. 968 969 Type: 970 option -> attrsOf { highestPrio, value } 971 */ 972 mergeAttrDefinitionsWithPrio = opt: 973 let 974 defsByAttr = 975 lib.zipAttrs ( 976 lib.concatLists ( 977 lib.concatMap 978 ({ value, ... }@def: 979 map 980 (lib.mapAttrsToList (k: value: { ${k} = def // { inherit value; }; })) 981 (pushDownProperties value) 982 ) 983 opt.definitionsWithLocations 984 ) 985 ); 986 in 987 assert opt.type.name == "attrsOf" || opt.type.name == "lazyAttrsOf"; 988 lib.mapAttrs 989 (k: v: 990 let merging = lib.mergeDefinitions (opt.loc ++ [k]) opt.type.nestedTypes.elemType v; 991 in { 992 value = merging.mergedValue; 993 inherit (merging.defsFinal') highestPrio; 994 }) 995 defsByAttr; 996 997 /* Properties. */ 998 999 mkIf = condition: content: 1000 { _type = "if"; 1001 inherit condition content; 1002 }; 1003 1004 mkAssert = assertion: message: content: 1005 mkIf 1006 (if assertion then true else throw "\nFailed assertion: ${message}") 1007 content; 1008 1009 mkMerge = contents: 1010 { _type = "merge"; 1011 inherit contents; 1012 }; 1013 1014 mkOverride = priority: content: 1015 { _type = "override"; 1016 inherit priority content; 1017 }; 1018 1019 mkOptionDefault = mkOverride 1500; # priority of option defaults 1020 mkDefault = mkOverride 1000; # used in config sections of non-user modules to set a default 1021 defaultOverridePriority = 100; 1022 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 1023 mkForce = mkOverride 50; 1024 mkVMOverride = mkOverride 10; # used by ‘nixos-rebuild build-vm’ 1025 1026 defaultPriority = lib.warnIf (lib.isInOldestRelease 2305) "lib.modules.defaultPriority is deprecated, please use lib.modules.defaultOverridePriority instead." defaultOverridePriority; 1027 1028 mkFixStrictness = lib.warn "lib.mkFixStrictness has no effect and will be removed. It returns its argument unmodified, so you can just remove any calls." id; 1029 1030 mkOrder = priority: content: 1031 { _type = "order"; 1032 inherit priority content; 1033 }; 1034 1035 mkBefore = mkOrder 500; 1036 defaultOrderPriority = 1000; 1037 mkAfter = mkOrder 1500; 1038 1039 # Convenient property used to transfer all definitions and their 1040 # properties from one option to another. This property is useful for 1041 # renaming options, and also for including properties from another module 1042 # system, including sub-modules. 1043 # 1044 # { config, options, ... }: 1045 # 1046 # { 1047 # # 'bar' might not always be defined in the current module-set. 1048 # config.foo.enable = mkAliasDefinitions (options.bar.enable or {}); 1049 # 1050 # # 'barbaz' has to be defined in the current module-set. 1051 # config.foobar.paths = mkAliasDefinitions options.barbaz.paths; 1052 # } 1053 # 1054 # Note, this is different than taking the value of the option and using it 1055 # as a definition, as the new definition will not keep the mkOverride / 1056 # mkDefault properties of the previous option. 1057 # 1058 mkAliasDefinitions = mkAliasAndWrapDefinitions id; 1059 mkAliasAndWrapDefinitions = wrap: option: 1060 mkAliasIfDef option (wrap (mkMerge option.definitions)); 1061 1062 # Similar to mkAliasAndWrapDefinitions but copies over the priority from the 1063 # option as well. 1064 # 1065 # If a priority is not set, it assumes a priority of defaultOverridePriority. 1066 mkAliasAndWrapDefsWithPriority = wrap: option: 1067 let 1068 prio = option.highestPrio or defaultOverridePriority; 1069 defsWithPrio = map (mkOverride prio) option.definitions; 1070 in mkAliasIfDef option (wrap (mkMerge defsWithPrio)); 1071 1072 mkAliasIfDef = option: 1073 mkIf (isOption option && option.isDefined); 1074 1075 /* Compatibility. */ 1076 fixMergeModules = modules: args: evalModules { inherit modules args; check = false; }; 1077 1078 1079 /* Return a module that causes a warning to be shown if the 1080 specified option is defined. For example, 1081 1082 mkRemovedOptionModule [ "boot" "loader" "grub" "bootDevice" ] "<replacement instructions>" 1083 1084 causes a assertion if the user defines boot.loader.grub.bootDevice. 1085 1086 replacementInstructions is a string that provides instructions on 1087 how to achieve the same functionality without the removed option, 1088 or alternatively a reasoning why the functionality is not needed. 1089 replacementInstructions SHOULD be provided! 1090 */ 1091 mkRemovedOptionModule = optionName: replacementInstructions: 1092 { options, ... }: 1093 { options = setAttrByPath optionName (mkOption { 1094 visible = false; 1095 apply = x: throw "The option `${showOption optionName}' can no longer be used since it's been removed. ${replacementInstructions}"; 1096 }); 1097 config.assertions = 1098 let opt = getAttrFromPath optionName options; in [{ 1099 assertion = !opt.isDefined; 1100 message = '' 1101 The option definition `${showOption optionName}' in ${showFiles opt.files} no longer has any effect; please remove it. 1102 ${replacementInstructions} 1103 ''; 1104 }]; 1105 }; 1106 1107 /* Return a module that causes a warning to be shown if the 1108 specified "from" option is defined; the defined value is however 1109 forwarded to the "to" option. This can be used to rename options 1110 while providing backward compatibility. For example, 1111 1112 mkRenamedOptionModule [ "boot" "copyKernels" ] [ "boot" "loader" "grub" "copyKernels" ] 1113 1114 forwards any definitions of boot.copyKernels to 1115 boot.loader.grub.copyKernels while printing a warning. 1116 1117 This also copies over the priority from the aliased option to the 1118 non-aliased option. 1119 */ 1120 mkRenamedOptionModule = from: to: doRename { 1121 inherit from to; 1122 visible = false; 1123 warn = true; 1124 use = builtins.trace "Obsolete option `${showOption from}' is used. It was renamed to `${showOption to}'."; 1125 }; 1126 1127 mkRenamedOptionModuleWith = { 1128 /* Old option path as list of strings. */ 1129 from, 1130 /* New option path as list of strings. */ 1131 to, 1132 1133 /* 1134 Release number of the first release that contains the rename, ignoring backports. 1135 Set it to the upcoming release, matching the nixpkgs/.version file. 1136 */ 1137 sinceRelease, 1138 1139 }: doRename { 1140 inherit from to; 1141 visible = false; 1142 warn = lib.isInOldestRelease sinceRelease; 1143 use = lib.warnIf (lib.isInOldestRelease sinceRelease) 1144 "Obsolete option `${showOption from}' is used. It was renamed to `${showOption to}'."; 1145 }; 1146 1147 /* Return a module that causes a warning to be shown if any of the "from" 1148 option is defined; the defined values can be used in the "mergeFn" to set 1149 the "to" value. 1150 This function can be used to merge multiple options into one that has a 1151 different type. 1152 1153 "mergeFn" takes the module "config" as a parameter and must return a value 1154 of "to" option type. 1155 1156 mkMergedOptionModule 1157 [ [ "a" "b" "c" ] 1158 [ "d" "e" "f" ] ] 1159 [ "x" "y" "z" ] 1160 (config: 1161 let value = p: getAttrFromPath p config; 1162 in 1163 if (value [ "a" "b" "c" ]) == true then "foo" 1164 else if (value [ "d" "e" "f" ]) == true then "bar" 1165 else "baz") 1166 1167 - options.a.b.c is a removed boolean option 1168 - options.d.e.f is a removed boolean option 1169 - options.x.y.z is a new str option that combines a.b.c and d.e.f 1170 functionality 1171 1172 This show a warning if any a.b.c or d.e.f is set, and set the value of 1173 x.y.z to the result of the merge function 1174 */ 1175 mkMergedOptionModule = from: to: mergeFn: 1176 { config, options, ... }: 1177 { 1178 options = foldl' recursiveUpdate {} (map (path: setAttrByPath path (mkOption { 1179 visible = false; 1180 # To use the value in mergeFn without triggering errors 1181 default = "_mkMergedOptionModule"; 1182 })) from); 1183 1184 config = { 1185 warnings = filter (x: x != "") (map (f: 1186 let val = getAttrFromPath f config; 1187 opt = getAttrFromPath f options; 1188 in 1189 optionalString 1190 (val != "_mkMergedOptionModule") 1191 "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." 1192 ) from); 1193 } // setAttrByPath to (mkMerge 1194 (optional 1195 (any (f: (getAttrFromPath f config) != "_mkMergedOptionModule") from) 1196 (mergeFn config))); 1197 }; 1198 1199 /* Single "from" version of mkMergedOptionModule. 1200 Return a module that causes a warning to be shown if the "from" option is 1201 defined; the defined value can be used in the "mergeFn" to set the "to" 1202 value. 1203 This function can be used to change an option into another that has a 1204 different type. 1205 1206 "mergeFn" takes the module "config" as a parameter and must return a value of 1207 "to" option type. 1208 1209 mkChangedOptionModule [ "a" "b" "c" ] [ "x" "y" "z" ] 1210 (config: 1211 let value = getAttrFromPath [ "a" "b" "c" ] config; 1212 in 1213 if value > 100 then "high" 1214 else "normal") 1215 1216 - options.a.b.c is a removed int option 1217 - options.x.y.z is a new str option that supersedes a.b.c 1218 1219 This show a warning if a.b.c is set, and set the value of x.y.z to the 1220 result of the change function 1221 */ 1222 mkChangedOptionModule = from: to: changeFn: 1223 mkMergedOptionModule [ from ] to changeFn; 1224 1225 /* Like mkRenamedOptionModule, but doesn't show a warning. */ 1226 mkAliasOptionModule = from: to: doRename { 1227 inherit from to; 1228 visible = true; 1229 warn = false; 1230 use = id; 1231 }; 1232 1233 /* Transitional version of mkAliasOptionModule that uses MD docs. 1234 1235 This function is no longer necessary and merely an alias of `mkAliasOptionModule`. 1236 */ 1237 mkAliasOptionModuleMD = mkAliasOptionModule; 1238 1239 /* mkDerivedConfig : Option a -> (a -> Definition b) -> Definition b 1240 1241 Create config definitions with the same priority as the definition of another option. 1242 This should be used for option definitions where one option sets the value of another as a convenience. 1243 For instance a config file could be set with a `text` or `source` option, where text translates to a `source` 1244 value using `mkDerivedConfig options.text (pkgs.writeText "filename.conf")`. 1245 1246 It takes care of setting the right priority using `mkOverride`. 1247 */ 1248 # TODO: make the module system error message include information about `opt` in 1249 # error messages about conflicts. E.g. introduce a variation of `mkOverride` which 1250 # adds extra location context to the definition object. This will allow context to be added 1251 # to all messages that report option locations "this value was derived from <full option name> 1252 # which was defined in <locations>". It can provide a trace of options that contributed 1253 # to definitions. 1254 mkDerivedConfig = opt: f: 1255 mkOverride 1256 (opt.highestPrio or defaultOverridePriority) 1257 (f opt.value); 1258 1259 /* 1260 Return a module that help declares an option that has been renamed. 1261 When a value is defined for the old option, it is forwarded to the `to` option. 1262 */ 1263 doRename = { 1264 # List of strings representing the attribute path of the old option. 1265 from, 1266 # List of strings representing the attribute path of the new option. 1267 to, 1268 # Boolean, whether the old option is to be included in documentation. 1269 visible, 1270 # Whether to warn when a value is defined for the old option. 1271 # NOTE: This requires the NixOS assertions module to be imported, so 1272 # - this generally does not work in submodules 1273 # - this may or may not work outside NixOS 1274 warn, 1275 # A function that is applied to the option value, to form the value 1276 # of the old `from` option. 1277 # 1278 # For example, the identity function can be passed, to return the option value unchanged. 1279 # ```nix 1280 # use = x: x; 1281 # ``` 1282 # 1283 # To add a warning, you can pass the partially applied `warn` function. 1284 # ```nix 1285 # use = lib.warn "Obsolete option `${opt.old}' is used. Use `${opt.to}' instead."; 1286 # ``` 1287 use, 1288 # Legacy option, enabled by default: whether to preserve the priority of definitions in `old`. 1289 withPriority ? true, 1290 # A boolean that defines the `mkIf` condition for `to`. 1291 # If the condition evaluates to `true`, and the `to` path points into an 1292 # `attrsOf (submodule ...)`, then `doRename` would cause an empty module to 1293 # be created, even if the `from` option is undefined. 1294 # By setting this to an expression that may return `false`, you can inhibit 1295 # this undesired behavior. 1296 # 1297 # Example: 1298 # 1299 # ```nix 1300 # { config, lib, ... }: 1301 # let 1302 # inherit (lib) mkOption mkEnableOption types doRename; 1303 # in 1304 # { 1305 # options = { 1306 # 1307 # # Old service 1308 # services.foo.enable = mkEnableOption "foo"; 1309 # 1310 # # New multi-instance service 1311 # services.foos = mkOption { 1312 # type = types.attrsOf (types.submodule …); 1313 # }; 1314 # }; 1315 # imports = [ 1316 # (doRename { 1317 # from = [ "services" "foo" "bar" ]; 1318 # to = [ "services" "foos" "" "bar" ]; 1319 # visible = true; 1320 # warn = false; 1321 # use = x: x; 1322 # withPriority = true; 1323 # # Only define services.foos."" if needed. (It's not just about `bar`) 1324 # condition = config.services.foo.enable; 1325 # }) 1326 # ]; 1327 # } 1328 # ``` 1329 condition ? true 1330 }: 1331 { config, options, ... }: 1332 let 1333 fromOpt = getAttrFromPath from options; 1334 toOf = attrByPath to 1335 (abort "Renaming error: option `${showOption to}' does not exist."); 1336 toType = let opt = attrByPath to {} options; in opt.type or (types.submodule {}); 1337 in 1338 { 1339 options = setAttrByPath from (mkOption { 1340 inherit visible; 1341 description = "Alias of {option}`${showOption to}`."; 1342 apply = x: use (toOf config); 1343 } // optionalAttrs (toType != null) { 1344 type = toType; 1345 }); 1346 config = mkIf condition (mkMerge [ 1347 (optionalAttrs (options ? warnings) { 1348 warnings = optional (warn && fromOpt.isDefined) 1349 "The option `${showOption from}' defined in ${showFiles fromOpt.files} has been renamed to `${showOption to}'."; 1350 }) 1351 (if withPriority 1352 then mkAliasAndWrapDefsWithPriority (setAttrByPath to) fromOpt 1353 else mkAliasAndWrapDefinitions (setAttrByPath to) fromOpt) 1354 ]); 1355 }; 1356 1357 /* Use this function to import a JSON file as NixOS configuration. 1358 1359 modules.importJSON :: path -> attrs 1360 */ 1361 importJSON = file: { 1362 _file = file; 1363 config = lib.importJSON file; 1364 }; 1365 1366 /* Use this function to import a TOML file as NixOS configuration. 1367 1368 modules.importTOML :: path -> attrs 1369 */ 1370 importTOML = file: { 1371 _file = file; 1372 config = lib.importTOML file; 1373 }; 1374 1375 private = lib.mapAttrs 1376 (k: lib.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/.") 1377 { 1378 inherit 1379 applyModuleArgsIfFunction 1380 dischargeProperties 1381 mergeModules 1382 mergeModules' 1383 pushDownProperties 1384 unifyModuleSyntax 1385 ; 1386 collectModules = collectModules null; 1387 }; 1388 1389in 1390private // 1391{ 1392 # NOTE: not all of these functions are necessarily public interfaces; some 1393 # are just needed by types.nix, but are not meant to be consumed 1394 # externally. 1395 inherit 1396 defaultOrderPriority 1397 defaultOverridePriority 1398 defaultPriority 1399 doRename 1400 evalModules 1401 evalOptionValue # for use by lib.types 1402 filterOverrides 1403 filterOverrides' 1404 fixMergeModules 1405 fixupOptionType # should be private? 1406 importJSON 1407 importTOML 1408 mergeDefinitions 1409 mergeAttrDefinitionsWithPrio 1410 mergeOptionDecls # should be private? 1411 mkAfter 1412 mkAliasAndWrapDefinitions 1413 mkAliasAndWrapDefsWithPriority 1414 mkAliasDefinitions 1415 mkAliasIfDef 1416 mkAliasOptionModule 1417 mkAliasOptionModuleMD 1418 mkAssert 1419 mkBefore 1420 mkChangedOptionModule 1421 mkDefault 1422 mkDerivedConfig 1423 mkFixStrictness 1424 mkForce 1425 mkIf 1426 mkImageMediaOverride 1427 mkMerge 1428 mkMergedOptionModule 1429 mkOptionDefault 1430 mkOrder 1431 mkOverride 1432 mkRemovedOptionModule 1433 mkRenamedOptionModule 1434 mkRenamedOptionModuleWith 1435 mkVMOverride 1436 setDefaultModuleLocation 1437 sortProperties; 1438}