Merge pull request #300257 from mschwaig/fix-mivisionx
[NixPkgs.git] / lib / modules.nix
blob79892f50c4fe29a32b709ce0e77cf5706c2ecd53
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     ;
48   inherit (lib.strings)
49     isConvertibleWithToString
50     ;
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       ));
65   /* See https://nixos.org/manual/nixpkgs/unstable/#module-system-lib-evalModules
66      or file://./../doc/module-system/module-system.chapter.md
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;
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;
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";
119         key = _file;
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`.
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.
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`
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.
164                 For this reason, callers of the module system can provide `specialArgs`
165                 which are available during import resolution.
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                 ```
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           };
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           };
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.
203               If this is `null`, definitions without an option
204               will throw an error unless {option}`_module.check` is
205               turned off.
206             '';
207           };
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         };
219         config = {
220           _module.args = {
221             inherit extendModules;
222             moduleType = type;
223           };
224           _module.specialArgs = specialArgs;
225         };
226       };
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);
236       options = merged.matchedOptions;
238       config =
239         let
241           # For definitions that have an associated option
242           declaredConfig = mapAttrsRecursiveCond (v: ! isOption v) (_: v: v.value) options;
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;
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;
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}
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}
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;
301       checked = builtins.seq checkUnmatched;
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           });
315       type = lib.types.submoduleWith {
316         inherit modules specialArgs class;
317       };
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;
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
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);
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;
367       /*
368       Collects all modules recursively into the form
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);
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
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
430             else if m?key
431             then
432               m.key
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}.";
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         });
445     in modulesPath: initialModules: args:
446       filterModules modulesPath (collectStructuredModules unknownModule "" initialModules args);
448   /* Wrap a module with a default location for reporting errors. */
449   setDefaultModuleLocation = file: m:
450     { _file = file; imports = [ m ]; };
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       };
488   applyModuleArgsIfFunction = key: f: args@{ config, options, lib, ... }:
489     if isFunction f then applyModuleArgs key f args else f;
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);
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);
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.
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);
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);
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}'!
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;
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);
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           };
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;
676       matchedOptions = mapAttrs (n: v: v.matchedOptions) resultsByName;
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;
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     };
703   throwDeclarationTypeError = loc: actualTag: file:
704     let
705       name = lib.strings.escapeNixIdentifier (lib.lists.last loc);
706       path = showOption loc;
707       depth = length loc;
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         '';
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);
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’).
736      'loc' is the list of attribute names where the option is located.
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;
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;
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';
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;
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}";
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       };
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;
828         # Process mkOverride properties.
829         defs'' = filterOverrides' defs';
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;
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.";
854     isDefined = defsFinal != [];
856     optionalValue =
857       if isDefined then { value = mergedValue; }
858       else {};
859   };
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,
866        mkMerge [ { boot = set1; } (mkIf cond { boot = set2; services = set3; }) ]
868      is transformed into
870        [ { boot = set1; } { boot = mkIf cond set2; services = mkIf cond set3; } ].
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 ];
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
892        mkMerge [ 1 (mkIf true 2) (mkIf true (mkIf false 3)) ]
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 ];
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,
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        ]
921      yields
923        [ { file = "/1"; value = "a"; }
924          { file = "/4"; value = "d"; }
925        ]
927      Note that "z" has the default priority 100.
928   */
929   filterOverrides = defs: (filterOverrides' defs).values;
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     };
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';
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 = []; };
963   /*
964     Merge an option's definitions in a way that preserves the priority of the
965     individual attributes in the option value.
967     This does not account for all option semantics, such as readOnly.
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;
997   /* Properties. */
999   mkIf = condition: content:
1000     { _type = "if";
1001       inherit condition content;
1002     };
1004   mkAssert = assertion: message: content:
1005     mkIf
1006       (if assertion then true else throw "\nFailed assertion: ${message}")
1007       content;
1009   mkMerge = contents:
1010     { _type = "merge";
1011       inherit contents;
1012     };
1014   mkOverride = priority: content:
1015     { _type = "override";
1016       inherit priority content;
1017     };
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’
1026   defaultPriority = lib.warnIf (lib.isInOldestRelease 2305) "lib.modules.defaultPriority is deprecated, please use lib.modules.defaultOverridePriority instead." defaultOverridePriority;
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;
1030   mkOrder = priority: content:
1031     { _type = "order";
1032       inherit priority content;
1033     };
1035   mkBefore = mkOrder 500;
1036   defaultOrderPriority = 1000;
1037   mkAfter = mkOrder 1500;
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));
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));
1072   mkAliasIfDef = option:
1073     mkIf (isOption option && option.isDefined);
1075   /* Compatibility. */
1076   fixMergeModules = modules: args: evalModules { inherit modules args; check = false; };
1079   /* Return a module that causes a warning to be shown if the
1080      specified option is defined. For example,
1082        mkRemovedOptionModule [ "boot" "loader" "grub" "bootDevice" ] "<replacement instructions>"
1084      causes a assertion if the user defines boot.loader.grub.bootDevice.
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     };
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,
1112        mkRenamedOptionModule [ "boot" "copyKernels" ] [ "boot" "loader" "grub" "copyKernels" ]
1114      forwards any definitions of boot.copyKernels to
1115      boot.loader.grub.copyKernels while printing a warning.
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   };
1127   mkRenamedOptionModuleWith = {
1128     /* Old option path as list of strings. */
1129     from,
1130     /* New option path as list of strings. */
1131     to,
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,
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   };
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.
1153      "mergeFn" takes the module "config" as a parameter and must return a value
1154      of "to" option type.
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")
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
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);
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     };
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.
1206      "mergeFn" takes the module "config" as a parameter and must return a value of
1207      "to" option type.
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")
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
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;
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   };
1233   /* Transitional version of mkAliasOptionModule that uses MD docs.
1235      This function is no longer necessary and merely an alias of `mkAliasOptionModule`.
1236   */
1237   mkAliasOptionModuleMD = mkAliasOptionModule;
1239   /* mkDerivedConfig : Option a -> (a -> Definition b) -> Definition b
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")`.
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);
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     };
1357   /* Use this function to import a JSON file as NixOS configuration.
1359      modules.importJSON :: path -> attrs
1360   */
1361   importJSON = file: {
1362     _file = file;
1363     config = lib.importJSON file;
1364   };
1366   /* Use this function to import a TOML file as NixOS configuration.
1368      modules.importTOML :: path -> attrs
1369   */
1370   importTOML = file: {
1371     _file = file;
1372     config = lib.importTOML file;
1373   };
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     };
1390 private //
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;