nixpkgs/nixos/doc/manual/development/option-def.section.md
Naïm Favier e8927c46b8
nixos/doc: document mkOrder and friends
Add a section on ordering option definitions.

Also mention `mkDefault` in the section on `mkOverride`.

Clarify the code a bit by renaming `defaultPriority` to
`defaultOverridePriority` and introducing `defaultOrderPriority`.
2022-12-02 14:15:24 +01:00

3.3 KiB

Option Definitions

Option definitions are generally straight-forward bindings of values to option names, like

config = {
  services.httpd.enable = true;
};

However, sometimes you need to wrap an option definition or set of option definitions in a property to achieve certain effects:

Delaying Conditionals

If a set of option definitions is conditional on the value of another option, you may need to use mkIf. Consider, for instance:

config = if config.services.httpd.enable then {
  environment.systemPackages = [ ... ];
  ...
} else {};

This definition will cause Nix to fail with an "infinite recursion" error. Why? Because the value of config.services.httpd.enable depends on the value being constructed here. After all, you could also write the clearly circular and contradictory:

config = if config.services.httpd.enable then {
  services.httpd.enable = false;
} else {
  services.httpd.enable = true;
};

The solution is to write:

config = mkIf config.services.httpd.enable {
  environment.systemPackages = [ ... ];
  ...
};

The special function mkIf causes the evaluation of the conditional to be "pushed down" into the individual definitions, as if you had written:

config = {
  environment.systemPackages = if config.services.httpd.enable then [ ... ] else [];
  ...
};

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.

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.

Ordering Definitions

It is also possible to influence the order in which the definitions for an option are merged by setting an order priority with mkOrder. The default order priority is 1000. The functions mkBefore and mkAfter are equal to mkOrder 500 and mkOrder 1500, respectively. As an example,

hardware.firmware = mkBefore [ myFirmware ];

This definition ensures that myFirmware comes before other unordered definitions in the final list value of hardware.firmware.

Note that this is different from override priorities: setting an order does not affect whether the definition is included or not.

Merging Configurations

In conjunction with mkIf, it is sometimes useful for a module to return multiple sets of option definitions, to be merged together as if they were declared in separate modules. This can be done using mkMerge:

config = mkMerge
  [ # Unconditional stuff.
    { environment.systemPackages = [ ... ];
    }
    # Conditional stuff.
    (mkIf config.services.bla.enable {
      environment.systemPackages = [ ... ];
    })
  ];