Unverified Commit 0196e537 authored by Johannes Kirschbauer's avatar Johannes Kirschbauer Committed by GitHub
Browse files

lib/modules: Init lib.mkDefinition (#390983)

parents 2025f72a 33daa3f4
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -446,6 +446,7 @@ let
        fixupOptionType
        mkIf
        mkAssert
        mkDefinition
        mkMerge
        mkOverride
        mkOptionDefault
+16 −4
Original line number Diff line number Diff line
@@ -1097,10 +1097,16 @@ let
        # Process mkMerge and mkIf properties.
        defs' = concatMap (
          m:
          map (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))
              }
          ) (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
+8 −0
Original line number Diff line number Diff line
@@ -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
+71 −0
Original line number Diff line number Diff line
{ 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;
        };
      };
    }
  ];
}
+62 −0
Original line number Diff line number Diff line
@@ -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.
Loading