From c6af0fd1492285356debc0f8165acb48b7ec488e Mon Sep 17 00:00:00 2001 From: Silvan Mosberger Date: Sun, 27 Sep 2020 17:15:57 +0200 Subject: [PATCH 1/3] lib/tests: Add submodule file propagation test --- lib/tests/modules.sh | 4 ++++ lib/tests/modules/submoduleFiles.nix | 21 +++++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 lib/tests/modules/submoduleFiles.nix diff --git a/lib/tests/modules.sh b/lib/tests/modules.sh index 309c5311361c1..176c20a28419c 100755 --- a/lib/tests/modules.sh +++ b/lib/tests/modules.sh @@ -179,6 +179,10 @@ 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 +# 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; + } + ]; +} From 29d194de0fc600083db1b4a0ee8cfaaacc9b91c0 Mon Sep 17 00:00:00 2001 From: Silvan Mosberger Date: Sun, 27 Sep 2020 16:39:57 +0200 Subject: [PATCH 2/3] lib/types: Simplify submoduleWith shorthandOnlydefinesConfig handling The module system already uses the parent modules _type as a fallback, so we don't need to inject the file in a weird way With a future commit that allows passing custom lib versions, this will also allow usage of older nixpkgs versions that don't have aa613427b7ec84ddb9f07c21482dbdc6c169855d yet --- lib/types.nix | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/lib/types.nix b/lib/types.nix index 77105740bc235..93b42c89c2d6a 100644 --- a/lib/types.nix +++ b/lib/types.nix @@ -414,15 +414,10 @@ rec { 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 { From 123573b3a2a930cbbba7a7160121738641524955 Mon Sep 17 00:00:00 2001 From: Silvan Mosberger Date: Sun, 27 Sep 2020 17:18:05 +0200 Subject: [PATCH 3/3] lib/types: Allow submoduleWith to pass different evalModules This allows evaluating modules with older lib versions --- lib/tests/modules.sh | 3 +++ lib/tests/modules/submoduleWithLib.nix | 13 +++++++++++++ lib/types.nix | 13 +++++++++++-- nixos/doc/manual/development/option-types.xml | 9 +++++++++ 4 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 lib/tests/modules/submoduleWithLib.nix diff --git a/lib/tests/modules.sh b/lib/tests/modules.sh index 176c20a28419c..526a2fac9117d 100755 --- a/lib/tests/modules.sh +++ b/lib/tests/modules.sh @@ -179,6 +179,9 @@ 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 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 93b42c89c2d6a..6a88c1ad51a7a 100644 --- a/lib/types.nix +++ b/lib/types.nix @@ -408,11 +408,11 @@ rec { submoduleWith = { modules + , evalModules ? lib.modules.evalModules , specialArgs ? {} , shorthandOnlyDefinesConfig ? false }@attrs: let - inherit (lib.modules) evalModules; allModules = defs: modules ++ imap1 (n: { value, file }: if isAttrs value && shorthandOnlyDefinesConfig @@ -423,7 +423,8 @@ rec { 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 { @@ -472,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; @@ -484,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.