Preferred method for defining optional features in packages

Hello, I see in the nixpkgs manual that the (finalAttrs: {}) form of mkDerivation can be used to make derivation attributes which work (correctly) as overrideable feature switches.

So package authors can add enableFoo type switches as either mkDerivation attributes, or as traditional callPackage arguments. The former can be overridden with drv.overrideAttrs and the latter with drv.override.

Are there reasons to prefer one way over the other?

Thanks

This is a very interesting question, one I’ve been wrestling with while experimenting with the different ways to eliminate .override and have .overrideAttrs take over its duties.

One general class of problems is when you have an expression like

{ lib
, stdenv
, foo
, someOption ? throw "you forgot to specify someOption's value!"
}:

foo someOption (stdenv.mkDerivation {
  # ...
})

Here, you can’t just inherit someOption inside the derivation and use overrideAttrs if foo is strict in its someOption argument (which it typically is, in the interesting cases).

Also, for flags that are understood by more than one package (e.g. pulseSupport), having them be package arguments rather than attributes is nice, because it lets you set those flags system-wide in an overlay.

For me, the package’s function attrs are its public interface. A package declares assumptions such as packages it depends on and/or configuration switches here. The user can then change these assumptions based on their preferences and needs. If they want the dependency foo to be slightly different (adjusting its public interface or using a different version), the could pass a new foo.

Derivation attrs are the (private) implementation details of a derivation. You can meddle with them in an additive manner using overrideAttrs but they’re not part of the interface. Well, most attributes are. Some attributes are arguably part of the interface such as src. You could pass an “ABI-compatible” src (i.e. unstable/git version) here and it’d still work.
Most attrs truly are implementation detail though and cannot be expected to be stable.

Though honestly, I’d probably rather see src moved to the function args; explicitly making it part of the interface.

6 Likes

As far as I can tell, the jury is still out on that issue. @infinisil and the Nixpkgs Architecture Team should have an authoritative answer on the current state of affairs.

I’m posting such comments here and there, because most answers to such tricky questions are usually in the style of “in my opinion…” and “it feels like…”, and it does happen that they’re factually wrong.

As this thread shows, there is some ambiguity: dependencies and configuration options coexist in the outer function interface, and also in the interface to mkDerivation. There was discussion about migrating Nixpkgs towards exposing build configuration through the module system in order to clarify these interfaces, and dream2nix is an independent, actively developed implementation of that idea. The last thing I know is that it would incur a significant evaluation performance overhead due to how the module system works, and it looked to me that it was prohibitive at Nixpkgs scale, but I don’t know any details.

I don’t think I or the architecture team should be authorative, but my opinion is the same as @Atemu’s. In the future we can hopefully improve this distinction between private vs public interface, but for now using .overrideAttrs vs .override is close enough.

3 Likes

Thanks all for your thoughts. This is helpful to know.