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

Support merging/overriding for unspecified attr-set options within freeformTypes (for e.g. nixpkgs.config) #194056

Open
Ma27 opened this issue Oct 2, 2022 · 2 comments
Labels
6.topic: module system About "NixOS" module system internals

Comments

@Ma27
Copy link
Member

Ma27 commented Oct 2, 2022

In #80582 I tried to implement merging/overriding for nixpkgs.config, i.e. to make

{
  nixpkgs = mkMerge [
    { config.allowUnfree = true; }
    { config.allowUnfree = mkForce false; }
  ];
}

possible. We decided to close this back then because it was rather messy and later on freeformType was born. With this, you can create submodules with unspecified options (rather than using types.attrs for options such as nixpkgs.config).

Now it'd be theoretically possible to re-implement options.nixpkgs.config like this (the examples below assume this definition):

{
  options.nixpkgs.config = mkOption {
    type = types.submodule {
      freeformType = types.attrsOf types.oneOf (types.bool types.attrs);
      options.allowUnfree = mkOption {
        type = types.bool;
        default = false;
      };
    };
  };
}

This works perfectly fine with specified options or unspecified options that are not attribute-sets (e.g. nixpkgs.config.cudaSupport).

However, it's not possible to do merging&overriding for unspecified options that are attribute-sets. With the option definition from above, you'd get the following (wrong) result for unspecified options:

(evalModules {
  modules = [
    { /* def for `options.nixpkgs.config` from above here */ }
    { nixpkgs.config.vim.gui = true; }
    { nixpkgs.config.vim.gui = mkForce false; }
  ];
}).config.nixpkgs.config

results in

$ nix-instantiate foo.nix --eval --strict
{ allowUnfree = false; vim = { gui = { _type = "override"; content = false; priority = 50; }; }; }

I guess that this is because it cannot be distinguished between vim.gui being an attr-set and vim.gui containing info about an override.

One might argue that _type is internal and thus it can be assumed to be the result of an override (or any other module-system operation). I think it might be possible to recursively apply pushDownProperties onto unspecified freeform options, but I'm not really sure if that's a good idea, so I figured I'd leave it open for discussion if we actually want to implement support for overriding/merging for unspecified attr-sets within freeformTypes (and switch nixpkgs.config to such a definition).

cc @infinisil

@Ma27 Ma27 added the 6.topic: module system About "NixOS" module system internals label Oct 2, 2022
@infinisil
Copy link
Member

I think all you need to make it work is

freeformType = types.attrsOf types.anything

@Ma27
Copy link
Member Author

Ma27 commented Oct 2, 2022

Oh dang, thanks, that works fine %)

I guess I'll just try it against nixpkgs.config and file a PR later.

Ma27 added a commit to Ma27/nixpkgs that referenced this issue Oct 13, 2022
Closes NixOS#194056
Alternative implementation to NixOS#80582

`nixpkgs.config` is now a freeform-type which imports the
`<nixpkgs/pkgs/top-level/config.nix>`-module. This brings the following
benefits:

* Proper merging (to a certain degree, more details later) of several
  `nixpkgs.config` declarations.

* `allowUnfree` and friends appear in the NixOS manual.

To be precise, this means that something like this is now possible:

    {
      nixpkgs = mkMerge [
        { allowUnfree = false; }
        { allowUnfree = mkForce true; }
      ];
    }

Previously this would lead to a bogus attr-set, namely

    { _type = "override"; content = true; priority = 50; }

since no merging for this option-type was implemented.

This however has certain limitations. For instance

    nixpkgs.config.allowUnfree = mkForce false;

works fine whereas

    nixpkgs.config.virtualbox.enableExtensionPack = mkForce true;

doesn't. This is because we use `types.raw` inside the config-module for
`nixpkgs` which doesn't merge undeclared attribute-sets (such as
`virtualbox`). This is because we don't know if any attribute-set is
actually mergeable, for further details see the previous discussion[1].

A basic type has been implemented for the (actually deprecated)
mechanisms `packageOverrides`/`perlPackageOverrides`. This one basically
applies the `pkgs` argument onto each definition of this function, so
e.g. the following expression

    {
      nixpkgs.config = mkMerge [
        { packageOverrides = pkgs: { randomattr = 23; }; }
        { packageOverrides = pkgs: { randomattr = pkgs.randomattr + 19; }; }
      ];
    }

adds an attribute `randomattr` of value `42` to `_module.args.pkgs`.

[1] NixOS#194207 (comment)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
6.topic: module system About "NixOS" module system internals
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants