diff --git a/lib/tests/modules.sh b/lib/tests/modules.sh index 309c5311361c1..526a2fac9117d 100755 --- a/lib/tests/modules.sh +++ b/lib/tests/modules.sh @@ -179,6 +179,13 @@ checkConfigOutput "true" config.submodule.outer ./declare-submoduleWith-modules. ## Paths should be allowed as values and work as expected checkConfigOutput "true" config.submodule.enable ./declare-submoduleWith-path.nix +## evalModules should be settable +checkConfigOutput 10 config.submodule.value ./submoduleWithLib.nix + +# Check the file location information is propagated into submodules +checkConfigOutput the-file.nix config.submodule.internalFiles.0 ./submoduleFiles.nix + + # Check that disabledModules works recursively and correctly checkConfigOutput "true" config.enable ./disable-recursive/main.nix checkConfigOutput "true" config.enable ./disable-recursive/{main.nix,disable-foo.nix} diff --git a/lib/tests/modules/submoduleFiles.nix b/lib/tests/modules/submoduleFiles.nix new file mode 100644 index 0000000000000..c0d9b2cef3e8d --- /dev/null +++ b/lib/tests/modules/submoduleFiles.nix @@ -0,0 +1,21 @@ +{ lib, ... }: { + options.submodule = lib.mkOption { + default = {}; + type = lib.types.submoduleWith { + modules = [ ({ options, ... }: { + options.value = lib.mkOption {}; + + options.internalFiles = lib.mkOption { + default = options.value.files; + }; + })]; + }; + }; + + imports = [ + { + _file = "the-file.nix"; + submodule.value = 10; + } + ]; +} diff --git a/lib/tests/modules/submoduleWithLib.nix b/lib/tests/modules/submoduleWithLib.nix new file mode 100644 index 0000000000000..a011b366ff34e --- /dev/null +++ b/lib/tests/modules/submoduleWithLib.nix @@ -0,0 +1,13 @@ +{ lib, ... }: { + options.submodule = lib.mkOption { + default = {}; + type = lib.types.submoduleWith { + evalModules = ((import ../..).extend (self: super: { value = 10; })).evalModules; + modules = [ ({ lib, ... }: { + options.value = lib.mkOption { + default = lib.value; + }; + })]; + }; + }; +} diff --git a/lib/types.nix b/lib/types.nix index 77105740bc235..6a88c1ad51a7a 100644 --- a/lib/types.nix +++ b/lib/types.nix @@ -408,27 +408,23 @@ rec { submoduleWith = { modules + , evalModules ? lib.modules.evalModules , specialArgs ? {} , shorthandOnlyDefinesConfig ? false }@attrs: let - inherit (lib.modules) evalModules; - - coerce = unify: value: if isFunction value - then setFunctionArgs (args: unify (value args)) (functionArgs value) - else unify (if shorthandOnlyDefinesConfig then { config = value; } else value); allModules = defs: modules ++ 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 + if isAttrs value && shorthandOnlyDefinesConfig + then { _file = file; config = value; } + else { _file = file; imports = [ value ]; } ) defs; freeformType = (evalModules { inherit modules specialArgs; args.name = "‹name›"; - })._module.freeformType; + })._module.freeformType or null; + # `or null` in case the given evalModules doesn't have that attribute yet in mkOptionType rec { @@ -477,6 +473,10 @@ rec { modules = modules; specialArgs = specialArgs; shorthandOnlyDefinesConfig = shorthandOnlyDefinesConfig; + } // optionalAttrs (attrs ? evalModules) { + # We need to be able to detect whether evalModules was set or not in + # the binOp function, to throw an error if it was set multiple times + inherit (attrs) evalModules; }; binOp = lhs: rhs: { modules = lhs.modules ++ rhs.modules; @@ -489,6 +489,10 @@ rec { if lhs.shorthandOnlyDefinesConfig == rhs.shorthandOnlyDefinesConfig then lhs.shorthandOnlyDefinesConfig else throw "A submoduleWith option is declared multiple times with conflicting shorthandOnlyDefinesConfig values"; + } // optionalAttrs (lhs ? evalModules || rhs ? evalModules) { + evalModules = if lhs ? evalModules && rhs ? evalModules + then throw "A submoduleWith option is declared multiple times with conflicting evalModules values" + else lhs.evalModules or rhs.evalModules; }; }; }; diff --git a/nixos/doc/manual/development/option-types.xml b/nixos/doc/manual/development/option-types.xml index 3d2191e2f3f31..25013918bd31a 100644 --- a/nixos/doc/manual/development/option-types.xml +++ b/nixos/doc/manual/development/option-types.xml @@ -319,6 +319,7 @@ types.submoduleWith { modules, + evalModules ? lib.evalModules, specialArgs ? {}, shorthandOnlyDefinesConfig ? false } @@ -335,6 +336,14 @@ Only options defined with this argument are included in rendered documentation. + + evalModules + The function used to evaluate the given modules. This can be set to a + lib.evalModules from another nixpkgs version if + the modules require that version for evaluation. This also causes + the lib module argument for the submodules to + point to the lib from evalModules. + specialArgs An attribute set of extra arguments to be passed to the module functions.