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

Implicit attribute defaults/overrides inside package definition #273534

Open
Atemu opened this issue Dec 11, 2023 · 13 comments
Open

Implicit attribute defaults/overrides inside package definition #273534

Atemu opened this issue Dec 11, 2023 · 13 comments

Comments

@Atemu
Copy link
Member

Atemu commented Dec 11, 2023

Issue description

Currently, in order to override a package's arguments in order to i.e. pin a version of a library, you must do so at the callPackage location, usually all-packages.nix. This has a few issues:

  • all-packages is huge
    • Easy to run into conflicts
    • Chaotic/Confusing
    • Hard to work with (can crash some LSPs)
  • Source of truth is split
    • It is no longer obvious from looking at a package definition file what its arguments are (they might be overridden)
    • Maintainers might forget/never now that overrides are in place (who here frequently looks inside all-packages for their packages?)
    • It just feels wrong to have this info in a separate place
  • Requires explicit imports

I've been a fan of explicit requirements; i.e. depending on ffmpeg_5 rather than ffmpeg with an ffmpeg = ffmpeg_5; override in all-packages. This also has issues, the primary one being that overrides aren't stable anymore and are even less intuitive as you'd have to override i.e. ffmpeg_5 = ffmpeg_6;.

IMHO, the benefits outweighed the downsides as overrides aren't really a stable thing to begin with but I'd prefer to fix this issue "proper". In order to do that we must either put the package definition into all-packages (yeah, no) or put the overrides into package definitions.

This issue exists as a collection point for ideas on how to realise this.

Related discussion: #204303

@eclairevoyant
Copy link
Contributor

eclairevoyant commented Dec 11, 2023

The first issue has another point - github won't even index the file for searching. Though it should be massively improved with the move to by-name

@infinisil
Copy link
Member

Yeah this problem has been discussed with the Nixpkgs Architecture Team throughout RFC 140. I don't have any concrete proposals nor the resources to come up with one for now, but if anybody else does, it would be great to synchronise with the NAT for that.

@roberth
Copy link
Member

roberth commented Dec 12, 2023

dream2nix deps pattern may be interesting to at least pull the default overrides into the package file.

@ghost
Copy link

ghost commented Jan 19, 2024

This also has issues, the primary one being that overrides aren't stable anymore and are even less intuitive as you'd have to override i.e. ffmpeg_5 = ffmpeg_6;.

⚠️ It would be really great if we could keep version numbers out of the attrnames. You've pointed out one of the problems this causes; there are lots of others. This frequently causes my local overlays to be silently ignored after a rebase -- wlroots is the worst offender.

Analogy to multi-platform packages

This has close parallels with how we handle platforms. We don't have separate toplevel ffmpeg_x86-linux and ffmpeg_aarch64-linux attributes -- instead, ffmpeg is a "spliced"1 collection of packages, one for each platform.

We should do the same thing with versions. Each top level attribute should be not a single package (which hasn't been true since __spliced was added) but rather a collection of all available versions of that package.

Version overrides then become straightforward: you simply delete from the __versions set any versions you know won't work with your package. This is better-behaved in the case of layered overrides: unlike replacements, deletions commute with each other, so you get the same result regardless of the order in which you apply the overrides.

Proof of Concept

I have a prototype here but it's nowhere near PR-ready. I developed it against gcc, since that is by far the gnarliest case of multiversioned packaging in nixpkgs. Some repl to give an idea of what it feels like:

nix-repl> gcc
«derivation /nix/store/20z7kwqc1fp8v4fmmm1p9jmvig3r6cys-gcc-wrapper-13.2.0.drv»

nix-repl> gcc.__versions.best
«derivation /nix/store/20z7kwqc1fp8v4fmmm1p9jmvig3r6cys-gcc-wrapper-13.2.0.drv»

nix-repl> gcc.version
"13.2.0"

nix-repl> builtins.attrNames gcc.__versions
[ "10.5.0" "11.4.0" "12.3.0" "13.2.0" "4.8.5" "4.9.4" "6.5.0" "7.5.0" "8.5.0" "9.5.0" "__unfix__" "best" "extends" "require" "unstable" ]

nix-repl> gcc.__versions.require "<10"
«derivation /nix/store/xc0hx0ijajgxjvdlrv9k92xlwfvrbb2b-gcc-wrapper-9.5.0.drv»

nix-repl> (gcc.__versions.require "<10").version
"9.5.0"

nix-repl> (gcc.__versions.require "<10").__versions.best.version
"9.5.0"

Notice how there is a reference cycle pkg.__versions.best.__versions.best... so you can always get a reference to the version-set from any of its members and vice-versa.

Highlight commits in the PoC

The main thing that's missing is that .overrideAttrs on a multiversion package needs to apply the override to every version, not just the best version.

Status

I have a bunch of other nix-related projects that are close to the finish line so unfortunately I am not going to be able to finish this in the next month or two. If anybody wants to steal this idea and run with it more power to them; otherwise I'll come back to it in the spring.

Footnotes

  1. I am one of the loudest complainers when it comes to issues caused by __spliced, but those problems come from two aspects which don't apply to __versions: (a) the "unspliced packages" optimization turned into a huge footgun and (b) when splicing was added we tried to keep the old buildInputs/nativeBuildInputs interface for stdenv.mkDerivation instead of having a single deps attribute with package authors projecting out the desired element of __spliced.

@roberth
Copy link
Member

roberth commented Jan 20, 2024

We really ought to have a place for abstract package information that isn't specific to a build of a package, or we'll keep incurring the performance and complexity cost of unused package evaluations / unnecessary overrides.

For instance, evaluating gcc.__versions.something requires that we evaluate gcc, as well as something, which has a real performance cost when applied at a scale that isn't tiny. Also overrideAttrs isn't particularly cheap. As for complexity, there's just less division of responsibilities, and the performance problem implies a strictness problem - e.g. gcc may fail to evaluate (well, more likely for other versioned packages perhaps). Also it's not possible to pass an exact version without the possibility that some code inspects its __versions to get something else instead. This could turn out a bit chaotic.

All that said, I'm glad to see some experimentation happen in this area.

#217243 was meant to mitigate some of this, but mkDerivation tech debt is bankrupt. I've put a plan in the bottom comments, but versioning should not be shoehorned into mkDerivation, regardless of whether we execute on such a plan or not.

nitpick: @amjoseph-nixpkgs It is my understanding that the convention around __ is that _ is reserved for libraries/frameworks/infra defined in expressions, where as __ is reserved for things defined and/or handled by Nix itself. There's definitely a gray area, but your __version is an instance of the prior.

@infinisil
Copy link
Member

infinisil commented Jan 31, 2024

For instance, evaluating gcc.__versions.something requires that we evaluate gcc, as well as something, which has a real performance cost when applied at a scale that isn't tiny.

With lazy attribute sets this could be improved. I'd like to pick up that PR again, cases where it seems useful frequently appear.

@roberth
Copy link
Member

roberth commented Jan 31, 2024

I don't think we should invent language features to patch architectural flaws; the lack of a package concept that's not instantiated. I know it may have other uses, but this one basically doesn't count.

@nixos-discourse
Copy link

This issue has been mentioned on NixOS Discourse. There might be relevant details there:

https://discourse.nixos.org/t/why-is-there-only-one-version-in-nixpkgs/47773/3

@nixos-discourse
Copy link

This issue has been mentioned on NixOS Discourse. There might be relevant details there:

https://discourse.nixos.org/t/why-does-the-nixos-infrastructure-have-to-be-hosted-in-a-centralized-way/46789/49

@nixos-discourse
Copy link

This issue has been mentioned on NixOS Discourse. There might be relevant details there:

https://discourse.nixos.org/t/discourse-via-email/29/13

@nixos-discourse
Copy link

This issue has been mentioned on NixOS Discourse. There might be relevant details there:

https://discourse.nixos.org/t/review-of-333575/50496/1

@nixos-discourse
Copy link

This issue has been mentioned on NixOS Discourse. There might be relevant details there:

https://discourse.nixos.org/t/review-of-333575/50496/3

@nixos-discourse
Copy link

This issue has been mentioned on NixOS Discourse. There might be relevant details there:

https://discourse.nixos.org/t/review-of-333575/50496/5

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants