Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lib: add option parameter for default value priority #296979

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
2 changes: 1 addition & 1 deletion lib/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ let
mergeModules' mergeOptionDecls mergeDefinitions
pushDownProperties dischargeProperties filterOverrides
sortProperties fixupOptionType mkIf mkAssert mkMerge mkOverride
mkOptionDefault mkDefault mkImageMediaOverride mkForce mkVMOverride
mkOptionDefault mkDefault mkBaseline mkImageMediaOverride mkForce mkVMOverride
mkFixStrictness mkOrder mkBefore mkAfter mkAliasDefinitions
mkAliasAndWrapDefinitions fixMergeModules mkRemovedOptionModule
mkRenamedOptionModule mkRenamedOptionModuleWith
Expand Down
62 changes: 51 additions & 11 deletions lib/modules.nix
Original file line number Diff line number Diff line change
Expand Up @@ -817,9 +817,11 @@ let
evalOptionValue = loc: opt: defs:
let
# Add in the default value for this option, if any.
defs' =
(optional (opt ? default)
{ file = head opt.declarations; value = mkOptionDefault opt.default; }) ++ defs;
defs' = (optional (opt ? default) {
file = head opt.declarations;
value = mkOverride (opt.defaultPriority or priorities.optionDefault) opt.default;
})
++ defs;

# Handle properties, check types, and merge everything together.
res =
Expand Down Expand Up @@ -1053,14 +1055,49 @@ let
inherit priority content;
};

mkOptionDefault = mkOverride 1500; # priority of option defaults
mkDefault = mkOverride 1000; # used in config sections of non-user modules to set a default
defaultOverridePriority = 100;
mkImageMediaOverride = mkOverride 60; # image media profiles can be derived by inclusion into host config, hence needing to override host config, but do allow user to mkForce
mkForce = mkOverride 50;
mkVMOverride = mkOverride 10; # used by ‘nixos-rebuild build-vm’

defaultPriority = warnIf (oldestSupportedReleaseIsAtLeast 2305) "lib.modules.defaultPriority is deprecated, please use lib.modules.defaultOverridePriority instead." defaultOverridePriority;
# This set contains the conventional override priority values that can be
# applied to definitions using `mkOverride`. Pre-applied `mkOverride`
# functions following the pattern of `mkPriorityName` also exist for
# convenience.
priorities = {
# Priority of options' default values. They get overridden by everything.
optionDefault = 1500;
# This priority exists to facilitate providing default values inside of
# config sections. These may get overridden if they are set with a greater
# priority definition elsewhere in much the same way as the optionDefault
# priority would but do override optionDefault values. This is *not* the
# default priority assigned to values when you don't explicitly provide one.
configDefault = 1000;
# This is the priority which definitions get assigned by default in config
# sections if you don't explicitly provide an override priority. This is the
# true "default" priority. The other priorities scale around this value.
baseline = 100;
# Image media profiles need to override host config in some regards but
# should still allow the user to forcibly override them.
mediaOverride = 60;
# Forcefully override a value
force = 50;
# VM profiles (i.e. `nixos-rebuild build-vm`) need to override some
# definitions regardless of the user's config.
vmOverride = 10;
};
mkOptionDefault = mkOverride priorities.optionDefault;
mkConfigDefault = mkOverride priorities.configDefault;
mkBaseline = mkOverride priorities.baseline;
mkImageMediaOverride = mkOverride priorities.mediaOverride;
mkForce = mkOverride priorities.force;
mkVMOverride = mkOverride priorities.vmOverride;

# Legacy aliases
defaultOverridePriority =
warnIf (oldestSupportedReleaseIsAtLeast 2505)
"`lib.modules.defaultOverridePriority` is deprecated, please use `lib.modules.priorities.baseline` instead."
priorities.baseline;
defaultPriority = warnIf (oldestSupportedReleaseIsAtLeast 2305) "lib.modules.defaultPriority is deprecated, please use lib.modules.priorities.baseline instead." lib.modules.priorities.baseline;
# Usage of this function is extremely wide-spread. While the new name is
# clearer and should be preferred, there isn't any great need to force people
# who are used to the old name to change either.
mkDefault = mkConfigDefault;

mkFixStrictness = warn "lib.mkFixStrictness has no effect and will be removed. It returns its argument unmodified, so you can just remove any calls." id;

Expand Down Expand Up @@ -1610,8 +1647,10 @@ private //
mkAliasOptionModule
mkAliasOptionModuleMD
mkAssert
mkBaseline
mkBefore
mkChangedOptionModule
mkConfigDefault
mkDefault
mkDerivedConfig
mkFixStrictness
Expand All @@ -1627,6 +1666,7 @@ private //
mkRenamedOptionModule
mkRenamedOptionModuleWith
mkVMOverride
priorities
setDefaultModuleLocation
sortProperties;
}
2 changes: 2 additions & 0 deletions lib/options.nix
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ rec {
{
# Default value used when no definition is given in the configuration.
default ? null,
# Which priority to assign to the default value. This can be used to control whether the default is be merged or overridden when the option is set.
defaultPriority ? lib.modules.priorities.optionDefault,
# Textual representation of the default, for the manual.
defaultText ? null,
# Example value used in the manual.
Expand Down
4 changes: 4 additions & 0 deletions lib/tests/modules.sh
Original file line number Diff line number Diff line change
Expand Up @@ -586,6 +586,10 @@ checkConfigOutput '^38|27$' options.submoduleLine38.declarationPositions.1.line
# nested options work
checkConfigOutput '^34$' options.nested.nestedLine34.declarationPositions.0.line ./declaration-positions.nix

# Set an option with `default = [ "foo" ]` with `= [ "bar" ]` again with varying defaultPriorities
checkConfigOutput '^\["bar"\]$' config.values.default ./defaultPriority.nix
checkConfigOutput '^\["foo","bar"\]$' config.values.baseline ./defaultPriority.nix
checkConfigOutput '^\["foo"\]$' config.values.force ./defaultPriority.nix

cat <<EOF
====== module tests ======
Expand Down
24 changes: 24 additions & 0 deletions lib/tests/modules/defaultPriority.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{ lib, config, ... }:

{
options.values = {
default = lib.mkOption {
default = [ "foo" ];
# No priority given
};
baseline = lib.mkOption {
default = [ "foo" ];
defaultPriority = lib.modules.priorities.baseline;
};
force = lib.mkOption {
default = [ "foo" ];
defaultPriority = lib.modules.priorities.force;
};
};

config.values = {
default = [ "bar" ];
baseline = [ "bar" ];
force = [ "bar" ];
};
}
2 changes: 1 addition & 1 deletion lib/tests/modules/test-mergeAttrDefinitionsWithPrio.nix
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ in
};
config.result =
assert defs.default.highestPrio == (lib.mkDefault (assertLazy __curPos)).priority;
assert defs.regular.highestPrio == lib.modules.defaultOverridePriority;
assert defs.regular.highestPrio == lib.modules.priorities.baseline;
assert defs.force.highestPrio == (lib.mkForce (assertLazy __curPos)).priority;
true;
}
9 changes: 9 additions & 0 deletions nixos/doc/manual/development/option-declarations.section.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,15 @@ The function `mkOption` accepts the following arguments.
a plain English description in [Nixpkgs-flavored Markdown](
https://nixos.org/nixpkgs/manual/#sec-contributing-markup) format.

`defaultPriority`

: The priority applied to the default value when merging. The conventional
priorities are available in `lib.modules.priorities`, e.g.
`lib.modules.priorities.force` as in `lib.mkForce`. This is most useful to
make the values set in `config` merge with the option's default value rather
than overriding it by setting `defaultPriority =
lib.modules.priorities.baseline;` but any priority may be passed.

`example`

: An example value that will be shown in the NixOS manual.
Expand Down
38 changes: 30 additions & 8 deletions nixos/doc/manual/development/option-def.section.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,21 +68,43 @@ be "pushed down" into the individual definitions, as if you had written:

## Setting Priorities {#sec-option-definitions-setting-priorities}

A module can override the definitions of an option in other modules by
setting an *override priority*. All option definitions that do not have the lowest
priority value are discarded. By default, option definitions have
priority 100 and option defaults have priority 1500.
You can specify an explicit priority by using `mkOverride`, e.g.
A module can override the definitions of an option in other modules by setting
an *override priority*. All option definitions that do not have the lowest
priority value are discarded. Definitions that have the same override priority
are merged according to the option's type's merge rules.

By default, defining an option's value will assign
priority 100 (`baseline`) and options' default values have priority 1500
(`optionDefault`). Thereby, an options' default value is overridden by the
config value (this may be changed by adjusting the option's `defaultPriority`).

These and further conventional override priority values are available in
`lib.modules.priorities`. Convenience functions such as `lib.mkConfigDefault`,
`lib.mkForce` etc. also exist to assign the priorities to a definition.

```nix
{
services.openssh.enable = mkForce false;
}
```

This definition causes all other definitions with priorities above 50 (such as
those with the baseline priority) to be discarded. If another module were to
define `services.openssh.enable = true`, the option's value would remain false
because the `mkForce`'d `false` definition's override priority value is lower
than the baseline value (100).

You can also specify a custom priority value outside of the conventional values
through `mkOverride`:

```nix
{
services.openssh.enable = mkOverride 10 false;
}
```

This definition causes all other definitions with priorities above 10 to
be discarded. The function `mkForce` is equal to `mkOverride 50`, and
`mkDefault` is equal to `mkOverride 1000`.
This definition causes all other definitions with priorities above 10 to be
discarded.

## Ordering Definitions {#sec-option-definitions-ordering}

Expand Down
2 changes: 1 addition & 1 deletion nixos/lib/testing/driver.nix
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ in
_module.args = {
hostPkgs =
# Comment is in nixos/modules/misc/nixpkgs.nix
lib.mkOverride lib.modules.defaultOverridePriority
lib.mkOverride lib.modules.priorities.baseline
config.hostPkgs.__splicedPackages;
};

Expand Down
4 changes: 2 additions & 2 deletions nixos/modules/misc/nixpkgs.nix
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,7 @@ in
# which is somewhat costly for Nixpkgs. With an explicit priority, we only
# evaluate the wrapper to find out that the priority is lower, and then we
# don't need to evaluate `finalPkgs`.
lib.mkOverride lib.modules.defaultOverridePriority finalPkgs.__splicedPackages;
lib.mkOverride lib.modules.priorities.baseline finalPkgs.__splicedPackages;
};

assertions =
Expand All @@ -367,7 +367,7 @@ in
# We set it with default priority and it can not be merged, so if the
# pkgs module argument has that priority, it's from us.
(lib.modules.mergeAttrDefinitionsWithPrio options._module.args).pkgs.highestPrio
== lib.modules.defaultOverridePriority
== lib.modules.priorities.baseline
# Although, if nixpkgs.pkgs is set, we did forward it, but we did not construct it.
&& !opt.pkgs.isDefined;
in
Expand Down
Loading