# Pre-RFC: Standardization of feature parameters

**URL:** https://discourse.nixos.org/t/pre-rfc-standardization-of-feature-parameters/38104
**Category:** RFCs
**Created:** [January 11, 2024, 3:42am UTC](https://discourse.nixos.org/t/pre-rfc-standardization-of-feature-parameters/38104 "2024-01-11T03:42:58Z")
**Posts on this page:** 20
**Page:** 3

<div class="post-metadata">

### Author: ![KAction](https://discourse.nixos.org/user_avatar/discourse.nixos.org/kaction/32/3888_2.png) [@KAction](https://discourse.nixos.org/u/KAction)
#### Post date: [January 12, 2024, 1:11am UTC](https://discourse.nixos.org/t/pre-rfc-standardization-of-feature-parameters/38104/42 "2024-01-12T01:11:28Z")

</div>

> [@Atemu](#):
>
> - This logic already exists/should exist: You’d effectively be re-implementing the configure script.

Yeah, I see your point, but by this logic we shouldn’t expose feature parameters at all, and say that overriding `patchPhase` and `configureFlags` is the way.

---

<div class="post-metadata">

### Author: ![KAction](https://discourse.nixos.org/user_avatar/discourse.nixos.org/kaction/32/3888_2.png) [@KAction](https://discourse.nixos.org/u/KAction)
#### Post date: [January 12, 2024, 1:12am UTC](https://discourse.nixos.org/t/pre-rfc-standardization-of-feature-parameters/38104/43 "2024-01-12T01:12:56Z")

</div>

> [@Atemu](#):
>
> I think we should explicitly accept that stupid configurations will be possible and that we should not put significant effort towards actively preventing the user from shooting themselves in the foot. If that’s what they want to do, that’s on them.

Alright, you convinced me. I’ll change it to `MAY put asserts about known-to-be-stupid configurations`.

---

<div class="post-metadata">

### Author: ![KAction](https://discourse.nixos.org/user_avatar/discourse.nixos.org/kaction/32/3888_2.png) [@KAction](https://discourse.nixos.org/u/KAction)
#### Post date: [January 12, 2024, 1:15am UTC](https://discourse.nixos.org/t/pre-rfc-standardization-of-feature-parameters/38104/44 "2024-01-12T01:15:51Z")

</div>

> [@7c6f434c](#):
>
> So if I want to override `openssl` to use a patched version, I need to pass it twice? Sounds suboptimal, too.

Right. Then we can bless `lib.oneOf providerSSL [null, "openssl", "libressl"]`, like some packages already do.

---

<div class="post-metadata">

### Author: ![Atemu](https://discourse.nixos.org/user_avatar/discourse.nixos.org/atemu/32/10383_2.png) [@Atemu](https://discourse.nixos.org/u/Atemu)
#### Post date: [January 12, 2024, 1:20am UTC](https://discourse.nixos.org/t/pre-rfc-standardization-of-feature-parameters/38104/45 "2024-01-12T01:20:37Z")

</div>

Ah, my mind skipped that. I should go to bed.

It still doesn’t strike me as a great pattern. It certainly won’t scale.  
In this context, I propose the ffmpeg-test: How much would you dread implementing this for ffmpeg’s forest of configure flags?  
A subset of them can be observed in the aforementioned file.

It’s also generally not something that I think we should spend significant resources on.

> [@KAction](#):
>
> by this logic we shouldn’t expose feature parameters at all, and say that overriding `patchPhase` and `configureFlags` is the way.

Partially correct. Providing these flags in the first place is optional:

> [@KAction](#):
>
> Derivation function MAY expose compile-time boolean options

There is a point to exposing such parameters though and that is abstraction; boolean flags are a helluvalot easier to work with than lists of strings.

If you as a maintainer do choose to expose config flags, standardising their names and format is this RFC’s core goal as I see it and I approve of that. We should keep it at that.

---

<div class="post-metadata">

### Author: ![KAction](https://discourse.nixos.org/user_avatar/discourse.nixos.org/kaction/32/3888_2.png) [@KAction](https://discourse.nixos.org/u/KAction)
#### Post date: [January 12, 2024, 1:36am UTC](https://discourse.nixos.org/t/pre-rfc-standardization-of-feature-parameters/38104/46 "2024-01-12T01:36:05Z")

</div>

> [@Atemu](#):
>
> It still doesn’t strike me as a great pattern. It certainly won’t scale.  
> In this context, I propose the ffmpeg-test: How much would you dread implementing this for ffmpeg’s forest of configure flags?  
> A subset of them can be observed in the aforementioned file.

From what I glanced at ffmpeg flags you listed, the only alternatives are `libva` vs `libva-small`. Probably out of ignorance, but I am not scared.

---

<div class="post-metadata">

### Author: ![KAction](https://discourse.nixos.org/user_avatar/discourse.nixos.org/kaction/32/3888_2.png) [@KAction](https://discourse.nixos.org/u/KAction)
#### Post date: [January 12, 2024, 5:40pm UTC](https://discourse.nixos.org/t/pre-rfc-standardization-of-feature-parameters/38104/47 "2024-01-12T17:40:20Z")

</div>

For some reason, I can’t edit post on top, so re-posting revised version here. Two more points in changelog.

* * *

## feature: feature\_parameter\_names start-date: 2024-01-10 author: Dmitry Bogatov co-authors: (find a buddy later to help out with the RFC) shepherd-team: (names, to be nominated and accepted by RFC steering committee) shepherd-leader: (name to be appointed by RFC steering committee) related-issues: (will contain links to implementation PRs)

# Summary

Nixpkgs usually exports compile-time options of the upstream build system  
through the named parameters of derivation function, but does it inconsistent  
way.

# Motivation

Developer interface for building environment with specific features enabled and  
disabled is more complicated and requires more knowledge of implementation  
details compared to already existing systems.

For example, Gentoo Linux has exhaustive list of [all known configuration  
flags](https://www.gentoo.org/support/use-flags) and has means to  
programmatically query what flags are available for particular package. Nixpkgs  
has neither, and this RFC strives to rectify it.

# Detailed design

1. Derivation function MAY expose compile-time boolean options of the upstream  
build system to enable or disable a feature using parameters named either as  
`enableX` or `withX`, consistent with Autoconf naming convention.

2. If compile-time feature requires build and runtime dependency on package  
`libfoo`, corresponding feature parameter MUST match regular expression  
`^with[^a-z]`. See guidelines below for choosing name of feature parameter.

3. If compile-time feature does not require any extra build dependencies,  
corresponding feature parameter MUST have name matching `^enable[^a-z]` and  
SHOULD correspond to the upstream naming.

4. If upstream build features and build dependencies do not map one-to-one,  
then one `with` feature parameter SHOULD be added for every build dependecy  
and one `enable` feature SHOULD be added for every upstream build feature  
intended to be optional. Assertions to preclude incoherent feature  
configurations MAY be added.

5. These rules are to be enforced by static code analyse linter. Since no  
static code analyzis is perfect, it shall have support for inhibiting  
warnings in individual cases that do not fit into general scheme.

6. Parameter names matching `^(enable|with)` regular expression MUST not be  
used for any other purpose. In particular, they always must be boolean.

7. Derivation function MAY expose compile-time string or numeric options of the  
upstream build system using feature parameters that MUST match `^conf[^a-z]`  
regular expression, e.g `confDefaultMaildir`.

8. Due overwhelming amount of possible combinations of feature flags for some  
packages, nixpkgs maintainer is not expected to test or maintain them all,  
but SHOULD accept provided technically sound contributions related to  
configurations with non-default feature flags.

9. Due overwhelming amount of possible combinations of feature flags for some  
packages, only configurations that has name in package set (e.g `emacs-nox`)  
shall be built on CI.

## The migration process.

Named parameters are part of the programming interface, and renaming them is a  
breaking change. As such, renaming of the parameters is done in following way:

1. Following function is added into `lib` set:

```auto
let renamed = { oldName, newName, sunset, oldValue }: newValue:
   let warning = builtins.concatStringsSep " "
      [ "Feature flag"
      , oldName
      , "is renamed to"
      , newName
      , "; old name will no longer be available in nixpkgs="
      , sunset
      , "."
      ]
   in lib.warnIf (value != null) warning (if (value != null) then value else newValue);

```

Starting with following function:

```auto
{ lib
, stdenv
, nonCompliantFoo ? true
}:

# uses nonCompliantFoo
stdenv.mkDerivation { ... }

```

First step of migration is to replace it with the following:

```auto
{ lib
, stdenv
, nonCompliantFoo ? null
, enableFoo ? lib.renamed {
    oldName = "nonCompliantFoo";
    newName = "enableFoo";
    sunset = "25.11";
    value = nonCompliantFoo;
  } true
}:

# uses enableFoo
stdenv.mkDerivation { ... }

```

and after grace period of two releases, any mentions of `nonCompliantFoo` are  
removed, and function becomes:

```auto
{ lib
, stdenv
, enableFoo ? true
}:

# uses enableFoo
stdenv.mkDerivation { ... }

```

## Feature parameter naming guidelines

1. Feature flags that require single external dependency SHOULD be named after  
that dependency. Prefix `lib` SHOULD be removed. For example,

2. When multiple feature flags require the same build dependency, for example  
derivation has optional support for FTP and HTTP protocols, any of which  
incur dependency on `curl`, and derivation would look like following:

3. Mutually-exclusive build dependencies that provide the same feature are also  
handled with assertions. For example, if derivation has optional SSL support  
that may be provided by multiple libraries, but only one may be used and it  
must be chosen at compilation time, derivation will look like following:

4. When build dependency comes from particular package set, it MAY make sense to  
name feature parameter after it. E.g build dependency on `qt6.qtbase` should  
have `withQt6` feature parameter.

5. If feature parameter name suggested by previous point is too generic,  
package name from the set MAY be included into the feature parameter name.  
Optional dependency on `pythonPackages.pillow` MAY have feature parameter  
`withPythonPillow`.

6. Build dependency on bindings to low-level libraries SHOULD be named after  
underlying library. For example, optional dependecy on `pyqt5` Python  
bindings to `Qt5` library should have `withQt5` feature parameter.

# Examples and Interactions

This is how one can query list of feature parameters that incur extra runtime dependencies:

```auto
$ nix eval --json --impure --expr 'with import ./. {}; builtins.attrNames gnuplot.override.__functionArgs' | jq '.[]| select(startswith("with"))'
"withCaca"
"withLua"
"withQt"
"withTeXLive"
"withWxGTK"

```

I picked the `gnuplot` as example since it is the closest to be compliant with proposed rules.

# Drawbacks

1. The migration process involves renaming feature parameters, so these changes  
will likely conflict with other outstanding changes to the derivation, and potentially  
even with automatic version bumps.

# Alternatives

## Group all feature parameters into separate attrset parameter

Instead of writing

```auto
{ lib, stdenv, enableFoo ? true }:

# uses enableFoo
stdenv.mkDerivation { ... }

```

derivation can be written as

```auto
{ lib, stdenv, features ? { enableFoo = true; }}:

# uses features.enableFoo
stdenv.mkDerivation { ... }

```

It is definitely looks cleaner, but unfortunately hits several limitations of  
the Nix language.

1. Nix language provides way to [introspect function argument names](https://nixos.org/manual/nix/stable/language/builtins.html#builtins-functionArgs),  
but no way to learn their default values. So this approach gives consistency, but no way to query list of derivation feature parameters.

2. Overrides become much more verbose. Simple and ubiquitous

Any approach that tries to pass attribute set to function will have these  
issues. Using existing [config.nix](https://github.com/NixOS/nixpkgs/blob/032116f435e2cca412917a42e6f52a26c1e116a4/pkgs/top-level/config.nix)  
is no different.

## Do nothing

Avoids all the work and drawbacks, but there is no evidence that consistency  
problem will [solve itself evolutionary](https://github.com/NixOS/nixpkgs/pull/234463#issuecomment-1574892207).  
For some build dependencies, we have multiple equally popular feature parameter  
names, and people keep picking random one when adding new packages.

# Prior art

As mentioned in motivation part, the best in class of feature flag  
configuration system is Gentoo Linux:

- [[USE flag index – Gentoo Linux](https://www.gentoo.org/support/use-flags)]

There is not much work I can find about this problem in Nixpkgs other than my  
previous attempts to solve it on package-by-package basis:

- [Naming convention for optional feature flags · Issue #148730 · NixOS/nixpkgs · GitHub](https://github.com/NixOS/nixpkgs/issues/148730)
- [dbus: rename "enableSystemd" to "withSystemd" by KAction · Pull Request #234463 · NixOS/nixpkgs · GitHub](https://github.com/NixOS/nixpkgs/pull/234463)

# Unresolved questions

This RFC makes it possible to introspect feature parameters of particular  
derivation, but still does not provide simple and efficient way to list all  
existing feature parameters.

# Future work

There are other configuration scenarios not covered by this RFC:

- Optional dependencies in shell wrappers (e.g [passage](https://github.com/NixOS/nixpkgs/blob/032116f435e2cca412917a42e6f52a26c1e116a4/pkgs/tools/security/passage/default.nix#L12)).
- Finding way to get list of all existing feature parameters. That can be possibly done by building and distributing the index separately,  
like [nix-index](https://github.com/nix-community/nix-index) does it.

# Changelog

1. Changed wording to not imply that every upstream build system knob SHOULD be  
exported via feature parameters. (Thx: @7c6f434c)

2. Relaxed wording on the name of feature parameters to avoid painting ourselves  
into ugly and non-intuitive names. (Thx: @7c6f434c)

3. Fix typo in regex to be consistent that feature flag name can’t have small  
letter after `with|conf|enable` prefix. (Ths: @don.dfh)

4. Explicitly mention that static code analysis has support for overrides based  
on human judgement call. (Thx: @7c6f434c)

5. Clarify solution scenarios when build inputs and feature flags don’t match  
one-to-one. (Thx: @Atemu, @7c6f434c)

6. Refine the deprecation plan to make sure the warning includes the sunset  
timestamp. (Thx: @pbsds)

7. Add rules about non-boolean feature parameters. (Thx: @Atemu, @pbsds)

8. Set expectations for building and maintaining multiple configurations. (Thx: @pbsds)

9. Removed non-boolean parameters from “Future Work” section.

10. Relaxed requirements for assertions about conflicting flags (Thx: @Atemu)

11. Add guideline so `pythonPackages.pillow` does not get `withPython` feature name. (Thx: @7c6f434c)

---

<div class="post-metadata">

### Author: ![7c6f434c](https://discourse.nixos.org/letter_avatar_proxy/v4/letter/7/8e7dd6/32.png) [@7c6f434c](https://discourse.nixos.org/u/7c6f434c)
#### Post date: [January 12, 2024, 6:07pm UTC](https://discourse.nixos.org/t/pre-rfc-standardization-of-feature-parameters/38104/48 "2024-01-12T18:07:53Z")

</div>

> [@KAction](#):
>
> Build dependency on bindings to low-level libraries SHOULD be named after  
> underlying library. For example, optional dependecy on `pyqt5` Python  
> bindings to `Qt5` library should have `withQt5` feature parameter.

By the way, does this kill a huge chunk of static linting?

---

<div class="post-metadata">

### Author: ![KAction](https://discourse.nixos.org/user_avatar/discourse.nixos.org/kaction/32/3888_2.png) [@KAction](https://discourse.nixos.org/u/KAction)
#### Post date: [January 12, 2024, 6:13pm UTC](https://discourse.nixos.org/t/pre-rfc-standardization-of-feature-parameters/38104/49 "2024-01-12T18:13:00Z")

</div>

It does kill some of static linting. We ended up with much less of MUST’s than I hoped, but life is complicated.

Still, it can contain canonical spellings (e.g `enableHTTP` vs `enableHttp`), and it can enforce MUST about naming (e.g it can catch `{ withFoo ? "foobar" }`).

---

<div class="post-metadata">

### Author: ![rnhmjoj](https://discourse.nixos.org/user_avatar/discourse.nixos.org/rnhmjoj/32/1792_2.png) [@rnhmjoj](https://discourse.nixos.org/u/rnhmjoj)
#### Post date: [January 12, 2024, 7:31pm UTC](https://discourse.nixos.org/t/pre-rfc-standardization-of-feature-parameters/38104/50 "2024-01-12T19:31:00Z")

</div>

IMHO the biggest problem is not that the options are named inconsistently, but that they are conflated with the other arguments. Even with a naming scheme or a ` features ? {...}` attrset, which would somewhat solve the discoverability problem, they are still a much worse version of NixOS options: they have no types, no semantics for merging, it’s not possible to inspect the defaults and generate docs automatically.

For me the proper solution would be to turn packages into modules. This would solve all of the above and more, for example it would allow a package to inspect the features enabled in a dependency. Of course, this is easier said than done: adding a new kind of module-package while remaining compatible with the current function-package and mixing them in Nixpkgs seems like a major headache.

---

<div class="post-metadata">

### Author: ![7c6f434c](https://discourse.nixos.org/letter_avatar_proxy/v4/letter/7/8e7dd6/32.png) [@7c6f434c](https://discourse.nixos.org/u/7c6f434c)
#### Post date: [January 12, 2024, 8:06pm UTC](https://discourse.nixos.org/t/pre-rfc-standardization-of-feature-parameters/38104/51 "2024-01-12T20:06:26Z")

</div>

> [@KAction](#):
>
> It does kill some of static linting

Actually I suspect that dropping this passthrough requirement would make name-linting theoretically possible, and it might not be completely unacceptably to just write withPythonPyqt.

> [@KAction](#):
>
> Still, it can contain canonical spellings (e.g `enableHTTP` vs `enableHttp`),

Given that we have camel, snake, and skewer casing happily coexisting, maybe it should be written explicitly how the naming is mangled into a uniform form.

Also, if `with` vs `enable` separation is mandatory, this means pointless churn for packages where we switch between vendored and our versions of a dependency.

> [@rnhmjoj](#):
>
> For me the proper solution would be to turn packages into modules.

Please no. It’s enough that NixOS is full of non-local effects on a fixpoint (fine with me, not going to use NixOS anytime soon anyway), but we have actual functions with actual scope in Nixpkgs and it should be kept this way.

Yeah, maybe splitting documentation-and-typing, merging, and scope-busting performance-killing fixpoint into well-defined layers could give something usable and close to the current modules system. But I won’t believe it before I see it.

> [@rnhmjoj](#):
>
> inspect the features enabled in a dependency

Of course the answer to the actually interesting question is hidden in a `let`. At least that’s how it went every time I wanted to use inspectability of modules.

---

<div class="post-metadata">

### Author: ![KAction](https://discourse.nixos.org/user_avatar/discourse.nixos.org/kaction/32/3888_2.png) [@KAction](https://discourse.nixos.org/u/KAction)
#### Post date: [January 12, 2024, 10:03pm UTC](https://discourse.nixos.org/t/pre-rfc-standardization-of-feature-parameters/38104/52 "2024-01-12T22:03:21Z")

</div>

> [@7c6f434c](#):
>
> Also, if `with` vs `enable` separation is mandatory, this means pointless churn for packages where we switch between vendored and our versions of a dependency.

Can you elaborate, please? I don’t think we ever should make it convenient to do vendorng.

---

<div class="post-metadata">

### Author: ![KAction](https://discourse.nixos.org/user_avatar/discourse.nixos.org/kaction/32/3888_2.png) [@KAction](https://discourse.nixos.org/u/KAction)
#### Post date: [January 12, 2024, 10:05pm UTC](https://discourse.nixos.org/t/pre-rfc-standardization-of-feature-parameters/38104/53 "2024-01-12T22:05:37Z")

</div>

> [@7c6f434c](#):
>
> Actually I suspect that dropping this passthrough requirement would make name-linting theoretically possible, and it might not be completely unacceptably to just write withPythonPyqt.

Yes, but as a user, I want to disable `Qt5`, not hunt down `pythonQt5`, `tclQt5` and so on. So I’d keep this pass-through clause.

---

<div class="post-metadata">

### Author: ![KAction](https://discourse.nixos.org/user_avatar/discourse.nixos.org/kaction/32/3888_2.png) [@KAction](https://discourse.nixos.org/u/KAction)
#### Post date: [January 12, 2024, 10:07pm UTC](https://discourse.nixos.org/t/pre-rfc-standardization-of-feature-parameters/38104/54 "2024-01-12T22:07:35Z")

</div>

> [@7c6f434c](#):
>
> Given that we have camel, snake, and skewer casing happily coexisting, maybe it should be written explicitly how the naming is mangled into a uniform form.

Good point. I’ll elaborate on `enableFoo` in the guidelines section.

---

<div class="post-metadata">

### Author: ![7c6f434c](https://discourse.nixos.org/letter_avatar_proxy/v4/letter/7/8e7dd6/32.png) [@7c6f434c](https://discourse.nixos.org/u/7c6f434c)
#### Post date: [January 12, 2024, 10:11pm UTC](https://discourse.nixos.org/t/pre-rfc-standardization-of-feature-parameters/38104/55 "2024-01-12T22:11:18Z")

</div>

> [@KAction](#):
>
> I don’t think we ever should make it convenient to do vendorng.

Globally: good luck keeping LibreOffice or Julia at zero vendoring for multiple releases. So what happens is we look «maybe this can be unvendored», but from time to time we are forced to go back to using the vendored version of something else. Doesn’t help that vendored versions sometimes carry project-specific patches…

The thing is, if one ever has an optional dependency in a safe-to-unvendor / too-complicated-to-unvendor-again cycle, by the wording of your proposal there will be pointless with/enable churn.

---

<div class="post-metadata">

### Author: ![7c6f434c](https://discourse.nixos.org/letter_avatar_proxy/v4/letter/7/8e7dd6/32.png) [@7c6f434c](https://discourse.nixos.org/u/7c6f434c)
#### Post date: [January 12, 2024, 10:12pm UTC](https://discourse.nixos.org/t/pre-rfc-standardization-of-feature-parameters/38104/56 "2024-01-12T22:12:53Z")

</div>

> [@KAction](#):
>
> Yes, but as a user, I want to disable `Qt5`, not hunt down `pythonQt5`, `tclQt5` and so on.

As a user you’ll need to have an idea what you have, and what has mandatory Qt dependencies. So yeah, withQt5 vs withQt6 vs withPythonPyqt is the least of your problems

---

<div class="post-metadata">

### Author: ![KAction](https://discourse.nixos.org/user_avatar/discourse.nixos.org/kaction/32/3888_2.png) [@KAction](https://discourse.nixos.org/u/KAction)
#### Post date: [January 12, 2024, 10:22pm UTC](https://discourse.nixos.org/t/pre-rfc-standardization-of-feature-parameters/38104/57 "2024-01-12T22:22:40Z")

</div>

Valid point. I’ll add clause that vendored dependencies, which technically are not external dependencies, are still `with`, not `enable`.

---

<div class="post-metadata">

### Author: ![KAction](https://discourse.nixos.org/user_avatar/discourse.nixos.org/kaction/32/3888_2.png) [@KAction](https://discourse.nixos.org/u/KAction)
#### Post date: [January 12, 2024, 10:24pm UTC](https://discourse.nixos.org/t/pre-rfc-standardization-of-feature-parameters/38104/58 "2024-01-12T22:24:45Z")

</div>

> [@7c6f434c](#):
>
> As a user you’ll need to have an idea what you have, and what has mandatory Qt dependencies. So yeah, withQt5 vs withQt6 vs withPythonPyqt is the least of your problems

Can’t argue, yet I still would rather make relatively small user’s problem even smaller.

---

<div class="post-metadata">

### Author: ![KAction](https://discourse.nixos.org/user_avatar/discourse.nixos.org/kaction/32/3888_2.png) [@KAction](https://discourse.nixos.org/u/KAction)
#### Post date: [January 12, 2024, 11:08pm UTC](https://discourse.nixos.org/t/pre-rfc-standardization-of-feature-parameters/38104/59 "2024-01-12T23:08:24Z")

</div>

Latest revision. Two more entries in changelog – about vendoring and about camel case.

As writing I realized that current wording has unfortunate implication that derivations that support multiple SSL libraries with have `enableSsl` flag, while derivation that only support openssl will only have `withOpenssl`, which is somewhat inconsistent.

I have no ideas how to fix it short of hard-coding list of blessed features, like `enableSsl`, `enablePdf`.

* * *

## feature: feature\_parameter\_names start-date: 2024-01-10 author: Dmitry Bogatov co-authors: (find a buddy later to help out with the RFC) shepherd-team: (names, to be nominated and accepted by RFC steering committee) shepherd-leader: (name to be appointed by RFC steering committee) related-issues: (will contain links to implementation PRs)

# Summary

Nixpkgs usually exports compile-time options of the upstream build system  
through the named parameters of derivation function, but does it inconsistent  
way.

# Motivation

Developer interface for building environment with specific features enabled and  
disabled is more complicated and requires more knowledge of implementation  
details compared to already existing systems.

For example, Gentoo Linux has exhaustive list of [all known configuration  
flags](https://www.gentoo.org/support/use-flags) and has means to  
programmatically query what flags are available for particular package. Nixpkgs  
has neither, and this RFC strives to rectify it.

# Detailed design

1. Derivation function MAY expose compile-time boolean options of the upstream  
build system to enable or disable a feature using parameters named either as  
`enableX` or `withX`, consistent with Autoconf naming convention.

2. If compile-time feature requires build and runtime dependency on package  
`libfoo`, corresponding feature parameter MUST match regular expression  
`^with[^a-z]`. See guidelines below for choosing name of feature parameter.

3. If compile-time feature does not require any extra build dependencies,  
corresponding feature parameter MUST have name matching `^enable[^a-z]` and  
SHOULD correspond to the upstream naming.

4. As special provision, if optional vendored dependency is exposed by feature  
parameter, that paramter name MUST have match `^with[^a-z]’ regular  
expression.

5. If upstream build features and build dependencies do not map one-to-one,  
then one `with` feature parameter SHOULD be added for every build dependecy  
and one `enable` feature SHOULD be added for every upstream build feature  
intended to be optional. Assertions to preclude incoherent feature  
configurations MAY be added.

6. These rules are to be enforced by static code analyse linter. Since no  
static code analyzis is perfect, it shall have support for inhibiting  
warnings in individual cases that do not fit into general scheme.

7. Parameter names matching `^(enable|with)` regular expression MUST not be  
used for any other purpose. In particular, they always must be boolean.

8. Derivation function MAY expose compile-time string or numeric options of the  
upstream build system using feature parameters that MUST match `^conf[^a-z]`  
regular expression, e.g `confDefaultMaildir`.

9. Due overwhelming amount of possible combinations of feature flags for some  
packages, nixpkgs maintainer is not expected to test or maintain them all,  
but SHOULD accept provided technically sound contributions related to  
configurations with non-default feature flags.

10. Due overwhelming amount of possible combinations of feature flags for some  
packages, only configurations that has name in package set (e.g `emacs-nox`)  
shall be built on CI.

## The migration process.

Named parameters are part of the programming interface, and renaming them is a  
breaking change. As such, renaming of the parameters is done in following way:

1. Following function is added into `lib` set:

```auto
let renamed = { oldName, newName, sunset, oldValue }: newValue:
   let warning = builtins.concatStringsSep " "
      [ "Feature flag"
      , oldName
      , "is renamed to"
      , newName
      , "; old name will no longer be available in nixpkgs="
      , sunset
      , "."
      ]
   in lib.warnIf (value != null) warning (if (value != null) then value else newValue);

```

Starting with following function:

```auto
{ lib
, stdenv
, nonCompliantFoo ? true
}:

# uses nonCompliantFoo
stdenv.mkDerivation { ... }

```

First step of migration is to replace it with the following:

```auto
{ lib
, stdenv
, nonCompliantFoo ? null
, enableFoo ? lib.renamed {
    oldName = "nonCompliantFoo";
    newName = "enableFoo";
    sunset = "25.11";
    value = nonCompliantFoo;
  } true
}:

# uses enableFoo
stdenv.mkDerivation { ... }

```

and after grace period of two releases, any mentions of `nonCompliantFoo` are  
removed, and function becomes:

```auto
{ lib
, stdenv
, enableFoo ? true
}:

# uses enableFoo
stdenv.mkDerivation { ... }

```

## Feature parameter naming guidelines

1. Feature flags that require single external dependency SHOULD be named after  
that dependency. Prefix `lib` SHOULD be removed. For example,

2. When multiple feature flags require the same build dependency, for example  
derivation has optional support for FTP and HTTP protocols, any of which  
incur dependency on `curl`, and derivation would look like following:

3. Mutually-exclusive build dependencies that provide the same feature are also  
handled with assertions. For example, if derivation has optional SSL support  
that may be provided by multiple libraries, but only one may be used and it  
must be chosen at compilation time, derivation will look like following:

4. When build dependency comes from particular package set, it MAY make sense to  
name feature parameter after it. E.g build dependency on `qt6.qtbase` should  
have `withQt6` feature parameter.

5. If feature parameter name suggested by previous point is too generic,  
package name from the set MAY be included into the feature parameter name.  
Optional dependency on `pythonPackages.pillow` MAY have feature parameter  
`withPythonPillow`.

6. Build dependency on bindings to low-level libraries SHOULD be named after  
underlying library. For example, optional dependecy on `pyqt5` Python  
bindings to `Qt5` library should have `withQt5` feature parameter.

7. Camel case is preferred to other styles. E.g:

```auto
pam => withPam
http => enableHttp
gemini => enableGemini
ssl => enableSsl
openssl => withOpenssl
pythonPackages.pillow => withPythonPillow

```

# Examples and Interactions

This is how one can query list of feature parameters that incur extra runtime dependencies:

```auto
$ nix eval --json --impure --expr 'with import ./. {}; builtins.attrNames gnuplot.override.__functionArgs' | jq '.[]| select(startswith("with"))'
"withCaca"
"withLua"
"withQt"
"withTeXLive"
"withWxGTK"

```

I picked the `gnuplot` as example since it is the closest to be compliant with proposed rules.

# Drawbacks

1. The migration process involves renaming feature parameters, so these changes  
will likely conflict with other outstanding changes to the derivation, and potentially  
even with automatic version bumps.

# Alternatives

## Group all feature parameters into separate attrset parameter

Instead of writing

```auto
{ lib, stdenv, enableFoo ? true }:

# uses enableFoo
stdenv.mkDerivation { ... }

```

derivation can be written as

```auto
{ lib, stdenv, features ? { enableFoo = true; }}:

# uses features.enableFoo
stdenv.mkDerivation { ... }

```

It is definitely looks cleaner, but unfortunately hits several limitations of  
the Nix language.

1. Nix language provides way to [introspect function argument names](https://nixos.org/manual/nix/stable/language/builtins.html#builtins-functionArgs),  
but no way to learn their default values. So this approach gives consistency, but no way to query list of derivation feature parameters.

2. Overrides become much more verbose. Simple and ubiquitous

Any approach that tries to pass attribute set to function will have these  
issues. Using existing [config.nix](https://github.com/NixOS/nixpkgs/blob/4d1a9c1d4a83cb502f680a2de3d4daf1c20714a2/pkgs/top-level/config.nix)  
is no different.

## Do nothing

Avoids all the work and drawbacks, but there is no evidence that consistency  
problem will [solve itself evolutionary](https://github.com/NixOS/nixpkgs/pull/234463#issuecomment-1574892207).  
For some build dependencies, we have multiple equally popular feature parameter  
names, and people keep picking random one when adding new packages.

# Prior art

As mentioned in motivation part, the best in class of feature flag  
configuration system is Gentoo Linux:

- [[USE flag index – Gentoo Linux](https://www.gentoo.org/support/use-flags)]

There is not much work I can find about this problem in Nixpkgs other than my  
previous attempts to solve it on package-by-package basis:

- [Naming convention for optional feature flags · Issue #148730 · NixOS/nixpkgs · GitHub](https://github.com/NixOS/nixpkgs/issues/148730)
- [dbus: rename "enableSystemd" to "withSystemd" by KAction · Pull Request #234463 · NixOS/nixpkgs · GitHub](https://github.com/NixOS/nixpkgs/pull/234463)

# Unresolved questions

This RFC makes it possible to introspect feature parameters of particular  
derivation, but still does not provide simple and efficient way to list all  
existing feature parameters.

# Future work

There are other configuration scenarios not covered by this RFC:

- Optional dependencies in shell wrappers (e.g [passage](https://github.com/NixOS/nixpkgs/blob/4d1a9c1d4a83cb502f680a2de3d4daf1c20714a2/pkgs/tools/security/passage/default.nix#L12)).
- Finding way to get list of all existing feature parameters. That can be possibly done by building and distributing the index separately,  
like [nix-index](https://github.com/nix-community/nix-index) does it.

# Changelog

1. Changed wording to not imply that every upstream build system knob SHOULD be  
exported via feature parameters. (Thx: 7c6f434c)

2. Relaxed wording on the name of feature parameters to avoid painting ourselves  
into ugly and non-intuitive names. (Thx: 7c6f434c)

3. Fix typo in regex to be consistent that feature flag name can’t have small  
letter after `with|conf|enable` prefix. (Ths: don.dfh)

4. Explicitly mention that static code analysis has support for overrides based  
on human judgement call. (Thx: 7c6f434c)

5. Clarify solution scenarios when build inputs and feature flags don’t match  
one-to-one. (Thx: Atemu, 7c6f434c)

6. Refine the deprecation plan to make sure the warning includes the sunset  
timestamp. (Thx: pbsds)

7. Add rules about non-boolean feature parameters. (Thx: Atemu, pbsds)

8. Set expectations for building and maintaining multiple configurations. (Thx: pbsds)

9. Removed non-boolean parameters from “Future Work” section.

10. Relaxed requirements for assertions about conflicting flags (Thx: Atemu)

11. Add guideline so `pythonPackages.pillow` does not get `withPython` feature name. (Thx: 7c6f434c)

12. Mention that vendored dependenices are still `with`. (Thx: 7c6f434c)

13. Elaborate on camel case convention. (Thx: 7c6f434c)

---

<div class="post-metadata">

### Author: ![rnhmjoj](https://discourse.nixos.org/user_avatar/discourse.nixos.org/rnhmjoj/32/1792_2.png) [@rnhmjoj](https://discourse.nixos.org/u/rnhmjoj)
#### Post date: [January 13, 2024, 9:31am UTC](https://discourse.nixos.org/t/pre-rfc-standardization-of-feature-parameters/38104/60 "2024-01-13T09:31:22Z")

</div>

> It’s enough that NixOS is full of non-local effects on a fixpoint (fine with me, not going to use NixOS anytime soon anyway), but we have actual functions with actual scope in Nixpkgs and it should be kept this way.

It would have to be a somewhat restricted version of the module system anyway, otherwise there’s no chance of scaling it to ~100000 packages. Evaluating NixOS is already slow with less than 2000 modules.

---

<div class="post-metadata">

### Author: ![7c6f434c](https://discourse.nixos.org/letter_avatar_proxy/v4/letter/7/8e7dd6/32.png) [@7c6f434c](https://discourse.nixos.org/u/7c6f434c)
#### Post date: [January 13, 2024, 10:51am UTC](https://discourse.nixos.org/t/pre-rfc-standardization-of-feature-parameters/38104/61 "2024-01-13T10:51:36Z")

</div>

> [@rnhmjoj](#):
>
> Evaluating NixOS is already slow with less than 2000 modules.

And, separately, evaluating Nixpkgs for CI is already too slow (the current proposal looks almost neutral, though)

[Previous page](https://discourse.nixos.org/t/pre-rfc-standardization-of-feature-parameters/38104.md?page=2)

[Next page](https://discourse.nixos.org/t/pre-rfc-standardization-of-feature-parameters/38104.md?page=4)
