evalModules
lib.evalModules
Docs pulled from | This Revision | 1 day ago
See https://nixos.org/manual/nixpkgs/unstable/#module-system-lib-evalModules or file://./../doc/module-system/module-system.chapter.md
!!! 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.
Noogle detected
Implementation
The following is the current implementation of this function.
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,
# there's _module.args. If specialArgs.modulesPath is defined it will be
# used as the base path for disabledModules.
specialArgs ? {}
, # `class`:
# A nominal type for modules. When set and non-null, this adds a check to
# make sure that only compatible modules are imported.
class ? null
, # This would be remove in the future, Prefer _module.args option instead.
args ? {}
, # This would be remove in the future, Prefer _module.check option instead.
check ? true
}:
let
withWarnings = x:
warnIf (evalModulesArgs?args) "The args argument to evalModules is deprecated. Please set config._module.args instead."
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 = "lib/modules.nix";
key = _file;
options = {
_module.args = mkOption {
# Because things like `mkIf` are entirely useless for
# `_module.args` (because there's no way modules can check which
# arguments were passed), we'll use `lazyAttrsOf` which drops
# support for that, in turn it's lazy in its values. This means e.g.
# a `_module.args.pkgs = import (fetchTarball { ... }) {}` won't
# start a download when `pkgs` wasn't evaluated.
type = types.lazyAttrsOf types.raw;
# Only render documentation once at the root of the option tree,
# not for all individual submodules.
# Allow merging option decls to make this internal regardless.
${if prefix == []
then null # unset => visible
else "internal"} = true;
# TODO: Change the type of this option to a submodule with a
# freeformType, so that individual arguments can be documented
# separately
description = ''
Additional arguments passed to each module in addition to ones
like `lib`, `config`,
and `pkgs`, `modulesPath`.
This option is also available to all submodules. Submodules do not
inherit args from their parent module, nor do they provide args to
their parent module or sibling submodules. The sole exception to
this is the argument `name` which is provided by
parent modules to a submodule and contains the attribute name
the submodule is bound to, or a unique generated name if it is
not bound to an attribute.
Some arguments are already passed by default, of which the
following *cannot* be changed with this option:
- {var}`lib`: The nixpkgs library.
- {var}`config`: The results of all options after merging the values from all modules together.
- {var}`options`: The options declared in all modules.
- {var}`specialArgs`: The `specialArgs` argument passed to `evalModules`.
- All attributes of {var}`specialArgs`
Whereas option values can generally depend on other option values
thanks to laziness, this does not apply to `imports`, which
must be computed statically before anything else.
For this reason, callers of the module system can provide `specialArgs`
which are available during import resolution.
For NixOS, `specialArgs` includes
{var}`modulesPath`, which allows you to import
extra modules from the nixpkgs package tree without having to
somehow make the module aware of the location of the
`nixpkgs` or NixOS directories.
```
{ modulesPath, ... }: {
imports = [
(modulesPath + "/profiles/minimal.nix")
];
}
```
For NixOS, the default value for this option includes at least this argument:
- {var}`pkgs`: The nixpkgs package set according to
the {option}`nixpkgs.pkgs` option.
'';
};
_module.check = mkOption {
type = types.bool;
internal = true;
default = true;
description = "Whether to check whether all option definitions have matching declarations.";
};
_module.freeformType = mkOption {
type = types.nullOr types.optionType;
internal = true;
default = null;
description = ''
If set, merge all definitions that don't have an associated option
together using this type. The result then gets combined with the
values of all declared options to produce the final `
config` value.
If this is `null`, definitions without an option
will throw an error unless {option}`_module.check` is
turned off.
'';
};
_module.specialArgs = mkOption {
readOnly = true;
internal = true;
description = ''
Externally provided module arguments that can't be modified from
within a configuration, but can be used in module imports.
'';
};
};
config = {
_module.args = {
inherit extendModules;
moduleType = type;
};
_module.specialArgs = specialArgs;
};
};
merged =
let collected = collectModules
class
(specialArgs.modulesPath or "")
(regularModules ++ [ internalModule ])
({ inherit lib options config specialArgs; } // specialArgs);
in mergeModules prefix (reverseList collected);
options = merged.matchedOptions;
config =
let
# For definitions that have an associated option
declaredConfig = mapAttrsRecursiveCond (v: ! isOption v) (_: v: v.value) options;
# If freeformType is set, this is for definitions that don't have an associated option
freeformConfig =
let
defs = map (def: {
file = def.file;
value = setAttrByPath def.prefix def.value;
}) merged.unmatchedDefns;
in if defs == [] then {}
else declaredConfig._module.freeformType.merge prefix defs;
in if declaredConfig._module.freeformType == null then declaredConfig
# Because all definitions that had an associated option ended in
# declaredConfig, freeformConfig can only contain the non-option
# paths, meaning recursiveUpdate will never override any value
else recursiveUpdate freeformConfig declaredConfig;
checkUnmatched =
if config._module.check && config._module.freeformType == null && merged.unmatchedDefns != [] then
let
firstDef = head merged.unmatchedDefns;
baseMsg =
let
optText = showOption (prefix ++ firstDef.prefix);
defText =
addErrorContext
"while evaluating the error message for definitions for `${optText}', which is an option that does not exist"
(addErrorContext
"while evaluating a definition from `${firstDef.file}'"
( showDefs [ firstDef ])
);
in
"The option `${optText}' does not exist. Definition values:${defText}";
in
if attrNames options == [ "_module" ]
# No options were declared at all (`_module` is built in)
# but we do have unmatched definitions, and no freeformType (earlier conditions)
then
let
optionName = showOption prefix;
in
if optionName == ""
then throw ''
${baseMsg}
It seems as if you're trying to declare an option by placing it into `config' rather than `options'!
''
else
throw ''
${baseMsg}
However there are no options defined in `${showOption prefix}'. Are you sure you've
declared your options properly? This can happen if you e.g. declared your options in `types.submodule'
under `config' rather than `options'.
''
else throw baseMsg
else null;
checked = seq checkUnmatched;
extendModules = extendArgs@{
modules ? [],
specialArgs ? {},
prefix ? [],
}:
evalModules (evalModulesArgs // {
inherit class;
modules = regularModules ++ modules;
specialArgs = evalModulesArgs.specialArgs or {} // specialArgs;
prefix = extendArgs.prefix or evalModulesArgs.prefix or [];
});
type = types.submoduleWith {
inherit modules specialArgs class;
};
result = withWarnings {
_type = "configuration";
options = checked options;
config = checked (removeAttrs config [ "_module" ]);
_module = checked (config._module);
inherit extendModules type;
class = class;
};
in result;