Skip to content

stdenv/darwin: remove workarounds and do cleanup#463900

Merged
emilazy merged 12 commits intoNixOS:stagingfrom
reckenrode:darwin-stdenv-cleanup
Jan 15, 2026
Merged

stdenv/darwin: remove workarounds and do cleanup#463900
emilazy merged 12 commits intoNixOS:stagingfrom
reckenrode:darwin-stdenv-cleanup

Conversation

@reckenrode
Copy link
Copy Markdown
Contributor

@reckenrode reckenrode commented Nov 22, 2025

This is a follow up to #445668. Many thanks to @LunNova for her work on that PR and to @RossComputerGuy for the initial work in #436350. Even though there are not as many deletions as I’d have liked, it has still allowed for a lot of cruft to be removed from the Darwin bootstrap.

Things done

  • Built on platform:
    • x86_64-linux
    • aarch64-linux
    • x86_64-darwin
    • aarch64-darwin
  • Tested, as applicable:
  • Ran nixpkgs-review on this PR. See nixpkgs-review usage.
  • Tested basic functionality of all binary files, usually in ./result/bin/.
  • Nixpkgs Release Notes
    • Package update: when the change is major or breaking.
  • NixOS Release Notes
    • Module addition: when adding a new NixOS module.
    • Module update: when the change is significant.
  • Fits CONTRIBUTING.md, pkgs/README.md, maintainers/README.md and other READMEs.

Add a 👍 reaction to pull requests you find important.

@reckenrode reckenrode changed the title stdenv/darwin: remove workarounds and cleanup stdenv/darwin: remove workarounds and do cleanup Nov 22, 2025
@reckenrode reckenrode added the backport staging-25.11 Backport PR automatically label Nov 22, 2025
@ofborg ofborg Bot added the 6.topic: darwin Running or building packages on Darwin label Nov 22, 2025
@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 labels Nov 22, 2025
@reckenrode reckenrode marked this pull request as ready for review November 22, 2025 13:36
@nixpkgs-ci nixpkgs-ci Bot requested review from emilazy and toonn November 22, 2025 20:51
@reckenrode reckenrode force-pushed the darwin-stdenv-cleanup branch 3 times, most recently from f261bbc to 6a3489f Compare November 28, 2025 15:55
@reckenrode reckenrode force-pushed the darwin-stdenv-cleanup branch from 6a3489f to 29bdd60 Compare December 14, 2025 12:51
Comment thread pkgs/stdenv/darwin/default.nix Outdated
// {
inherit (super.llvmPackages) override;
inherit (super."llvmPackages_${llvmVersion}") override;
};
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I believe this produces incorrect results, as the .override will drop the .overrideScope changes. It was like this before for bug‐compatibility, but making sure .override and .overrideScope both work correctly seems like an important part of the clean‐up here – unless it’s intentional that these .overrideScopes get dropped completely when a user does llvmPackages.override? For early bootstrap stages I could see that potentially making sense, but is it what we want for the final .overrideScope?

Assuming we do want these .overrideScopes to be applied post‐.override, per #445668 (comment), I believe that what we’ll want to do here is define a function like overrideLlvmPackagesScope = llvmPackages: f: lib.makeOverridable (lib.mirrorFunctionArgs llvmPackages.override (args: (llvmPackages.override args).overrideScope f));. Then we can skip the // stuff here, and just use htat instead of .overrideScope. If this results in a measurable eval perf impact, then I think overrideLlvmPackagesScope = llvmPackages: f: llvmPackages.overrideScope f // { override = g: overrideLlvmPackagesScope (llvmPackages.override g) f; }; would avoid that at the expense of being less pleasant, but it probably isn’t necessary.

Of course having to do this kind of stuff is not very pleasant in general, but the various override interfaces interacting badly with each other is a known problem; we have special cases for .overrideAttrs but not .overrideScope. See #447012 and #454997 for the general case here; I’d like to work on unifying these interfaces rather than adding on more elaborate/generalized special cases every time it comes up, but I haven’t had the time lately…

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I thought it was like that because of the issue with overrideScope losing override not because of bug compatibility.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Assuming we do want these .overrideScopes to be applied post‐.override, per #445668 (comment), I believe that what we’ll want to do here is define a function like overrideLlvmPackagesScope = llvmPackages: f: lib.makeOverridable (lib.mirrorFunctionArgs llvmPackages.override (args: (llvmPackages.override args).overrideScope f));.

I tried this, but it didn’t work.

$ nix repl -f . --debugger --ignore-try
Lix 2.93.3
Type :? for help.
error: attribute 'compiler-rt' missing
       at /Users/reckenrode/Developer/nixpkgs/pkgs/stdenv/darwin/default.nix:260:55:
          259|   llvmLibrariesPackages = prevStage: {
          260|     inherit (prevStage."llvmPackages_${llvmVersion}") compiler-rt libcxx;
             |                                                       ^
          261|   };

Added 35 variables.
nix-repl> prevStage.llvmPackages_21
{
  __functionArgs = { ... };
  __functor = «lambda __functor @ /Users/reckenrode/Developer/nixpkgs/lib/trivial.nix:1018:17»;
}

nix-repl> prevStage.llvmPackages_21.__functionArgs
{
  bootBintools = true;
  bootBintoolsNoLibc = true;
  buildPackages = false;
  callPackage = false;
  generateSplicesForMkScope = false;
  lib = false;
  llvmVersions = true;
  patchesFn = true;
  pkgs = false;
  stdenv = false;
  stdenvAdapters = false;
  targetPackages = false;
}

If this results in a measurable eval perf impact, then I think overrideLlvmPackagesScope = llvmPackages: f: llvmPackages.overrideScope f // { override = g: overrideLlvmPackagesScope (llvmPackages.override g) f; }; would avoid that at the expense of being less pleasant, but it probably isn’t necessary.

This worked though.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I pushed a commit that incorporates the second fix. llvmPackages.override { } should equal llvmPackages, and llvmPackages.overrideScope (_: _: { }) should equal it modulo the lost override issue.

Comment thread pkgs/stdenv/darwin/default.nix Outdated
Comment on lines +1169 to +1179
// lib.optionalAttrs (super.stdenv.targetPlatform == localSystem) {
# Make sure the following are all the same in the local == target case:
# - clang
# - llvmPackages.stdenv.cc
# - llvmPackages.systemLibcxxClang.
# - llvmPackages.clang
# - stdenv.cc
systemLibcxxClang = prevStage."llvmPackages_${llvmVersion}".systemLibcxxClang.override {
cc = finalLLVM.clang-unwrapped;
};
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Do we definitely want this to be // and not .overrideScope? Again it seems like llvmPackages.override { } and llvmPackages.overrideScope { } should both be identical to llvmPackages.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It’s in the overrideScope.

Comment thread pkgs/stdenv/darwin/default.nix Outdated
Comment on lines +1153 to +1164
{
inherit (prevStage."llvmPackages_${llvmVersion}") libllvm;
compiler-rt = prevStage."llvmPackages_${llvmVersion}".compiler-rt.override {
inherit (finalLLVM) libllvm;
};
libclang = prevStage."llvmPackages_${llvmVersion}".libclang.override {
inherit (finalLLVM) libllvm;
};
lld = prevStage."llvmPackages_${llvmVersion}".lld.override {
inherit (finalLLVM) libllvm;
};
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Following on from my previous comment, I believe this means that (llvmPackages_21.override { }).compiler-rt is not the same as llvmPackages_21.compiler-rt, which seems very surprising.

I think this also means that (llvmPackages_21.override { libxml2 = …; }).libclang doesn’t work correctly, because the prevStage package set doesn’t get the overrides. llvmPackages_21.libclang.tests.withoutOptionalFeatures also seems like it would be incorrect in the presence of various kinds of overrides.

This is bootstrap build avoidance, right? I am wondering if the cure here might not be worse than the disease. It seems difficult to make dependency overrides propagate correctly while keeping things “incorrectly” one stage back in the default case, but maybe there’s a way we can do this closer to the root that would work reliably?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I’ll reply more later when I’m at home, but this isn’t bootstrap avoidance. It’s so that overriding these does what you expect. Otherwise, overriding (for example) libllvm won’t rebuild dependent packages, which is surprising behavior.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I see what you’re saying with llvmPackages.override { } not being equal to llvmPackages, but llvmPackages.overrideScope (_: _: { }) is identical except for the attributes that overrideScope drops. Even if we overlay nothing, scope.overrideScope (_: _: { }) will not equal scope. It should, but fixing that is outside the scope of this PR.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thinking about the “correct” way to do the bootstrap, it might be to drop the overlays completely and make buildPackages equal to prevStage. You’d probably need to do that a few times to go from the bootstrap tools to the final stage package set (assuming you want the final stage package set built with “its” compiler and not with the bootstrap one). That’s way outside the scope of this PR, and I’d prefer to work on other Darwin stuff before even messing with something like that.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

See above re: the llvmPackages vs. override vs. overrideScope issue.

@reckenrode reckenrode force-pushed the darwin-stdenv-cleanup branch 4 times, most recently from 300ac9a to 73f9135 Compare December 21, 2025 13:22
@reckenrode reckenrode force-pushed the darwin-stdenv-cleanup branch 4 times, most recently from 0e0d2c4 to 0b825fd Compare January 4, 2026 11:55
It’s not ideal that the version has to be hardcoded, but right now it’s
not recorded in any configuration accessible from the bootstrap outside
of a package set. If that changes in the future, this should be changed
to get `llvmVersion` from that source.
The LLVM derivation defines `llvm` as an alias for `libllvm`. The same
is true for `clang-unwrapped` and `clang`. Doing the overrides this way
allows overrides to work as expected (especially at the end).
This change allows `overrideScope` to work correctly outside of the
bootstrap. Packages have to be overriden manually with the final
`libllvm` to make sure any overrides to `libllvm` are picked up. The
workaround is being dropped because rebuilds are expected.

Note that the top-level `clang` is being dropped because it is an alias
for `llvmPackages.clang`. Not overlaying it preserves the expected
behavior if someone overlays `llvmPackages.clang` in their config (which
is that the top-level one will be the same derivation as the overlay).
The Darwin bintools were moved to have all `darwin` packages in the same
place. The LLVM ones are using the original, unaliased names.
Building `expand-response-params` early in the bootstrap will allow the
custom wrappers (for `cc` and `bintools`) to be dropped.
This removes the need to keep these in sync, which should (hopefully)
simplify maintenance.
This is the libc++ from the bootstrap. Once the past the xclang stage,
the bootstrap uses the system libc++. Unfortunately, it doesn’t seem to
work with the bootstrap tools.
@reckenrode reckenrode force-pushed the darwin-stdenv-cleanup branch from 0b825fd to 2441745 Compare January 11, 2026 17:08
@reckenrode reckenrode removed the backport staging-25.11 Backport PR automatically label Jan 11, 2026
@reckenrode
Copy link
Copy Markdown
Contributor Author

I dropped the backport label and rebased on current staging. @emilazy, is there anything else needed to move this forward?

@nixpkgs-ci nixpkgs-ci Bot requested review from Samasaur1 and emilazy January 11, 2026 17:14
Copy link
Copy Markdown
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.

Looks like this is working about as well as can be expected under the current system now, thanks!

Comment thread pkgs/stdenv/darwin/default.nix Outdated
Comment on lines +48 to +55
# Workaround for losing `override` after using `overrideScope`
# https://github.com/NixOS/nixpkgs/issues/447012
overrideLlvmPackagesScope =
llvmPackages: f:
llvmPackages.overrideScope f
// {
override = g: overrideLlvmPackagesScope (llvmPackages.override g) f;
};
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Could you try:

Suggested change
# Workaround for losing `override` after using `overrideScope`
# https://github.com/NixOS/nixpkgs/issues/447012
overrideLlvmPackagesScope =
llvmPackages: f:
llvmPackages.overrideScope f
// {
override = g: overrideLlvmPackagesScope (llvmPackages.override g) f;
};
# Workaround for losing `override` after using `overrideScope`
# https://github.com/NixOS/nixpkgs/issues/447012
overrideLlvmPackagesScope =
llvmPackages: f:
let
override = args: (llvmPackages.override args).overrideScope f;
in
lib.makeOverridable (lib.mirrorFunctionArgs llvmPackages.override override) { };

I think this will achieve the same thing but in a more override‐native way; I just messed up the arguments last time. If it still doesn’t work, feel free to just merge.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It seemed to work. I pushed an update using this method instead.

let
inherit (localSystem) system;

llvmVersion = "21"; # This needs to be updated when the default LLVM version is changed.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I assume we can’t get this out of the package set for infrec reasons?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We don’t have any way of knowing what the default version actually is at this point because we don’t have a package set.

@github-project-automation github-project-automation Bot moved this to In Progress in Stdenv Jan 14, 2026
@nixpkgs-ci nixpkgs-ci Bot added the 12.approvals: 1 This PR was reviewed and approved by one person. label Jan 14, 2026
Co-authored-by: Emily <vcs@emily.moe>
@reckenrode reckenrode force-pushed the darwin-stdenv-cleanup branch from 2441745 to ad2b074 Compare January 15, 2026 00:42
@emilazy emilazy added this pull request to the merge queue Jan 15, 2026
Merged via the queue into NixOS:staging with commit c802db0 Jan 15, 2026
29 of 31 checks passed
@github-project-automation github-project-automation Bot moved this from In Progress to Done in Stdenv Jan 15, 2026
@reckenrode reckenrode deleted the darwin-stdenv-cleanup branch January 18, 2026 12:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

6.topic: darwin Running or building packages on Darwin 6.topic: stdenv Standard environment 10.rebuild-darwin: 501+ This PR causes many rebuilds on Darwin and should normally target the staging branches. 10.rebuild-darwin: 5001+ This PR causes many rebuilds on Darwin and must target the staging branches. 10.rebuild-darwin-stdenv This PR causes stdenv to rebuild on Darwin and must target a staging branch. 10.rebuild-linux: 0 This PR does not cause any packages to rebuild on Linux. 12.approvals: 1 This PR was reviewed and approved by one person.

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants