diff options
Diffstat (limited to 'lib/modules.nix')
-rw-r--r-- | lib/modules.nix | 113 |
1 files changed, 102 insertions, 11 deletions
diff --git a/lib/modules.nix b/lib/modules.nix index e3abf846625..e3bb27aa946 100644 --- a/lib/modules.nix +++ b/lib/modules.nix @@ -50,15 +50,43 @@ in rec { - /* Evaluate a set of modules. The result is a set of two - attributes: ‘options’: the nested set of all option declarations, - and ‘config’: the nested set of all option values. + /* + Evaluate a set of modules. The result is a set with the attributes: + + ‘options’: The nested set of all option declarations, + + ‘config’: The nested set of all option values. + + ‘type’: A module system type representing the module set as a submodule, + to be extended by configuration from the containing module set. + + This is also available as the module argument ‘moduleType’. + + ‘extendModules’: A function similar to ‘evalModules’ but building on top + of the module set. Its arguments, ‘modules’ and ‘specialArgs’ are + added to the existing values. + + Using ‘extendModules’ a few times has no performance impact as long + as you only reference the final ‘options’ and ‘config’. + If you do reference multiple ‘config’ (or ‘options’) from before and + after ‘extendModules’, performance is the same as with multiple + ‘evalModules’ invocations, because the new modules' ability to + override existing configuration fundamentally requires a new + fixpoint to be constructed. + + This is also available as a module argument. + + ‘_module’: A portion of the configuration tree which is elided from + ‘config’. It contains some values that are mostly internal to the + module system implementation. + !!! Please think twice before adding to this argument list! The more that is specified here instead of in the modules themselves the harder it is to transparently move a set of modules to be a submodule of another config (as the proper arguments need to be replicated at each call to evalModules) and the less declarative the module set is. */ - evalModules = { modules + evalModules = evalModulesArgs@ + { modules , prefix ? [] , # This should only be used for special arguments that need to be evaluated # when resolving module structure (like in imports). For everything else, @@ -71,9 +99,31 @@ rec { check ? true }: let + withWarnings = x: + lib.warnIf (evalModulesArgs?args) "The args argument to evalModules is deprecated. Please set config._module.args instead." + lib.warnIf (evalModulesArgs?check) "The check argument to evalModules is deprecated. Please set config._module.check instead." + x; + + legacyModules = + optional (evalModulesArgs?args) { + config = { + _module.args = args; + }; + } + ++ optional (evalModulesArgs?check) { + config = { + _module.check = mkDefault check; + }; + }; + regularModules = modules ++ legacyModules; + # This internal module declare internal options under the `_module' # attribute. These options are fragile, as they are used by the # module system to change the interpretation of modules. + # + # When extended with extendModules or moduleType, a fresh instance of + # this module is used, to avoid conflicts and allow chaining of + # extendModules. internalModule = rec { _file = ./modules.nix; @@ -95,7 +145,7 @@ rec { _module.check = mkOption { type = types.bool; internal = true; - default = check; + default = true; description = "Whether to check whether all option definitions have matching declarations."; }; @@ -118,14 +168,17 @@ rec { }; config = { - _module.args = args; + _module.args = { + inherit extendModules; + moduleType = type; + }; }; }; merged = let collected = collectModules (specialArgs.modulesPath or "") - (modules ++ [ internalModule ]) + (regularModules ++ [ internalModule ]) ({ inherit lib options config specialArgs; } // specialArgs); in mergeModules prefix (reverseList collected); @@ -181,10 +234,28 @@ rec { else throw baseMsg else null; - result = builtins.seq checkUnmatched { - inherit options; - config = removeAttrs config [ "_module" ]; - inherit (config) _module; + checked = builtins.seq checkUnmatched; + + extendModules = extendArgs@{ + modules ? [], + specialArgs ? {}, + prefix ? [], + }: + evalModules (evalModulesArgs // { + modules = regularModules ++ modules; + specialArgs = evalModulesArgs.specialArgs or {} // specialArgs; + prefix = extendArgs.prefix or evalModulesArgs.prefix; + }); + + type = lib.types.submoduleWith { + inherit modules specialArgs; + }; + + result = withWarnings { + options = checked options; + config = checked (removeAttrs config [ "_module" ]); + _module = checked (config._module); + inherit extendModules type; }; in result; @@ -917,6 +988,26 @@ rec { use = id; }; + /* mkDerivedConfig : Option a -> (a -> Definition b) -> Definition b + + Create config definitions with the same priority as the definition of another option. + This should be used for option definitions where one option sets the value of another as a convenience. + For instance a config file could be set with a `text` or `source` option, where text translates to a `source` + value using `mkDerivedConfig options.text (pkgs.writeText "filename.conf")`. + + It takes care of setting the right priority using `mkOverride`. + */ + # TODO: make the module system error message include information about `opt` in + # error messages about conflicts. E.g. introduce a variation of `mkOverride` which + # adds extra location context to the definition object. This will allow context to be added + # to all messages that report option locations "this value was derived from <full option name> + # which was defined in <locations>". It can provide a trace of options that contributed + # to definitions. + mkDerivedConfig = opt: f: + mkOverride + (opt.highestPrio or defaultPriority) + (f opt.value); + doRename = { from, to, visible, warn, use, withPriority ? true }: { config, options, ... }: let |