diff options
| author | Johannes Kirschbauer <hsjobeki@gmail.com> | 2025-04-07 14:52:48 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-04-07 14:52:48 +0200 |
| commit | 0196e5372b8b7a282cb3bbe5cbf446617141ce38 (patch) | |
| tree | 64f84d854902ccbf4d12b3eeb2942c1a7b84d244 | |
| parent | python3Packages.pyghmi: init at 1.5.77 (#383866) (diff) | |
| parent | lib.modules: init test for lib.mkDefinition (diff) | |
| download | nixpkgs-0196e5372b8b7a282cb3bbe5cbf446617141ce38.tar.gz | |
lib/modules: Init lib.mkDefinition (#390983)
| -rw-r--r-- | lib/default.nix | 1 | ||||
| -rw-r--r-- | lib/modules.nix | 20 | ||||
| -rwxr-xr-x | lib/tests/modules.sh | 8 | ||||
| -rw-r--r-- | lib/tests/modules/mkDefinition.nix | 71 | ||||
| -rw-r--r-- | nixos/doc/manual/development/option-def.section.md | 62 | ||||
| -rw-r--r-- | nixos/doc/manual/redirects.json | 3 |
6 files changed, 161 insertions, 4 deletions
diff --git a/lib/default.nix b/lib/default.nix index e671fcf9546d..19316addb8cb 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -446,6 +446,7 @@ let fixupOptionType mkIf mkAssert + mkDefinition mkMerge mkOverride mkOptionDefault diff --git a/lib/modules.nix b/lib/modules.nix index a9ddaf7bda02..7716e855ebb2 100644 --- a/lib/modules.nix +++ b/lib/modules.nix @@ -1097,10 +1097,16 @@ let # Process mkMerge and mkIf properties. defs' = concatMap ( m: - map (value: { - inherit (m) file; - inherit value; - }) (addErrorContext "while evaluating definitions from `${m.file}':" (dischargeProperties m.value)) + map ( + value: + if value._type or null == "definition" then + value + else + { + inherit (m) file; + inherit value; + } + ) (addErrorContext "while evaluating definitions from `${m.file}':" (dischargeProperties m.value)) ) defs; # Process mkOverride properties. @@ -1365,6 +1371,11 @@ let inherit contents; }; + /** + Return a definition with file location information. + */ + mkDefinition = args@{ file, value, ... }: args // { _type = "definition"; }; + mkOverride = priority: content: { _type = "override"; inherit priority content; @@ -2095,6 +2106,7 @@ private mkBefore mkChangedOptionModule mkDefault + mkDefinition mkDerivedConfig mkFixStrictness mkForce diff --git a/lib/tests/modules.sh b/lib/tests/modules.sh index e623c0fb55b8..9df6e61797b7 100755 --- a/lib/tests/modules.sh +++ b/lib/tests/modules.sh @@ -673,6 +673,14 @@ checkConfigError 'The option .conflictingPathOptionType. in .*/pathWith.nix. is # types.pathWith { inStore = true; absolute = false; } checkConfigError 'In pathWith, inStore means the path must be absolute' config.impossiblePathOptionType ./pathWith.nix +# mkDefinition +# check that mkDefinition 'file' is printed in the error message +checkConfigError 'Cannot merge definitions.*\n\s*- In .file.*\n\s*- In .other.*' config.conflict ./mkDefinition.nix +checkConfigError 'A definition for option .viaOptionDefault. is not of type .boolean.*' config.viaOptionDefault ./mkDefinition.nix +checkConfigOutput '^true$' config.viaConfig ./mkDefinition.nix +checkConfigOutput '^true$' config.mkMerge ./mkDefinition.nix +checkConfigOutput '^true$' config.mkForce ./mkDefinition.nix + cat <<EOF ====== module tests ====== $pass Pass diff --git a/lib/tests/modules/mkDefinition.nix b/lib/tests/modules/mkDefinition.nix new file mode 100644 index 000000000000..0a807090fa28 --- /dev/null +++ b/lib/tests/modules/mkDefinition.nix @@ -0,0 +1,71 @@ +{ lib, ... }: +let + inherit (lib) + mkOption + mkDefinition + mkOptionDefault + ; +in +{ + imports = [ + { + _file = "file"; + options.conflict = mkOption { + default = 1; + }; + config.conflict = mkDefinition { + file = "other"; + value = mkOptionDefault 42; + }; + } + { + # Check that mkDefinition works within 'config' + options.viaConfig = mkOption { }; + config.viaConfig = mkDefinition { + file = "other"; + value = true; + }; + } + { + # Check mkMerge can wrap mkDefinitions + # Not the other way around + options.mkMerge = mkOption { + type = lib.types.bool; + }; + config.mkMerge = lib.mkMerge [ + (mkDefinition { + file = "a.nix"; + value = true; + }) + (mkDefinition { + file = "b.nix"; + value = true; + }) + ]; + } + { + # Check mkDefinition can use mkForce on the value + # Not the other way around + options.mkForce = mkOption { + type = lib.types.bool; + default = false; + }; + config.mkForce = mkDefinition { + file = "other"; + value = lib.mkForce true; + }; + } + { + # Currently expects an error + # mkDefinition doesn't work on option default + # This is a limitation and might be resolved in the future + options.viaOptionDefault = mkOption { + type = lib.types.bool; + default = mkDefinition { + file = "other"; + value = true; + }; + }; + } + ]; +} diff --git a/nixos/doc/manual/development/option-def.section.md b/nixos/doc/manual/development/option-def.section.md index 227f41d812ff..fddcfef393ae 100644 --- a/nixos/doc/manual/development/option-def.section.md +++ b/nixos/doc/manual/development/option-def.section.md @@ -123,3 +123,65 @@ they were declared in separate modules. This can be done using ]; } ``` + +## Free-floating definitions {#sec-option-definitions-definitions} + +:::{.note} +The module system internally transforms module syntax into definitions. This always happens internally. +::: + +It is possible to create first class definitions which are not transformed _again_ into definitions by the module system. + +Usually the file location of a definition is implicit and equal to the file it came from. +However, when manipulating definitions, it may be useful for them to be completely self-contained (or "free-floating"). + +A free-floating definition is created with `mkDefinition { file = ...; value = ...; }`. + +Preserving the file location creates better error messages, for example when copying definitions from one option to another. + +Other properties like `mkOverride` `mkMerge` `mkAfter` can be used in the `value` attribute but not on the entire definition. + +This is what would work + +```nix +mkDefinition { + value = mkForce 42; + file = "somefile.nix"; +} +``` + +While this would NOT work. + +```nix +mkForce (mkDefinition { + value = 42; + file = "somefile.nix"; +}) +``` + +The following shows an example configuration that yields an error with the custom position information: + +```nix +{ + _file = "file.nix"; + options.foo = mkOption { + default = 13; + }; + config.foo = lib.mkDefinition { + file = "custom place"; + # mkOptionDefault creates a conflict with the option foo's `default = 1` on purpose + # So we see the error message below contains the conflicting values and different positions + value = lib.mkOptionDefault 42; + }; +} +``` + +evaluating the module yields the following error: + +``` +error: Cannot merge definitions of `foo'. Definition values: +- In `file.nix': 13 +- In `custom place': 42 +``` + +To set the file location for all definitions in a module, you may add the `_file` module syntax attribute, which has a similar effect to using `mkDefinition` on all definitions in the module, without the hassle. diff --git a/nixos/doc/manual/redirects.json b/nixos/doc/manual/redirects.json index 65520e2afb7d..ab86ee58dda7 100644 --- a/nixos/doc/manual/redirects.json +++ b/nixos/doc/manual/redirects.json @@ -1664,6 +1664,9 @@ "sec-option-definitions-merging": [ "index.html#sec-option-definitions-merging" ], + "sec-option-definitions-definitions": [ + "index.html#sec-option-definitions-definitions" + ], "sec-assertions": [ "index.html#sec-assertions" ], |
