Skip to content

Option dependencies

Now we have some basic knowledge of how to create an option and set its value with a config. But previously, we created an option that was both the input and output. What if we want several inputs and a output that is some combination of those inputs?

That is completely possible!

Options can depend on other options, making it possible to build more useful abstractions. To make option values available to a module, the arguments of the function declaring the module must include the config attribute.

Note

Lazy evaluation in the Nix language allows the module system to make a value available in the config argument passed to the module which defines that value.

options.nix
{
  lib,
  config,
  ...
}: let
  cfg = config;
in {
  options = {
    name = lib.mkOption {
      type = lib.types.str;
    };
    title = lib.mkOption {
      type = lib.types.str;
    };
    origin = lib.mkOption {
      type = lib.types.str;
    };
    greeting = lib.mkOption {
      type = lib.types.str;
    };
  };

  config = {
    greeting = ''
      Hello
      My name is ${cfg.name}.
      I am a ${cfg.title}.
      I am from ${cfg.origin}.'';
  };
}

As you can see, we have added config to the function argument of the options.nix module. We have also declared name, title, and origin which are all str types. Those will be our inputs that will we configure.

We have also declared an option, greeting, which is also a str type. In the same file, we have defined its value using the final configuration values of the other options.

Note

The line cfg = config; is not strictly necessary. We could have used the config argument wherever cfg was used. However, it does help mentally separate the idea of the config argument and the config attribute. If you explore the NixOS modules, you will find more interesting examples of this pattern.

Warning

The config argument is not the same as the config attribute:

  • The config argument holds the result of the module system’s lazy evaluation, which takes into account all modules passed to evalModules and their imports.
  • The config attribute of a module exposes that particular module’s option values to the module system for evaluation.

So now we just set values for name, title, and origin in config.nix like we have done before.

config.nix
{...}: {
  config = {
    name = "Boaty McBoatface";
    title = "Autonomous Underwater Vehicle";
    origin = "England";
  };
}

Setup an eval.nix to evaluate our modules and return the config attribute.

eval.nix
{pkgs}:
(
  pkgs.lib.evalModules {
    modules = [
      ./options.nix
      ./config.nix
    ];
  }
)
.config

Create a run.sh run script to evalute the eval.nix file.

run.sh
nix eval -f eval.nix \
    --apply 'x: x {pkgs = import <nixpkgs> {};}' \
    --json | nix run nixpkgs#jq -- .

And if we run the script (./run.sh), we have our configuration.

{
  "greeting": "Hello\nMy name is Boaty McBoatface.\nI am a Autonomous Underwater Vehicle.\nI am from England.",
  "name": "Boaty McBoatface",
  "origin": "England",
  "title": "Autonomous Underwater Vehicle"
}

Notice that it gave use the entire configuration, which is to be expected. What if we just want greeting?

We can specify to only return the greeting attribute in our new eval-greeting.nix file.

eval-greeting.nix
{pkgs}:
(
  pkgs.lib.evalModules {
    modules = [
      ./options.nix
      ./config.nix
    ];
  }
)
.config
.greeting

Update our run script as shown in run-greeting.sh.

run-greeting.sh
nix eval -f eval-greeting.nix \
    --apply 'x: x {pkgs = import <nixpkgs> {};}' \
    --json | nix run nixpkgs#jq -- -r

And if we run the new script (./run-greeting.sh), we have just the configuration of greeting.

Hello
My name is Boaty McBoatface.
I am a Autonomous Underwater Vehicle.
I am from England.