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