From d030e2109fd491e32cb48df54d100aa608551298 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 24 Jan 2022 15:58:17 +0100 Subject: lib.modules: Let module declare options directly in bare submodule ... where a bare submodule is an option that has a type like `submoduleWith x`, as opposed to `attrsOf (submoduleWith x)`. This makes migration unnecessary when introducing a freeform type in an existing option tree. Closes #146882 --- lib/modules.nix | 22 +++++++++++++++++++++- lib/tests/modules.sh | 5 +++++ .../modules/declare-bare-submodule-deep-option.nix | 10 ++++++++++ .../declare-bare-submodule-nested-option.nix | 18 ++++++++++++++++++ lib/tests/modules/declare-bare-submodule.nix | 12 ++++++++++++ lib/tests/modules/define-bare-submodule-values.nix | 4 ++++ lib/types.nix | 5 +++++ 7 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 lib/tests/modules/declare-bare-submodule-deep-option.nix create mode 100644 lib/tests/modules/declare-bare-submodule-nested-option.nix create mode 100644 lib/tests/modules/declare-bare-submodule.nix create mode 100644 lib/tests/modules/define-bare-submodule-values.nix diff --git a/lib/modules.nix b/lib/modules.nix index 79d54e4a538..bde89123cd9 100644 --- a/lib/modules.nix +++ b/lib/modules.nix @@ -474,6 +474,18 @@ rec { [{ inherit (module) file; inherit value; }] ) configs; + # Convert an option tree decl to a submodule option decl + optionTreeToOption = decl: + if isOption decl.options + then decl + else decl // { + options = mkOption { + type = types.submoduleWith { + modules = [ { options = decl.options; } ]; + }; + }; + }; + resultsByName = mapAttrs (name: decls: # We're descending into attribute ‘name’. let @@ -493,7 +505,15 @@ rec { firstOption = findFirst (m: isOption m.options) "" decls; firstNonOption = findFirst (m: !isOption m.options) "" decls; in - throw "The option `${showOption loc}' in `${firstOption._file}' is a prefix of options in `${firstNonOption._file}'." + if firstOption.options.type?isSubmodule && firstOption.options.type.isSubmodule + then + let opt = fixupOptionType loc (mergeOptionDecls loc (map optionTreeToOption decls)); + in { + matchedOptions = evalOptionValue loc opt defns'; + unmatchedDefns = []; + } + else + throw "The option `${showOption loc}' in `${firstOption._file}' is a prefix of options in `${firstNonOption._file}'." else mergeModules' loc decls defns) declsByName; diff --git a/lib/tests/modules.sh b/lib/tests/modules.sh index e4bb7ad2190..3591b8f1e6f 100755 --- a/lib/tests/modules.sh +++ b/lib/tests/modules.sh @@ -62,6 +62,11 @@ checkConfigError() { checkConfigOutput '^false$' config.enable ./declare-enable.nix checkConfigError 'The option .* does not exist. Definition values:\n\s*- In .*: true' config.enable ./define-enable.nix +checkConfigOutput '^1$' config.bare-submodule.nested ./declare-bare-submodule.nix ./declare-bare-submodule-nested-option.nix +checkConfigOutput '^2$' config.bare-submodule.deep ./declare-bare-submodule.nix ./declare-bare-submodule-deep-option.nix +checkConfigOutput '^42$' config.bare-submodule.nested ./declare-bare-submodule.nix ./declare-bare-submodule-nested-option.nix ./declare-bare-submodule-deep-option.nix ./define-bare-submodule-values.nix +checkConfigOutput '^420$' config.bare-submodule.deep ./declare-bare-submodule.nix ./declare-bare-submodule-nested-option.nix ./declare-bare-submodule-deep-option.nix ./define-bare-submodule-values.nix + # Check integer types. # unsigned checkConfigOutput '^42$' config.value ./declare-int-unsigned-value.nix ./define-value-int-positive.nix diff --git a/lib/tests/modules/declare-bare-submodule-deep-option.nix b/lib/tests/modules/declare-bare-submodule-deep-option.nix new file mode 100644 index 00000000000..06ad1f6e0a5 --- /dev/null +++ b/lib/tests/modules/declare-bare-submodule-deep-option.nix @@ -0,0 +1,10 @@ +{ lib, ... }: +let + inherit (lib) mkOption types; +in +{ + options.bare-submodule.deep = mkOption { + type = types.int; + default = 2; + }; +} diff --git a/lib/tests/modules/declare-bare-submodule-nested-option.nix b/lib/tests/modules/declare-bare-submodule-nested-option.nix new file mode 100644 index 00000000000..009dd4d6cb3 --- /dev/null +++ b/lib/tests/modules/declare-bare-submodule-nested-option.nix @@ -0,0 +1,18 @@ +{ lib, ... }: +let + inherit (lib) mkOption types; +in +{ + options.bare-submodule = mkOption { + type = types.submoduleWith { + modules = [ + { + options.nested = mkOption { + type = types.int; + default = 1; + }; + } + ]; + }; + }; +} diff --git a/lib/tests/modules/declare-bare-submodule.nix b/lib/tests/modules/declare-bare-submodule.nix new file mode 100644 index 00000000000..298c71e3ca0 --- /dev/null +++ b/lib/tests/modules/declare-bare-submodule.nix @@ -0,0 +1,12 @@ +{ lib, ... }: +let + inherit (lib) mkOption types; +in +{ + options.bare-submodule = mkOption { + type = types.submoduleWith { + modules = [ ]; + }; + default = {}; + }; +} diff --git a/lib/tests/modules/define-bare-submodule-values.nix b/lib/tests/modules/define-bare-submodule-values.nix new file mode 100644 index 00000000000..00ede929ee6 --- /dev/null +++ b/lib/tests/modules/define-bare-submodule-values.nix @@ -0,0 +1,4 @@ +{ + bare-submodule.nested = 42; + bare-submodule.deep = 420; +} diff --git a/lib/types.nix b/lib/types.nix index 3fcac9c31b3..51046c2c31b 100644 --- a/lib/types.nix +++ b/lib/types.nix @@ -642,6 +642,11 @@ rec { else throw "A submoduleWith option is declared multiple times with conflicting shorthandOnlyDefinesConfig values"; }; }; + } // { + # Submodule-typed options get special treatment in order to facilitate + # certain migrations, such as the addition of freeformTypes onto + # existing option trees. + isSubmodule = true; }; # A value from a set of allowed ones. -- cgit 1.4.1