python310Packages.pydeconz: 104 -> 105
[NixPkgs.git] / lib / modules.nix
blob204a2cc1ac12a757060acbafcbe2bba8159b2140
1 { lib }:
3 let
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     ;
49   showDeclPrefix = loc: decl: prefix:
50     " - option(s) with prefix `${showOption (loc ++ [prefix])}' in module `${decl._file}'";
51   showRawDecls = loc: decls:
52     concatStringsSep "\n"
53       (sort (a: b: a < b)
54         (concatMap
55           (decl: map
56             (showDeclPrefix loc decl)
57             (attrNames decl.options)
58           )
59           decls
60       ));
64 rec {
66   /*
67     Evaluate a set of modules.  The result is a set with the attributes:
69       ‘options’: The nested set of all option declarations,
71       ‘config’: The nested set of all option values.
73       ‘type’: A module system type representing the module set as a submodule,
74             to be extended by configuration from the containing module set.
76             This is also available as the module argument ‘moduleType’.
78       ‘extendModules’: A function similar to ‘evalModules’ but building on top
79             of the module set. Its arguments, ‘modules’ and ‘specialArgs’ are
80             added to the existing values.
82             Using ‘extendModules’ a few times has no performance impact as long
83             as you only reference the final ‘options’ and ‘config’.
84             If you do reference multiple ‘config’ (or ‘options’) from before and
85             after ‘extendModules’, performance is the same as with multiple
86             ‘evalModules’ invocations, because the new modules' ability to
87             override existing configuration fundamentally requires a new
88             fixpoint to be constructed.
90             This is also available as a module argument.
92       ‘_module’: A portion of the configuration tree which is elided from
93             ‘config’. It contains some values that are mostly internal to the
94             module system implementation.
96      !!! Please think twice before adding to this argument list! The more
97      that is specified here instead of in the modules themselves the harder
98      it is to transparently move a set of modules to be a submodule of another
99      config (as the proper arguments need to be replicated at each call to
100      evalModules) and the less declarative the module set is. */
101   evalModules = evalModulesArgs@
102                 { modules
103                 , prefix ? []
104                 , # This should only be used for special arguments that need to be evaluated
105                   # when resolving module structure (like in imports). For everything else,
106                   # there's _module.args. If specialArgs.modulesPath is defined it will be
107                   # used as the base path for disabledModules.
108                   specialArgs ? {}
109                 , # This would be remove in the future, Prefer _module.args option instead.
110                   args ? {}
111                 , # This would be remove in the future, Prefer _module.check option instead.
112                   check ? true
113                 }:
114     let
115       withWarnings = x:
116         lib.warnIf (evalModulesArgs?args) "The args argument to evalModules is deprecated. Please set config._module.args instead."
117         lib.warnIf (evalModulesArgs?check) "The check argument to evalModules is deprecated. Please set config._module.check instead."
118         x;
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;
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";
143         key = _file;
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             ${if prefix == []
158               then null  # unset => visible
159               else "internal"} = true;
160             # TODO: Change the type of this option to a submodule with a
161             # freeformType, so that individual arguments can be documented
162             # separately
163             description = lib.mdDoc ''
164               Additional arguments passed to each module in addition to ones
165               like `lib`, `config`,
166               and `pkgs`, `modulesPath`.
168               This option is also available to all submodules. Submodules do not
169               inherit args from their parent module, nor do they provide args to
170               their parent module or sibling submodules. The sole exception to
171               this is the argument `name` which is provided by
172               parent modules to a submodule and contains the attribute name
173               the submodule is bound to, or a unique generated name if it is
174               not bound to an attribute.
176               Some arguments are already passed by default, of which the
177               following *cannot* be changed with this option:
178               - {var}`lib`: The nixpkgs library.
179               - {var}`config`: The results of all options after merging the values from all modules together.
180               - {var}`options`: The options declared in all modules.
181               - {var}`specialArgs`: The `specialArgs` argument passed to `evalModules`.
182               - All attributes of {var}`specialArgs`
184                 Whereas option values can generally depend on other option values
185                 thanks to laziness, this does not apply to `imports`, which
186                 must be computed statically before anything else.
188                 For this reason, callers of the module system can provide `specialArgs`
189                 which are available during import resolution.
191                 For NixOS, `specialArgs` includes
192                 {var}`modulesPath`, which allows you to import
193                 extra modules from the nixpkgs package tree without having to
194                 somehow make the module aware of the location of the
195                 `nixpkgs` or NixOS directories.
196                 ```
197                 { modulesPath, ... }: {
198                   imports = [
199                     (modulesPath + "/profiles/minimal.nix")
200                   ];
201                 }
202                 ```
204               For NixOS, the default value for this option includes at least this argument:
205               - {var}`pkgs`: The nixpkgs package set according to
206                 the {option}`nixpkgs.pkgs` option.
207             '';
208           };
210           _module.check = mkOption {
211             type = types.bool;
212             internal = true;
213             default = true;
214             description = lib.mdDoc "Whether to check whether all option definitions have matching declarations.";
215           };
217           _module.freeformType = mkOption {
218             type = types.nullOr types.optionType;
219             internal = true;
220             default = null;
221             description = lib.mdDoc ''
222               If set, merge all definitions that don't have an associated option
223               together using this type. The result then gets combined with the
224               values of all declared options to produce the final `
225               config` value.
227               If this is `null`, definitions without an option
228               will throw an error unless {option}`_module.check` is
229               turned off.
230             '';
231           };
233           _module.specialArgs = mkOption {
234             readOnly = true;
235             internal = true;
236             description = lib.mdDoc ''
237               Externally provided module arguments that can't be modified from
238               within a configuration, but can be used in module imports.
239             '';
240           };
241         };
243         config = {
244           _module.args = {
245             inherit extendModules;
246             moduleType = type;
247           };
248           _module.specialArgs = specialArgs;
249         };
250       };
252       merged =
253         let collected = collectModules
254           (specialArgs.modulesPath or "")
255           (regularModules ++ [ internalModule ])
256           ({ inherit lib options config specialArgs; } // specialArgs);
257         in mergeModules prefix (reverseList collected);
259       options = merged.matchedOptions;
261       config =
262         let
264           # For definitions that have an associated option
265           declaredConfig = mapAttrsRecursiveCond (v: ! isOption v) (_: v: v.value) options;
267           # If freeformType is set, this is for definitions that don't have an associated option
268           freeformConfig =
269             let
270               defs = map (def: {
271                 file = def.file;
272                 value = setAttrByPath def.prefix def.value;
273               }) merged.unmatchedDefns;
274             in if defs == [] then {}
275             else declaredConfig._module.freeformType.merge prefix defs;
277         in if declaredConfig._module.freeformType == null then declaredConfig
278           # Because all definitions that had an associated option ended in
279           # declaredConfig, freeformConfig can only contain the non-option
280           # paths, meaning recursiveUpdate will never override any value
281           else recursiveUpdate freeformConfig declaredConfig;
283       checkUnmatched =
284         if config._module.check && config._module.freeformType == null && merged.unmatchedDefns != [] then
285           let
286             firstDef = head merged.unmatchedDefns;
287             baseMsg = "The option `${showOption (prefix ++ firstDef.prefix)}' does not exist. Definition values:${showDefs [ firstDef ]}";
288           in
289             if attrNames options == [ "_module" ]
290               then
291                 let
292                   optionName = showOption prefix;
293                 in
294                   if optionName == ""
295                     then throw ''
296                       ${baseMsg}
298                       It seems as if you're trying to declare an option by placing it into `config' rather than `options'!
299                     ''
300                   else
301                     throw ''
302                       ${baseMsg}
304                       However there are no options defined in `${showOption prefix}'. Are you sure you've
305                       declared your options properly? This can happen if you e.g. declared your options in `types.submodule'
306                       under `config' rather than `options'.
307                     ''
308             else throw baseMsg
309         else null;
311       checked = builtins.seq checkUnmatched;
313       extendModules = extendArgs@{
314         modules ? [],
315         specialArgs ? {},
316         prefix ? [],
317         }:
318           evalModules (evalModulesArgs // {
319             modules = regularModules ++ modules;
320             specialArgs = evalModulesArgs.specialArgs or {} // specialArgs;
321             prefix = extendArgs.prefix or evalModulesArgs.prefix or [];
322           });
324       type = lib.types.submoduleWith {
325         inherit modules specialArgs;
326       };
328       result = withWarnings {
329         options = checked options;
330         config = checked (removeAttrs config [ "_module" ]);
331         _module = checked (config._module);
332         inherit extendModules type;
333       };
334     in result;
336   # collectModules :: (modulesPath: String) -> (modules: [ Module ]) -> (args: Attrs) -> [ Module ]
337   #
338   # Collects all modules recursively through `import` statements, filtering out
339   # all modules in disabledModules.
340   collectModules = let
342       # Like unifyModuleSyntax, but also imports paths and calls functions if necessary
343       loadModule = args: fallbackFile: fallbackKey: m:
344         if isFunction m || isAttrs m then
345           unifyModuleSyntax fallbackFile fallbackKey (applyModuleArgsIfFunction fallbackKey m args)
346         else if isList m then
347           let defs = [{ file = fallbackFile; value = m; }]; in
348           throw "Module imports can't be nested lists. Perhaps you meant to remove one level of lists? Definitions: ${showDefs defs}"
349         else unifyModuleSyntax (toString m) (toString m) (applyModuleArgsIfFunction (toString m) (import m) args);
351       /*
352       Collects all modules recursively into the form
354         {
355           disabled = [ <list of disabled modules> ];
356           # All modules of the main module list
357           modules = [
358             {
359               key = <key1>;
360               module = <module for key1>;
361               # All modules imported by the module for key1
362               modules = [
363                 {
364                   key = <key1-1>;
365                   module = <module for key1-1>;
366                   # All modules imported by the module for key1-1
367                   modules = [ ... ];
368                 }
369                 ...
370               ];
371             }
372             ...
373           ];
374         }
375       */
376       collectStructuredModules =
377         let
378           collectResults = modules: {
379             disabled = concatLists (catAttrs "disabled" modules);
380             inherit modules;
381           };
382         in parentFile: parentKey: initialModules: args: collectResults (imap1 (n: x:
383           let
384             module = loadModule args parentFile "${parentKey}:anon-${toString n}" x;
385             collectedImports = collectStructuredModules module._file module.key module.imports args;
386           in {
387             key = module.key;
388             module = module;
389             modules = collectedImports.modules;
390             disabled = module.disabledModules ++ collectedImports.disabled;
391           }) initialModules);
393       # filterModules :: String -> { disabled, modules } -> [ Module ]
394       #
395       # Filters a structure as emitted by collectStructuredModules by removing all disabled
396       # modules recursively. It returns the final list of unique-by-key modules
397       filterModules = modulesPath: { disabled, modules }:
398         let
399           moduleKey = m: if isString m && (builtins.substring 0 1 m != "/")
400             then toString modulesPath + "/" + m
401             else toString m;
402           disabledKeys = map moduleKey disabled;
403           keyFilter = filter (attrs: ! elem attrs.key disabledKeys);
404         in map (attrs: attrs.module) (builtins.genericClosure {
405           startSet = keyFilter modules;
406           operator = attrs: keyFilter attrs.modules;
407         });
409     in modulesPath: initialModules: args:
410       filterModules modulesPath (collectStructuredModules unknownModule "" initialModules args);
412   /* Wrap a module with a default location for reporting errors. */
413   setDefaultModuleLocation = file: m:
414     { _file = file; imports = [ m ]; };
416   /* Massage a module into canonical form, that is, a set consisting
417      of ‘options’, ‘config’ and ‘imports’ attributes. */
418   unifyModuleSyntax = file: key: m:
419     let
420       addMeta = config: if m ? meta
421         then mkMerge [ config { meta = m.meta; } ]
422         else config;
423       addFreeformType = config: if m ? freeformType
424         then mkMerge [ config { _module.freeformType = m.freeformType; } ]
425         else config;
426     in
427     if m ? config || m ? options then
428       let badAttrs = removeAttrs m ["_file" "key" "disabledModules" "imports" "options" "config" "meta" "freeformType"]; in
429       if badAttrs != {} then
430         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."
431       else
432         { _file = toString m._file or file;
433           key = toString m.key or key;
434           disabledModules = m.disabledModules or [];
435           imports = m.imports or [];
436           options = m.options or {};
437           config = addFreeformType (addMeta (m.config or {}));
438         }
439     else
440       # shorthand syntax
441       lib.throwIfNot (isAttrs m) "module ${file} (${key}) does not look like a module."
442       { _file = toString m._file or file;
443         key = toString m.key or key;
444         disabledModules = m.disabledModules or [];
445         imports = m.require or [] ++ m.imports or [];
446         options = {};
447         config = addFreeformType (removeAttrs m ["_file" "key" "disabledModules" "require" "imports" "freeformType"]);
448       };
450   applyModuleArgsIfFunction = key: f: args@{ config, options, lib, ... }: if isFunction f then
451     let
452       # Module arguments are resolved in a strict manner when attribute set
453       # deconstruction is used.  As the arguments are now defined with the
454       # config._module.args option, the strictness used on the attribute
455       # set argument would cause an infinite loop, if the result of the
456       # option is given as argument.
457       #
458       # To work-around the strictness issue on the deconstruction of the
459       # attributes set argument, we create a new attribute set which is
460       # constructed to satisfy the expected set of attributes.  Thus calling
461       # a module will resolve strictly the attributes used as argument but
462       # not their values.  The values are forwarding the result of the
463       # evaluation of the option.
464       context = name: ''while evaluating the module argument `${name}' in "${key}":'';
465       extraArgs = builtins.mapAttrs (name: _:
466         builtins.addErrorContext (context name)
467           (args.${name} or config._module.args.${name})
468       ) (lib.functionArgs f);
470       # Note: we append in the opposite order such that we can add an error
471       # context on the explicited arguments of "args" too. This update
472       # operator is used to make the "args@{ ... }: with args.lib;" notation
473       # works.
474     in f (args // extraArgs)
475   else
476     f;
478   /* Merge a list of modules.  This will recurse over the option
479      declarations in all modules, combining them into a single set.
480      At the same time, for each option declaration, it will merge the
481      corresponding option definitions in all machines, returning them
482      in the ‘value’ attribute of each option.
484      This returns a set like
485        {
486          # A recursive set of options along with their final values
487          matchedOptions = {
488            foo = { _type = "option"; value = "option value of foo"; ... };
489            bar.baz = { _type = "option"; value = "option value of bar.baz"; ... };
490            ...
491          };
492          # A list of definitions that weren't matched by any option
493          unmatchedDefns = [
494            { file = "file.nix"; prefix = [ "qux" ]; value = "qux"; }
495            ...
496          ];
497        }
498   */
499   mergeModules = prefix: modules:
500     mergeModules' prefix modules
501       (concatMap (m: map (config: { file = m._file; inherit config; }) (pushDownProperties m.config)) modules);
503   mergeModules' = prefix: options: configs:
504     let
505      /* byName is like foldAttrs, but will look for attributes to merge in the
506         specified attribute name.
508         byName "foo" (module: value: ["module.hidden=${module.hidden},value=${value}"])
509         [
510           {
511             hidden="baz";
512             foo={qux="bar"; gla="flop";};
513           }
514           {
515             hidden="fli";
516             foo={qux="gne"; gli="flip";};
517           }
518         ]
519         ===>
520         {
521           gla = [ "module.hidden=baz,value=flop" ];
522           gli = [ "module.hidden=fli,value=flip" ];
523           qux = [ "module.hidden=baz,value=bar" "module.hidden=fli,value=gne" ];
524         }
525       */
526       byName = attr: f: modules:
527         zipAttrsWith (n: concatLists)
528           (map (module: let subtree = module.${attr}; in
529               if !(builtins.isAttrs subtree) then
530                 throw ''
531                   You're trying to declare a value of type `${builtins.typeOf subtree}'
532                   rather than an attribute-set for the option
533                   `${builtins.concatStringsSep "." prefix}'!
535                   This usually happens if `${builtins.concatStringsSep "." prefix}' has option
536                   definitions inside that are not matched. Please check how to properly define
537                   this option by e.g. referring to `man 5 configuration.nix'!
538                 ''
539               else
540                 mapAttrs (n: f module) subtree
541               ) modules);
542       # an attrset 'name' => list of submodules that declare ‘name’.
543       declsByName = byName "options" (module: option:
544           [{ inherit (module) _file; options = option; }]
545         ) options;
546       # an attrset 'name' => list of submodules that define ‘name’.
547       defnsByName = byName "config" (module: value:
548           map (config: { inherit (module) file; inherit config; }) (pushDownProperties value)
549         ) configs;
550       # extract the definitions for each loc
551       defnsByName' = byName "config" (module: value:
552           [{ inherit (module) file; inherit value; }]
553         ) configs;
555       # Convert an option tree decl to a submodule option decl
556       optionTreeToOption = decl:
557         if isOption decl.options
558         then decl
559         else decl // {
560             options = mkOption {
561               type = types.submoduleWith {
562                 modules = [ { options = decl.options; } ];
563                 # `null` is not intended for use by modules. It is an internal
564                 # value that means "whatever the user has declared elsewhere".
565                 # This might become obsolete with https://github.com/NixOS/nixpkgs/issues/162398
566                 shorthandOnlyDefinesConfig = null;
567               };
568             };
569           };
571       resultsByName = mapAttrs (name: decls:
572         # We're descending into attribute ‘name’.
573         let
574           loc = prefix ++ [name];
575           defns = defnsByName.${name} or [];
576           defns' = defnsByName'.${name} or [];
577           optionDecls = filter (m: isOption m.options) decls;
578         in
579           if length optionDecls == length decls then
580             let opt = fixupOptionType loc (mergeOptionDecls loc decls);
581             in {
582               matchedOptions = evalOptionValue loc opt defns';
583               unmatchedDefns = [];
584             }
585           else if optionDecls != [] then
586               if all (x: x.options.type.name == "submodule") optionDecls
587               # Raw options can only be merged into submodules. Merging into
588               # attrsets might be nice, but ambiguous. Suppose we have
589               # attrset as a `attrsOf submodule`. User declares option
590               # attrset.foo.bar, this could mean:
591               #  a. option `bar` is only available in `attrset.foo`
592               #  b. option `foo.bar` is available in all `attrset.*`
593               #  c. reject and require "<name>" as a reminder that it behaves like (b).
594               #  d. magically combine (a) and (c).
595               # All of the above are merely syntax sugar though.
596               then
597                 let opt = fixupOptionType loc (mergeOptionDecls loc (map optionTreeToOption decls));
598                 in {
599                   matchedOptions = evalOptionValue loc opt defns';
600                   unmatchedDefns = [];
601                 }
602               else
603                 let
604                   nonOptions = filter (m: !isOption m.options) decls;
605                 in
606                 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${
607                   showRawDecls loc nonOptions
608                 }"
609           else
610             mergeModules' loc decls defns) declsByName;
612       matchedOptions = mapAttrs (n: v: v.matchedOptions) resultsByName;
614       # an attrset 'name' => list of unmatched definitions for 'name'
615       unmatchedDefnsByName =
616         # Propagate all unmatched definitions from nested option sets
617         mapAttrs (n: v: v.unmatchedDefns) resultsByName
618         # Plus the definitions for the current prefix that don't have a matching option
619         // removeAttrs defnsByName' (attrNames matchedOptions);
620     in {
621       inherit matchedOptions;
623       # Transforms unmatchedDefnsByName into a list of definitions
624       unmatchedDefns =
625         if configs == []
626         then
627           # When no config values exist, there can be no unmatched config, so
628           # we short circuit and avoid evaluating more _options_ than necessary.
629           []
630         else
631           concatLists (mapAttrsToList (name: defs:
632             map (def: def // {
633               # Set this so we know when the definition first left unmatched territory
634               prefix = [name] ++ (def.prefix or []);
635             }) defs
636           ) unmatchedDefnsByName);
637     };
639   /* Merge multiple option declarations into a single declaration.  In
640      general, there should be only one declaration of each option.
641      The exception is the ‘options’ attribute, which specifies
642      sub-options.  These can be specified multiple times to allow one
643      module to add sub-options to an option declared somewhere else
644      (e.g. multiple modules define sub-options for ‘fileSystems’).
646      'loc' is the list of attribute names where the option is located.
648      'opts' is a list of modules.  Each module has an options attribute which
649      correspond to the definition of 'loc' in 'opt.file'. */
650   mergeOptionDecls =
651    loc: opts:
652     foldl' (res: opt:
653       let t  = res.type;
654           t' = opt.options.type;
655           mergedType = t.typeMerge t'.functor;
656           typesMergeable = mergedType != null;
657           typeSet = if (bothHave "type") && typesMergeable
658                        then { type = mergedType; }
659                        else {};
660           bothHave = k: opt.options ? ${k} && res ? ${k};
661       in
662       if bothHave "default" ||
663          bothHave "example" ||
664          bothHave "description" ||
665          bothHave "apply" ||
666          (bothHave "type" && (! typesMergeable))
667       then
668         throw "The option `${showOption loc}' in `${opt._file}' is already declared in ${showFiles res.declarations}."
669       else
670         let
671           getSubModules = opt.options.type.getSubModules or null;
672           submodules =
673             if getSubModules != null then map (setDefaultModuleLocation opt._file) getSubModules ++ res.options
674             else res.options;
675         in opt.options // res //
676           { declarations = res.declarations ++ [opt._file];
677             options = submodules;
678           } // typeSet
679     ) { inherit loc; declarations = []; options = []; } opts;
681   /* Merge all the definitions of an option to produce the final
682      config value. */
683   evalOptionValue = loc: opt: defs:
684     let
685       # Add in the default value for this option, if any.
686       defs' =
687           (optional (opt ? default)
688             { file = head opt.declarations; value = mkOptionDefault opt.default; }) ++ defs;
690       # Handle properties, check types, and merge everything together.
691       res =
692         if opt.readOnly or false && length defs' > 1 then
693           let
694             # For a better error message, evaluate all readOnly definitions as
695             # if they were the only definition.
696             separateDefs = map (def: def // {
697               value = (mergeDefinitions loc opt.type [ def ]).mergedValue;
698             }) defs';
699           in throw "The option `${showOption loc}' is read-only, but it's set multiple times. Definition values:${showDefs separateDefs}"
700         else
701           mergeDefinitions loc opt.type defs';
703       # Apply the 'apply' function to the merged value. This allows options to
704       # yield a value computed from the definitions
705       value = if opt ? apply then opt.apply res.mergedValue else res.mergedValue;
707       warnDeprecation =
708         warnIf (opt.type.deprecationMessage != null)
709           "The type `types.${opt.type.name}' of option `${showOption loc}' defined in ${showFiles opt.declarations} is deprecated. ${opt.type.deprecationMessage}";
711     in warnDeprecation opt //
712       { value = builtins.addErrorContext "while evaluating the option `${showOption loc}':" value;
713         inherit (res.defsFinal') highestPrio;
714         definitions = map (def: def.value) res.defsFinal;
715         files = map (def: def.file) res.defsFinal;
716         definitionsWithLocations = res.defsFinal;
717         inherit (res) isDefined;
718         # This allows options to be correctly displayed using `${options.path.to.it}`
719         __toString = _: showOption loc;
720       };
722   # Merge definitions of a value of a given type.
723   mergeDefinitions = loc: type: defs: rec {
724     defsFinal' =
725       let
726         # Process mkMerge and mkIf properties.
727         defs' = concatMap (m:
728           map (value: { inherit (m) file; inherit value; }) (builtins.addErrorContext "while evaluating definitions from `${m.file}':" (dischargeProperties m.value))
729         ) defs;
731         # Process mkOverride properties.
732         defs'' = filterOverrides' defs';
734         # Sort mkOrder properties.
735         defs''' =
736           # Avoid sorting if we don't have to.
737           if any (def: def.value._type or "" == "order") defs''.values
738           then sortProperties defs''.values
739           else defs''.values;
740       in {
741         values = defs''';
742         inherit (defs'') highestPrio;
743       };
744     defsFinal = defsFinal'.values;
746     # Type-check the remaining definitions, and merge them. Or throw if no definitions.
747     mergedValue =
748       if isDefined then
749         if all (def: type.check def.value) defsFinal then type.merge loc defsFinal
750         else let allInvalid = filter (def: ! type.check def.value) defsFinal;
751         in throw "A definition for option `${showOption loc}' is not of type `${type.description}'. Definition values:${showDefs allInvalid}"
752       else
753         # (nixos-option detects this specific error message and gives it special
754         # handling.  If changed here, please change it there too.)
755         throw "The option `${showOption loc}' is used but not defined.";
757     isDefined = defsFinal != [];
759     optionalValue =
760       if isDefined then { value = mergedValue; }
761       else {};
762   };
764   /* Given a config set, expand mkMerge properties, and push down the
765      other properties into the children.  The result is a list of
766      config sets that do not have properties at top-level.  For
767      example,
769        mkMerge [ { boot = set1; } (mkIf cond { boot = set2; services = set3; }) ]
771      is transformed into
773        [ { boot = set1; } { boot = mkIf cond set2; services = mkIf cond set3; } ].
775      This transform is the critical step that allows mkIf conditions
776      to refer to the full configuration without creating an infinite
777      recursion.
778   */
779   pushDownProperties = cfg:
780     if cfg._type or "" == "merge" then
781       concatMap pushDownProperties cfg.contents
782     else if cfg._type or "" == "if" then
783       map (mapAttrs (n: v: mkIf cfg.condition v)) (pushDownProperties cfg.content)
784     else if cfg._type or "" == "override" then
785       map (mapAttrs (n: v: mkOverride cfg.priority v)) (pushDownProperties cfg.content)
786     else # FIXME: handle mkOrder?
787       [ cfg ];
789   /* Given a config value, expand mkMerge properties, and discharge
790      any mkIf conditions.  That is, this is the place where mkIf
791      conditions are actually evaluated.  The result is a list of
792      config values.  For example, ‘mkIf false x’ yields ‘[]’,
793      ‘mkIf true x’ yields ‘[x]’, and
795        mkMerge [ 1 (mkIf true 2) (mkIf true (mkIf false 3)) ]
797      yields ‘[ 1 2 ]’.
798   */
799   dischargeProperties = def:
800     if def._type or "" == "merge" then
801       concatMap dischargeProperties def.contents
802     else if def._type or "" == "if" then
803       if isBool def.condition then
804         if def.condition then
805           dischargeProperties def.content
806         else
807           [ ]
808       else
809         throw "‘mkIf’ called with a non-Boolean condition"
810     else
811       [ def ];
813   /* Given a list of config values, process the mkOverride properties,
814      that is, return the values that have the highest (that is,
815      numerically lowest) priority, and strip the mkOverride
816      properties.  For example,
818        [ { file = "/1"; value = mkOverride 10 "a"; }
819          { file = "/2"; value = mkOverride 20 "b"; }
820          { file = "/3"; value = "z"; }
821          { file = "/4"; value = mkOverride 10 "d"; }
822        ]
824      yields
826        [ { file = "/1"; value = "a"; }
827          { file = "/4"; value = "d"; }
828        ]
830      Note that "z" has the default priority 100.
831   */
832   filterOverrides = defs: (filterOverrides' defs).values;
834   filterOverrides' = defs:
835     let
836       getPrio = def: if def.value._type or "" == "override" then def.value.priority else defaultPriority;
837       highestPrio = foldl' (prio: def: min (getPrio def) prio) 9999 defs;
838       strip = def: if def.value._type or "" == "override" then def // { value = def.value.content; } else def;
839     in {
840       values = concatMap (def: if getPrio def == highestPrio then [(strip def)] else []) defs;
841       inherit highestPrio;
842     };
844   /* Sort a list of properties.  The sort priority of a property is
845      1000 by default, but can be overridden by wrapping the property
846      using mkOrder. */
847   sortProperties = defs:
848     let
849       strip = def:
850         if def.value._type or "" == "order"
851         then def // { value = def.value.content; inherit (def.value) priority; }
852         else def;
853       defs' = map strip defs;
854       compare = a: b: (a.priority or 1000) < (b.priority or 1000);
855     in sort compare defs';
857   # This calls substSubModules, whose entire purpose is only to ensure that
858   # option declarations in submodules have accurate position information.
859   # TODO: Merge this into mergeOptionDecls
860   fixupOptionType = loc: opt:
861     if opt.type.getSubModules or null == null
862     then opt // { type = opt.type or types.unspecified; }
863     else opt // { type = opt.type.substSubModules opt.options; options = []; };
866   /* Properties. */
868   mkIf = condition: content:
869     { _type = "if";
870       inherit condition content;
871     };
873   mkAssert = assertion: message: content:
874     mkIf
875       (if assertion then true else throw "\nFailed assertion: ${message}")
876       content;
878   mkMerge = contents:
879     { _type = "merge";
880       inherit contents;
881     };
883   mkOverride = priority: content:
884     { _type = "override";
885       inherit priority content;
886     };
888   mkOptionDefault = mkOverride 1500; # priority of option defaults
889   mkDefault = mkOverride 1000; # used in config sections of non-user modules to set a default
890   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
891   mkForce = mkOverride 50;
892   mkVMOverride = mkOverride 10; # used by ‘nixos-rebuild build-vm’
894   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;
896   mkOrder = priority: content:
897     { _type = "order";
898       inherit priority content;
899     };
901   mkBefore = mkOrder 500;
902   mkAfter = mkOrder 1500;
904   # The default priority for things that don't have a priority specified.
905   defaultPriority = 100;
907   # Convenient property used to transfer all definitions and their
908   # properties from one option to another. This property is useful for
909   # renaming options, and also for including properties from another module
910   # system, including sub-modules.
911   #
912   #   { config, options, ... }:
913   #
914   #   {
915   #     # 'bar' might not always be defined in the current module-set.
916   #     config.foo.enable = mkAliasDefinitions (options.bar.enable or {});
917   #
918   #     # 'barbaz' has to be defined in the current module-set.
919   #     config.foobar.paths = mkAliasDefinitions options.barbaz.paths;
920   #   }
921   #
922   # Note, this is different than taking the value of the option and using it
923   # as a definition, as the new definition will not keep the mkOverride /
924   # mkDefault properties of the previous option.
925   #
926   mkAliasDefinitions = mkAliasAndWrapDefinitions id;
927   mkAliasAndWrapDefinitions = wrap: option:
928     mkAliasIfDef option (wrap (mkMerge option.definitions));
930   # Similar to mkAliasAndWrapDefinitions but copies over the priority from the
931   # option as well.
932   #
933   # If a priority is not set, it assumes a priority of defaultPriority.
934   mkAliasAndWrapDefsWithPriority = wrap: option:
935     let
936       prio = option.highestPrio or defaultPriority;
937       defsWithPrio = map (mkOverride prio) option.definitions;
938     in mkAliasIfDef option (wrap (mkMerge defsWithPrio));
940   mkAliasIfDef = option:
941     mkIf (isOption option && option.isDefined);
943   /* Compatibility. */
944   fixMergeModules = modules: args: evalModules { inherit modules args; check = false; };
947   /* Return a module that causes a warning to be shown if the
948      specified option is defined. For example,
950        mkRemovedOptionModule [ "boot" "loader" "grub" "bootDevice" ] "<replacement instructions>"
952      causes a assertion if the user defines boot.loader.grub.bootDevice.
954      replacementInstructions is a string that provides instructions on
955      how to achieve the same functionality without the removed option,
956      or alternatively a reasoning why the functionality is not needed.
957      replacementInstructions SHOULD be provided!
958   */
959   mkRemovedOptionModule = optionName: replacementInstructions:
960     { options, ... }:
961     { options = setAttrByPath optionName (mkOption {
962         visible = false;
963         apply = x: throw "The option `${showOption optionName}' can no longer be used since it's been removed. ${replacementInstructions}";
964       });
965       config.assertions =
966         let opt = getAttrFromPath optionName options; in [{
967           assertion = !opt.isDefined;
968           message = ''
969             The option definition `${showOption optionName}' in ${showFiles opt.files} no longer has any effect; please remove it.
970             ${replacementInstructions}
971           '';
972         }];
973     };
975   /* Return a module that causes a warning to be shown if the
976      specified "from" option is defined; the defined value is however
977      forwarded to the "to" option. This can be used to rename options
978      while providing backward compatibility. For example,
980        mkRenamedOptionModule [ "boot" "copyKernels" ] [ "boot" "loader" "grub" "copyKernels" ]
982      forwards any definitions of boot.copyKernels to
983      boot.loader.grub.copyKernels while printing a warning.
985      This also copies over the priority from the aliased option to the
986      non-aliased option.
987   */
988   mkRenamedOptionModule = from: to: doRename {
989     inherit from to;
990     visible = false;
991     warn = true;
992     use = builtins.trace "Obsolete option `${showOption from}' is used. It was renamed to `${showOption to}'.";
993   };
995   mkRenamedOptionModuleWith = {
996     /* Old option path as list of strings. */
997     from,
998     /* New option path as list of strings. */
999     to,
1001     /*
1002       Release number of the first release that contains the rename, ignoring backports.
1003       Set it to the upcoming release, matching the nixpkgs/.version file.
1004     */
1005     sinceRelease,
1007   }: doRename {
1008     inherit from to;
1009     visible = false;
1010     warn = lib.isInOldestRelease sinceRelease;
1011     use = lib.warnIf (lib.isInOldestRelease sinceRelease)
1012       "Obsolete option `${showOption from}' is used. It was renamed to `${showOption to}'.";
1013   };
1015   /* Return a module that causes a warning to be shown if any of the "from"
1016      option is defined; the defined values can be used in the "mergeFn" to set
1017      the "to" value.
1018      This function can be used to merge multiple options into one that has a
1019      different type.
1021      "mergeFn" takes the module "config" as a parameter and must return a value
1022      of "to" option type.
1024        mkMergedOptionModule
1025          [ [ "a" "b" "c" ]
1026            [ "d" "e" "f" ] ]
1027          [ "x" "y" "z" ]
1028          (config:
1029            let value = p: getAttrFromPath p config;
1030            in
1031            if      (value [ "a" "b" "c" ]) == true then "foo"
1032            else if (value [ "d" "e" "f" ]) == true then "bar"
1033            else "baz")
1035      - options.a.b.c is a removed boolean option
1036      - options.d.e.f is a removed boolean option
1037      - options.x.y.z is a new str option that combines a.b.c and d.e.f
1038        functionality
1040      This show a warning if any a.b.c or d.e.f is set, and set the value of
1041      x.y.z to the result of the merge function
1042   */
1043   mkMergedOptionModule = from: to: mergeFn:
1044     { config, options, ... }:
1045     {
1046       options = foldl' recursiveUpdate {} (map (path: setAttrByPath path (mkOption {
1047         visible = false;
1048         # To use the value in mergeFn without triggering errors
1049         default = "_mkMergedOptionModule";
1050       })) from);
1052       config = {
1053         warnings = filter (x: x != "") (map (f:
1054           let val = getAttrFromPath f config;
1055               opt = getAttrFromPath f options;
1056           in
1057           optionalString
1058             (val != "_mkMergedOptionModule")
1059             "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."
1060         ) from);
1061       } // setAttrByPath to (mkMerge
1062              (optional
1063                (any (f: (getAttrFromPath f config) != "_mkMergedOptionModule") from)
1064                (mergeFn config)));
1065     };
1067   /* Single "from" version of mkMergedOptionModule.
1068      Return a module that causes a warning to be shown if the "from" option is
1069      defined; the defined value can be used in the "mergeFn" to set the "to"
1070      value.
1071      This function can be used to change an option into another that has a
1072      different type.
1074      "mergeFn" takes the module "config" as a parameter and must return a value of
1075      "to" option type.
1077        mkChangedOptionModule [ "a" "b" "c" ] [ "x" "y" "z" ]
1078          (config:
1079            let value = getAttrFromPath [ "a" "b" "c" ] config;
1080            in
1081            if   value > 100 then "high"
1082            else "normal")
1084      - options.a.b.c is a removed int option
1085      - options.x.y.z is a new str option that supersedes a.b.c
1087      This show a warning if a.b.c is set, and set the value of x.y.z to the
1088      result of the change function
1089   */
1090   mkChangedOptionModule = from: to: changeFn:
1091     mkMergedOptionModule [ from ] to changeFn;
1093   /* Like ‘mkRenamedOptionModule’, but doesn't show a warning. */
1094   mkAliasOptionModule = from: to: doRename {
1095     inherit from to;
1096     visible = true;
1097     warn = false;
1098     use = id;
1099   };
1101   /* mkDerivedConfig : Option a -> (a -> Definition b) -> Definition b
1103     Create config definitions with the same priority as the definition of another option.
1104     This should be used for option definitions where one option sets the value of another as a convenience.
1105     For instance a config file could be set with a `text` or `source` option, where text translates to a `source`
1106     value using `mkDerivedConfig options.text (pkgs.writeText "filename.conf")`.
1108     It takes care of setting the right priority using `mkOverride`.
1109   */
1110   # TODO: make the module system error message include information about `opt` in
1111   # error messages about conflicts. E.g. introduce a variation of `mkOverride` which
1112   # adds extra location context to the definition object. This will allow context to be added
1113   # to all messages that report option locations "this value was derived from <full option name>
1114   # which was defined in <locations>". It can provide a trace of options that contributed
1115   # to definitions.
1116   mkDerivedConfig = opt: f:
1117     mkOverride
1118       (opt.highestPrio or defaultPriority)
1119       (f opt.value);
1121   doRename = { from, to, visible, warn, use, withPriority ? true }:
1122     { config, options, ... }:
1123     let
1124       fromOpt = getAttrFromPath from options;
1125       toOf = attrByPath to
1126         (abort "Renaming error: option `${showOption to}' does not exist.");
1127       toType = let opt = attrByPath to {} options; in opt.type or (types.submodule {});
1128     in
1129     {
1130       options = setAttrByPath from (mkOption {
1131         inherit visible;
1132         description = lib.mdDoc "Alias of {option}`${showOption to}`.";
1133         apply = x: use (toOf config);
1134       } // optionalAttrs (toType != null) {
1135         type = toType;
1136       });
1137       config = mkMerge [
1138         (optionalAttrs (options ? warnings) {
1139           warnings = optional (warn && fromOpt.isDefined)
1140             "The option `${showOption from}' defined in ${showFiles fromOpt.files} has been renamed to `${showOption to}'.";
1141         })
1142         (if withPriority
1143           then mkAliasAndWrapDefsWithPriority (setAttrByPath to) fromOpt
1144           else mkAliasAndWrapDefinitions (setAttrByPath to) fromOpt)
1145       ];
1146     };
1148   /* Use this function to import a JSON file as NixOS configuration.
1150      modules.importJSON :: path -> attrs
1151   */
1152   importJSON = file: {
1153     _file = file;
1154     config = lib.importJSON file;
1155   };
1157   /* Use this function to import a TOML file as NixOS configuration.
1159      modules.importTOML :: path -> attrs
1160   */
1161   importTOML = file: {
1162     _file = file;
1163     config = lib.importTOML file;
1164   };