summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorRobert Hensing <robert@roberthensing.nl>2021-10-27 20:42:05 +0200
committerRobert Hensing <robert@roberthensing.nl>2021-11-01 09:34:07 +0100
commitdece37b83a46d488787859332e18286727b96cc4 (patch)
tree979d27f2821373269bf00f13912b2b02a7ebb234 /lib
parent9ec8a16c7908c7807e46fa4f38479ccc0af2b64e (diff)
lib.evalModules: Add extendModules and type to result
Allows the simultaneous construction of top-level invocations and submodule types. This helps structure configuration systems integration code.
Diffstat (limited to 'lib')
-rw-r--r--lib/modules.nix27
-rwxr-xr-xlib/tests/modules.sh7
-rw-r--r--lib/tests/modules/declare-submodule-via-evalModules.nix28
-rw-r--r--lib/types.nix56
4 files changed, 85 insertions, 33 deletions
diff --git a/lib/modules.nix b/lib/modules.nix
index 46ae3f136310..c25972999dfe 100644
--- a/lib/modules.nix
+++ b/lib/modules.nix
@@ -60,7 +60,8 @@ rec {
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,
@@ -183,10 +184,26 @@ rec {
else throw baseMsg
else null;
- result = builtins.seq checkUnmatched {
- inherit options;
- config = removeAttrs config [ "_module" ];
- inherit (config) _module;
+ checked = builtins.seq checkUnmatched;
+
+ result = {
+ options = checked options;
+ config = checked (removeAttrs config [ "_module" ]);
+ _module = checked (config._module);
+
+ extendModules = extendArgs@{
+ modules ? [],
+ specialArgs ? {},
+ prefix ? [],
+ }:
+ evalModules (evalModulesArgs // {
+ modules = evalModulesArgs.modules ++ modules;
+ specialArgs = evalModulesArgs.specialArgs or {} // specialArgs;
+ prefix = extendArgs.prefix or evalModulesArgs.prefix;
+ });
+ type = lib.types.submoduleWith {
+ inherit modules specialArgs;
+ };
};
in result;
diff --git a/lib/tests/modules.sh b/lib/tests/modules.sh
index b51db91f6b07..49fc8bcbafc4 100755
--- a/lib/tests/modules.sh
+++ b/lib/tests/modules.sh
@@ -179,6 +179,13 @@ checkConfigOutput "true" config.submodule.outer ./declare-submoduleWith-modules.
# which evaluates all the modules defined by the type)
checkConfigOutput "submodule" options.submodule.type.description ./declare-submoduleWith-modules.nix
+## submodules can be declared using (evalModules {...}).type
+checkConfigOutput "true" config.submodule.inner ./declare-submodule-via-evalModules.nix
+checkConfigOutput "true" config.submodule.outer ./declare-submodule-via-evalModules.nix
+# Should also be able to evaluate the type name (which evaluates freeformType,
+# which evaluates all the modules defined by the type)
+checkConfigOutput "submodule" options.submodule.type.description ./declare-submodule-via-evalModules.nix
+
## Paths should be allowed as values and work as expected
checkConfigOutput "true" config.submodule.enable ./declare-submoduleWith-path.nix
diff --git a/lib/tests/modules/declare-submodule-via-evalModules.nix b/lib/tests/modules/declare-submodule-via-evalModules.nix
new file mode 100644
index 000000000000..2841c64a073d
--- /dev/null
+++ b/lib/tests/modules/declare-submodule-via-evalModules.nix
@@ -0,0 +1,28 @@
+{ lib, ... }: {
+ options.submodule = lib.mkOption {
+ inherit (lib.evalModules {
+ modules = [
+ {
+ options.inner = lib.mkOption {
+ type = lib.types.bool;
+ default = false;
+ };
+ }
+ ];
+ }) type;
+ default = {};
+ };
+
+ config.submodule = lib.mkMerge [
+ ({ lib, ... }: {
+ options.outer = lib.mkOption {
+ type = lib.types.bool;
+ default = false;
+ };
+ })
+ {
+ inner = true;
+ outer = true;
+ }
+ ];
+}
diff --git a/lib/types.nix b/lib/types.nix
index c2532065d7ea..244cbb6b5354 100644
--- a/lib/types.nix
+++ b/lib/types.nix
@@ -505,17 +505,36 @@ rec {
then setFunctionArgs (args: unify (value args)) (functionArgs value)
else unify (if shorthandOnlyDefinesConfig then { config = value; } else value);
- allModules = defs: modules ++ imap1 (n: { value, file }:
+ allModules = defs: imap1 (n: { value, file }:
if isAttrs value || isFunction value then
# Annotate the value with the location of its definition for better error messages
coerce (lib.modules.unifyModuleSyntax file "${toString file}-${toString n}") value
else value
) defs;
- freeformType = (evalModules {
- inherit modules specialArgs;
- args.name = "‹name›";
- })._module.freeformType;
+ base = evalModules {
+ inherit specialArgs;
+ modules = [{
+ # This is a work-around for the fact that some sub-modules,
+ # such as the one included in an attribute set, expects an "args"
+ # attribute to be given to the sub-module. As the option
+ # evaluation does not have any specific attribute name yet, we
+ # provide a default for the documentation and the freeform type.
+ #
+ # This is necessary as some option declaration might use the
+ # "name" attribute given as argument of the submodule and use it
+ # as the default of option declarations.
+ #
+ # We use lookalike unicode single angle quotation marks because
+ # of the docbook transformation the options receive. In all uses
+ # &gt; and &lt; wouldn't be encoded correctly so the encoded values
+ # would be used, and use of `<` and `>` would break the XML document.
+ # It shouldn't cause an issue since this is cosmetic for the manual.
+ _module.args.name = lib.mkOptionDefault "‹name›";
+ }] ++ modules;
+ };
+
+ freeformType = base._module.freeformType;
in
mkOptionType rec {
@@ -523,32 +542,13 @@ rec {
description = freeformType.description or name;
check = x: isAttrs x || isFunction x || path.check x;
merge = loc: defs:
- (evalModules {
- modules = allModules defs;
- inherit specialArgs;
- args.name = last loc;
+ (base.extendModules {
+ modules = [ { _module.args.name = last loc; } ] ++ allModules defs;
prefix = loc;
}).config;
emptyValue = { value = {}; };
- getSubOptions = prefix: (evalModules
- { inherit modules prefix specialArgs;
- # This is a work-around due to the fact that some sub-modules,
- # such as the one included in an attribute set, expects a "args"
- # attribute to be given to the sub-module. As the option
- # evaluation does not have any specific attribute name, we
- # provide a default one for the documentation.
- #
- # This is mandatory as some option declaration might use the
- # "name" attribute given as argument of the submodule and use it
- # as the default of option declarations.
- #
- # Using lookalike unicode single angle quotation marks because
- # of the docbook transformation the options receive. In all uses
- # &gt; and &lt; wouldn't be encoded correctly so the encoded values
- # would be used, and use of `<` and `>` would break the XML document.
- # It shouldn't cause an issue since this is cosmetic for the manual.
- args.name = "‹name›";
- }).options // optionalAttrs (freeformType != null) {
+ getSubOptions = prefix: (base.extendModules
+ { inherit prefix; }).options // optionalAttrs (freeformType != null) {
# Expose the sub options of the freeform type. Note that the option
# discovery doesn't care about the attribute name used here, so this
# is just to avoid conflicts with potential options from the submodule