Skip to content

pkgs/development/compilers/llvm: use makeScopeWithSplicing#445668

Merged
emilazy merged 7 commits intoNixOS:masterfrom
LunNova:push-lmpqqluvqonk
Oct 26, 2025
Merged

pkgs/development/compilers/llvm: use makeScopeWithSplicing#445668
emilazy merged 7 commits intoNixOS:masterfrom
LunNova:push-lmpqqluvqonk

Conversation

@LunNova
Copy link
Member

@LunNova LunNova commented Sep 24, 2025

Rebase of @RossComputerGuy's PR #436350 + ROCm scope usage & rebuild avoidance + darwin rebuild avoidance.

Things done

  • Ensure no rebuilds
  • why is .override trying to disappear from darwin default llvmPackages?
    • Because override and overrideScope don't compose without changes to makeOverridable
    • We need these to compose for cases like
      llvmPackages0 = rustc.llvmPackages;
      llvmPackagesBuildBuild0 = pkgsBuildBuild.rustc.llvmPackages;
      # Force the use of lld and other llvm tools for LTO
      llvmPackages = llvmPackages0.override {
      bootBintoolsNoLibc = null;
      bootBintools = null;
      };
    • To get to zero rebuilds we need to propagate an incorrect override function that discards all of the stdenv overrides, because that's what happened in the past
  • Fits CONTRIBUTING.md, pkgs/README.md, maintainers/README.md and other READMEs.

Add a 👍 reaction to pull requests you find important.

@LunNova LunNova force-pushed the push-lmpqqluvqonk branch 3 times, most recently from b0d2561 to ce403c5 Compare September 24, 2025 00:32
@LunNova
Copy link
Member Author

LunNova commented Sep 24, 2025

Ran into a problem. darwin's llvmPackages ends up not having a .override, causing an eval failure if that's ever used. darwin applies an overrideScope in pkgs/stdenv/darwin/default.nix which ends up removing that attr.

This happens on this rebased version, it also was happening on the original. No eval error must be because in that version nothing was calling .override on the default version of llvmPackages.

# PR 436350's override missing for darwin
$ nix eval github:nixos/nixpkgs/acd183d2efb23f3877c448feec7d76bf6125b5dd#legacyPackages.aarch64-darwin.llvmPackages.override
# but it's present for the other versions
$ nix eval github:nixos/nixpkgs/acd183d2efb23f3877c448feec7d76bf6125b5dd#legacyPackages.aarch64-darwin.llvmPackages_20.override
{ __functionArgs = { bootBintools = true; bootBintoolsNoLibc = true; buildPackages = false; callPackage = false; generateSplicesForMkScope = false; lib = false; llvmVersions = true; patchesFn = true; pkgs = false; recurseIntoAttrs = false; stdenv = false; stdenvAdapters = false; targetPackages = false; }; __functor = «lambda __functor @ /nix/store/ny1zr85wfp3yishpwfpqhh3dpnslrl6g-source/lib/trivial.nix:989:17»; }

For 0 rebuilds we probably need to make darwin stdenv's llvmPackages immediately // { inherit (super.llvmPackages) override; } since that's what it ends up as in master, but that wouldn't be a good permanent fix.

There must be some mechanism that allows this to work for (something.overrideAttrs{}).override{}, which I'll try to find. cc @ShamrockLee since I vaguely recall you touching related override machinery for a similar problem when you were teaching python packages to compose overrides well.

@nixpkgs-ci nixpkgs-ci bot added 10.rebuild-darwin: 501+ This PR causes many rebuilds on Darwin and should normally target the staging branches. 10.rebuild-linux: 0 This PR does not cause any packages to rebuild on Linux. 10.rebuild-darwin-stdenv This PR causes stdenv to rebuild on Darwin and must target a staging branch. 10.rebuild-darwin: 5001+ This PR causes many rebuilds on Darwin and must target the staging branches. 6.topic: stdenv Standard environment 6.topic: rocm ROCm is an Advanced Micro Devices software stack for graphics processing unit programming. 6.topic: llvm/clang Issues related to llvmPackages, clangStdenv and related labels Sep 24, 2025
@LunNova

This comment was marked as resolved.

@nixpkgs-ci nixpkgs-ci bot added 10.rebuild-darwin: 11-100 This PR causes between 11 and 100 packages to rebuild on Darwin. and removed 10.rebuild-darwin: 501+ This PR causes many rebuilds on Darwin and should normally target the staging branches. 10.rebuild-darwin-stdenv This PR causes stdenv to rebuild on Darwin and must target a staging branch. 10.rebuild-darwin: 5001+ This PR causes many rebuilds on Darwin and must target the staging branches. labels Sep 24, 2025
Comment on lines -1253 to -1255
// lib.optionalAttrs (super.stdenv.targetPlatform == localSystem) {
inherit (prevStage.llvmPackages) clang;
}
Copy link
Member Author

@LunNova LunNova Sep 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we don't remove these 3 lines it fixes llvmPackages.clang rebuilding + various other packages like casadi
but will cause ~1k additional rebuilds elsewhere eg all php packages, which seems to be down to llvmPackages.stdenv.cc becoming different

I don't understand how replacing just the clang attr with an attrset merge // outside the overrideScope call can cause llvm scope's stdenv attr to change so I'm probably badly misunderstanding something going on here.

with clang inherited from prevStage here llvmPackages.stdenv.cc, php etc are different. llvmPackages. prefix only, stdenv.cc is always not a rebuild:

nix-diff (nix derivation show github:nixos/nixpkgs/staging-next#legacyPackages.aarch64-darwin.llvmPackages.stdenv.cc | jq -r 'keys[0]') (nix derivation show .#legacyPackages.aarch64-darwin.llvmPackages.stdenv.cc | jq -r 'keys[0]')

with clang not inherited from prevStage here llvmPackages.clang, casadi etc are different:

nix-diff (nix derivation show github:nixos/nixpkgs/staging-next#legacyPackages.aarch64-darwin.llvmPackages.clang | jq -r 'keys[0]') (nix derivation show .#legacyPackages.aarch64-darwin.llvmPackages.clang | jq -r 'keys[0]')

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With that applied we're back to php* rebuilds. :c

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That’s really strange because nix-diff showed no differences for me. 😞

Copy link
Contributor

@reckenrode reckenrode Oct 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I have a real solution. This results in no differences for php and for casadi.

diff --git a/pkgs/stdenv/darwin/default.nix b/pkgs/stdenv/darwin/default.nix
index dc8ac58328..f7c5262acc 100644
--- a/pkgs/stdenv/darwin/default.nix
+++ b/pkgs/stdenv/darwin/default.nix
@@ -1245,9 +1245,6 @@
                   // {
                     inherit (super."llvmPackages_${llvmVersion}") llvm-manpages;
                   }
-                  // lib.optionalAttrs (super.stdenv.targetPlatform == localSystem) {
-                    inherit (prevStage.llvmPackages) systemLibcxxClang;
-                  }
                 ))
                 // {
                   inherit (super."llvmPackages_${llvmVersion}") override;
@@ -1284,7 +1281,24 @@
     assert prevStage.libiconv == prevStage.darwin.libiconv;
 
     {
-      inherit (prevStage) config overlays stdenv;
+      inherit (prevStage) config overlays;
+      # This should be done in the `overrideScope` above, but it causes rebuilds.
+      # TODO: Move it there once https://github.com/NixOS/nixpkgs/pull/445668 is merged.
+      stdenv = prevStage.stdenv // {
+        overrides =
+          self: super:
+          (prevStage.stdenv.overrides self super)
+          // lib.optionalAttrs (super.stdenv.targetPlatform == localSystem) (
+            let
+              llvmVersion = lib.versions.major prevStage.llvmPackages.release_version;
+            in
+            {
+              "llvmPackages_${llvmVersion}" = prevStage."llvmPackages_${llvmVersion}" // {
+                inherit (prevStage) clang;
+              };
+            }
+          );
+      };
     }
   )
 ]

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Running nixpkgs-review with this patch results in only one rebuild: flang_21.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yay! Down to zero rebuilds now.

stdenv mess to get it there was not fun, thanks for helping out.

@ShamrockLee
Copy link
Contributor

There must be some mechanism that allows this to work for (something.overrideAttrs{}).override{}, which I'll try to find.

lib.makeOverridable specifically decorates the overrrideAttrs attribute (and the overrideDerivationattribute) to preserve its own effect. IIRC, @infinisil abstracted such functionality as the internal function overrideResult in the makeOverride implementatio in PR #67809.

A workaround is to adjust lib.makeOverridable and do the same for the overrideScopeattribute, but I think it would be more sustainable to add makeOverridableWith or even mkeOverridableGeneric that allows developers to specify existing overriders.

@bb010g
Copy link
Contributor

bb010g commented Oct 1, 2025

lib.makeOverridable specifically decorates the overrrideAttrs attribute (and the overrideDerivationattribute) to preserve its own effect. IIRC, @infinisil abstracted such functionality as the internal function overrideResult in the makeOverride implementatio in PR #67809.

I've messed around with a patch for exposing overrideResult from lib.customisation.makeOverridable and it had a negligible effect on eval times and memory usage in a successful full eval (nix-build ci -A eval.baseline --arg quickTest false). Additionally, it'd allow cleaning up some Nixpkgs machinery that currently emulates overrideResult. (I haven't tested NixOS with that patch at all.) I should probably get around to submitting that. I think the lib.customisation.makeScopes using .overrideResult, when available, to equip their attributes would resolve #447012.

@nixpkgs-ci nixpkgs-ci bot added the 2.status: merge conflict This PR has merge conflicts with the target branch label Oct 7, 2025
@LunNova LunNova changed the base branch from staging-next to master October 22, 2025 03:33
@LunNova LunNova requested a review from reckenrode October 23, 2025 13:28
Copy link
Member

@emilazy emilazy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for picking this up, and for @RossComputerGuy for the original PR. I am very glad to see rebuilds down to zero and that exposing all the ways in which things were screwy before :)

I think there is one important change we should make around how this works with cross, and I’d like to understand what we’re going to do about llvmPackages.override in the Darwin stdenv, but this looks like a big improvement to me in general and I hope we can merge this very soon.

Comment on lines +529 to +531
# AMD has a separate MLIR impl which we package under rocmPackages.rocmlir
# It would be an error to rely on the original mlir package from this scope
mlir = null;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This wasn’t here previously; is it related to rebuild avoidance?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Newly introduced, to me it made sense to include with introducing overrideScope usage here as it doesn't cause a rebuild. Without overrideScope there wasn't a way to enforce this.

Comment on lines +550 to +553
))
// {
inherit (super.llvmPackages) override;
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I lost track of what the plan is with these. This will mean that llvmPackages.override { } will produce super.llvmPackages, without the overrides from this stage. That may be required for rebuild avoidance, of course, but do we know what we’re going to do for eating the rebuilds? It seems quite important for the final llvmPackages.override after the bootstrap to behave correctly, and to correctly override the overridden scope.

I guess this is an existing problem – clang != (llvmPackages.override { }).stdenv on Darwin right now, which is quite distressing. But we really ought to fix that, so I’m just checking if we know how we’re going to. @ShamrockLee’s proposal for a general solution to composable overrides seems sound, and I’ve spent an inordinate amount of time thinking about how this stuff should look lately myself. But we should have a short‐term solution for llvmPackages at least, when we’re switching over how that scope is assembled.

I think that llvmPackages = callPackage (lib.mirrorFunctionArgs llvmPackages.override (args: (llvmPackages.override args).overrideScope …)); might work, at the expense of being quite horrendous – although AIUI @Artturin says you should always use callPackage in overlays anyway for splicing reasons, so maybe it would actually be good?? I suppose this is analogous to the way overrideResult works, which would more directly be llvmPackages = lib.makeOverridable (lib.mirrorFunctionArgs llvmPackages.override (args: (llvmPackages.override args).overrideScope …)). I frankly have no clue which one would be better and hope one of the people I just pinged has a better idea :)

We could abstract this into an overrideLlvmPackages function in this file. This is certainly a problem we need to solve more generally, for overriding LLVM to work well, let alone many other cases where our overrides system falls down. Motivation to keep working on my related grant plans…

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel okay about this plan for now.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

although AIUI @Artturin says you should always use callPackage in overlays anyway for splicing reasons, so maybe it would actually be good??

Is there discussion elsewhere about this to follow?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I forget where I read @Artturin saying this, I’m afraid. But it’s because of final.callPackage having access to the spliced packages but final not.

RossComputerGuy and others added 7 commits October 25, 2025 20:06
Uses new overrideScope feature to override base LLVM packages
without risking leaking incorrect non-overridden packages
from the base LLVM version.

To achieve no rebuilds or new attrs:
- Explicit list of packages to expose in an inherit instead
  of the entire scope
- Noting where workarounds for lack of overrideScope should
  be removed in a followup such as re-attaching a
  replaceVars patch or disabling separate tblgen package,
  but are kept for now to avoid a rebuild

To avoid a large formatting diff a small helper has
been introduced that allows an overrideScope call with a single
arg, convincing nixfmt not to indent the scope body further.
- Setting recurseForDerivations again post overrideScope
- Hackily propagating initial value llvmPackages.override
This was used in 4b07aea to get
what was actually a *host* LLVM in practice, when the `buildHost`
LLVM package set was included in the `callPackage` scope used for
libraries. It should no longer be necessary and is now quite confusing.
@LunNova
Copy link
Member Author

LunNova commented Oct 26, 2025

Thanks @emilazy, that looks great*!

I can prep followup changes that drop the rebuild avoidance introduced here.

*skipping over some consequences of past decisions that maybe do not look great

@emilazy emilazy added this pull request to the merge queue Oct 26, 2025
Merged via the queue into NixOS:master with commit fcd3b9c Oct 26, 2025
27 of 30 checks passed
@LunNova LunNova deleted the push-lmpqqluvqonk branch February 26, 2026 04:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

6.topic: llvm/clang Issues related to llvmPackages, clangStdenv and related 6.topic: rocm ROCm is an Advanced Micro Devices software stack for graphics processing unit programming. 6.topic: stdenv Standard environment 10.rebuild-darwin: 0 This PR does not cause any packages to rebuild on Darwin. 10.rebuild-linux: 0 This PR does not cause any packages to rebuild on Linux.

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

6 participants