Skip to content

buildEnv: support fixed-point arguments#432957

Open
ShamrockLee wants to merge 16 commits intoNixOS:stagingfrom
ShamrockLee:buildenv-overriding
Open

buildEnv: support fixed-point arguments#432957
ShamrockLee wants to merge 16 commits intoNixOS:stagingfrom
ShamrockLee:buildenv-overriding

Conversation

@ShamrockLee
Copy link
Contributor

@ShamrockLee ShamrockLee commented Aug 12, 2025

  1. This PR restructure buildEnv with lib.extendMkDerivation, stdenvNoCC.mkDerivation, and buildCommand. buildEnv can now take fixed-point arguments (finalAttrs: { }).
  2. We still rely on the custom overrider <env-pkg>.override, as fixing the __structuredAttrs = true support might change the <pkg>.overrideAttrs interface.
    PR buildEnv: use structured attribute and support <pkg>.overrideAttrs #434815 proves that the argument overriding is mostly the same after adopting structured attributes. Let's deprecate the custom overrider <env-pkg>.override right away, but keep it until tree-wide transition to <pkg>.overrideAttrs.
  3. This PR introduces the derivationArgs argument for buildEnv so that we won't have to exhaustively list all the stdenv.mkDerivation arguments users might wish to pass.
  4. If tested, this PR minus the release note can be backported.

This PR depends on:

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.

@nixpkgs-ci nixpkgs-ci bot added 10.rebuild-linux: 501+ This PR causes many rebuilds on Linux and should normally target the staging branches. 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. 10.rebuild-linux: 5001+ This PR causes many rebuilds on Linux and must target the staging branches. 8.has: changelog This PR adds or changes release notes 8.has: documentation This PR adds or changes documentation labels Aug 12, 2025
@ShamrockLee ShamrockLee force-pushed the buildenv-overriding branch 2 times, most recently from bb27fcf to 09aa8ad Compare August 12, 2025 10:56
@ShamrockLee ShamrockLee changed the title buildEnv: support fixed-point arguments and <pkg>.overrideAttrs buildEnv: support fixed-point arguments Aug 13, 2025
@ShamrockLee
Copy link
Contributor Author

@ofborg build nixpkgs-vet

@ShamrockLee
Copy link
Contributor Author

ShamrockLee commented Aug 14, 2025

I don't know why the CI complains that nixpkgs-vet fails to build. It builds locally. There seemed to be a network issue when the CI vetting jobs run.

Update: Rerun. They are now green.

@ShamrockLee ShamrockLee marked this pull request as ready for review August 14, 2025 08:21
@nix-owners nix-owners bot requested review from Ch1keen and philiptaron August 14, 2025 08:21
@ShamrockLee ShamrockLee requested a review from drupol August 14, 2025 08:27
@philiptaron
Copy link
Contributor

Taking a look. Is there a version of these that doesn't rebuild everything?

@Ch1keen
Copy link
Member

Ch1keen commented Aug 14, 2025

Would take a look. But it is first time reviewing the core of the Nix builtin function, are there any ways that I can test with this change? Would building derivations with the change be helpful?

Copy link
Contributor

@philiptaron philiptaron left a comment

Choose a reason for hiding this comment

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

My sense is that the rebuilds come about because of the switch from runCommand to stdenvNoCC.mkDerivation. Is that right?

The order of the merge operations (derivationArgs first, then applying the functions arguments) gives me some heartburn. It's always odd to have something set in derivationArgs which then doesn't take effect. It's sort of a bug factory. The other way is also perilous however, as certain derivationArgs are actually invalid for this build helper.

My preferred way to handle this is to use something like intersectAttrs to forbid a few things in derivationAttrs that would malform the resulting derivation. See replaceVarsWith for an example of that. Thanks to @wolfgangwalther for the pattern.

@philiptaron
Copy link
Contributor

I'm also interested in how we deprecate and remove the lib.makeOverrideable call.

@ShamrockLee
Copy link
Contributor Author

ShamrockLee commented Aug 14, 2025

Taking a look. Is there a version of these that doesn't rebuild everything?

It might be doable by excluding some arguments, but excluding paths from the passing arguments makes it impossible to get its final state from finalAttrs and contradicts the purpose of this PR.

My sense is that the rebuilds come about because of the switch from runCommand to stdenvNoCC.mkDerivation. Is that right?

It's not the main reason of the mass rebuild. The gap between stdenvNoCC.mkDerivation and runCommand can be bridged by prefixing buildCommand to passAsFile (which I think is a good idea and is going to implement).

@ShamrockLee ShamrockLee force-pushed the buildenv-overriding branch 2 times, most recently from 9b0afe2 to aa291a2 Compare August 14, 2025 17:33
@ShamrockLee
Copy link
Contributor Author

ShamrockLee commented Aug 14, 2025

I'm also interested in how we deprecate and remove the lib.makeOverrideable call.

We can support structured attributes while keeping <pkg>.overrideAttrs unchanged by refactoring buildenv/builder.pl to get variables from the JSON file whose path is inside the environment variable NIX_ATTRS_JSON_FILE if such environment variable is available and nonempty, and falling back to the old behaviour of getting from environment variables of the same names.

Cc: @wolfgangwalther

@ShamrockLee
Copy link
Contributor Author

Would take a look. But it is first time reviewing the core of the Nix builtin function,

Thank you for helping out! To be precise, buildEnv is provided by Nixpkgs instead of Nix.

are there any ways that I can test with this change? Would building derivations with the change be helpful?

Maybe we could build some packages given buildEnv's wide adoption, or build some custom packages constructed by buildEnv.

For example,

nix build --no-link --print-out-paths -L --impure --expr "
  let
    pkgs = import ./. { };
  in
  pkgs.buildEnv (finalAttrs: {
    paths = with pkgs; [ hello figlet ];
  })
"

and compare its output contents with that of hello and figlet.

I have little idea how to test it thoroughly, either. I tried to write some test cases when working on ignoreSingleFileOutputs, but it turned out to be harder than I had thought.

Copy link
Contributor

@wolfgangwalther wolfgangwalther left a comment

Choose a reason for hiding this comment

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

We can support structured attributes while keeping <pkg>.overrideAttrs unchanged by refactoring buildenv/builder.pl to get variables from the JSON file whose path is inside the environment variable NIX_ATTRS_JSON_FILE if such environment variable is available and nonempty, and falling back to the old behaviour of getting from environment variables of the same names.

Sounds like the right approach. But since you're mentioning overrideAttrs explicitly, it sounds like there is another way of fixing structuredAttrs support which would not keep overrideAttrs the same? Which way would that be?

Comment on lines +21 to +22
# We currently still rely on this custom overrider `<env-pkg>.override`,
# as fixing the structured attribute support would change the `<pkg>.overrideAttrs` interface.
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is "changing the overrideAttrs interface" considered to be a problem?

In which way would this interface change?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The approaches I considered includes:

  1. Moving under env the variables to pass to the builder. This way we don't need to change the Perl build script.

  2. Enforce __structuredAttrs = true for all constructed packages and pass everythong consistently via NIX_ATTRS_JSON_FILE. This way we avoid the manual serialization of [ { paths = ; priority = ; } ] completely.

Copy link
Contributor

Choose a reason for hiding this comment

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

I looked into the builder a bit, and I think I'd prefer this:

2. Enforce __structuredAttrs = true for all constructed packages and pass everythong consistently via NIX_ATTRS_JSON_FILE. This way we avoid the manual serialization of [ { paths = ; priority = ; } ] completely.

This would not change the overrideAttrs interface either, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This would not change the overrideAttrs interface either, right?

Yes, it won't change the overriding interface. (I personally feel like replacing env.pkgs with a structual env.pathSpecs and make it part of the public interface, so that people could add paths with custom priority.)

Nevertheless, it's a tree-wide backward-incompatible change whose impact spreads across several language- and framework-specific toolchain. Python's python3Packagrs.buildEnv is currently a pkgs.buildEnv-constructed package, and python3.withPackabes is implemented by overriding python3Packages.buildEnv. In addition, it provides postBuild to allow appending custom commands. In the worst case scenario, we'll have to test dependent packages and frameworks one by one and do our best to inform users about the breakage.

Copy link
Contributor

Choose a reason for hiding this comment

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

Nevertheless, it's a tree-wide backward-incompatible change

Wait, which one? Enforcing structured attrs or the pathSpecs you brought into play?

I think enforcing structuredAttrs should not be breaking, right?

Copy link
Contributor

Choose a reason for hiding this comment

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

Since we set buildCommand, the phases will not be run. I think this means that setup hooks won't be run at all.

So we just need to deal with postBuild, which is manually ran in the buildCommand.

So the breaking change would be, because the structured attrs file is not additionally loaded as bash variables?

I think this would actually already happen through stdenv/setup.sh.

So I don't see the breaking part, yet.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It just came to me that we usually don't provide bavkward compatibility for pre- or post- stuff. Maybe we could try it on the master branch, whose users are expected to tolerate breakage, and fix things that break.

Copy link
Contributor Author

@ShamrockLee ShamrockLee Aug 16, 2025

Choose a reason for hiding this comment

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

Since we set buildCommand, the phases will not be run. I think this means that setup hooks won't be run at all.

So we just need to deal with postBuild, which is manually ran in the buildCommand.

So the breaking change would be, because the structured attrs file is not additionally loaded as bash variables?

I think this would actually already happen through stdenv/setup.sh.

So I don't see the breaking part, yet.

Ah, i see.

I thought that a user might pass some random attributes and expects them to be passed to buildCommand as environment variables, but that is not possible without the derivationArgs argument introduced in this PR or overrideAttrs. Since both are not currently supported, there shouldn't be backward incompatibility issues.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As for the setup hooks that still fail to support structual attrs, let's consider such failure their bugs.

Copy link
Contributor

Choose a reason for hiding this comment

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

As for the setup hooks that still fail to support structural attrs, let's consider such failures to be their bugs.

Quite so. I agree with both you and Wolfgang this is the direction the PR ought to go.

@ShamrockLee ShamrockLee force-pushed the buildenv-overriding branch 3 times, most recently from 84b1c5a to 75428e1 Compare August 17, 2025 19:08
@nixpkgs-ci nixpkgs-ci bot added 10.rebuild-linux: 501+ This PR causes many rebuilds on Linux and should normally target the staging branches. 10.rebuild-darwin: 501+ This PR causes many rebuilds on Darwin and should normally target the staging branches. 10.rebuild-linux-stdenv This PR causes stdenv to rebuild on Linux and must target a staging branch. and removed 10.rebuild-darwin: 1 This PR causes 1 package to rebuild on Darwin. labels Nov 4, 2025
@ShamrockLee
Copy link
Contributor Author

I fixed the evaluation by fixing the check-meta defaultCPEParts with PR #458529
I'll rebase once it is merged and comes to the staging branch.

Copy link
Contributor

@RossSmyth RossSmyth left a comment

Choose a reason for hiding this comment

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

Very nice PR, would be cool to see it.

eval "$postBuild"
''
++ lib.optional (lib.stringLength finalAttrs.pkgs >= 128 * 1024) "pkgs"
++ derivationArgs.passAsFile or [ ];
Copy link
Contributor

Choose a reason for hiding this comment

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

I will note that passAsFile is not very compatible with structuredAttrs, so the the goal is to move buildEnv to structuredAttrs, this should probably be removed.

  1. When __structuredAttrs = false, one can find values via $myAttrPath set in the environment, while $myAttr is unset
  2. When __structuredAttrs = true, $myAttrPath is unset, and myAttr contains the value that would be in the file

So I would not recommend exposing that to users.

Copy link
Contributor

@philiptaron philiptaron left a comment

Choose a reason for hiding this comment

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

I added a bunch of tests. Please take a look.

philiptaron and others added 16 commits February 16, 2026 16:15
Add an eval-time test harness for buildEnv following the pattern established by `tests.overriding`.
Uses `lib.runTests` to compare expr/expected pairs, and the buildCommand reports pass/fail.

No tests yet; subsequent commits add them individually.
Verify that:
- `name` argument is used directly as the derivation name
- `pname` + `version` composes into the derivation name
- Omitting all three triggers the buildEnv-specific assertion

The assertion test uses `builtins.tryEval` + `builtins.seq` to force evaluation of .drvPath and confirm it throws.
Specify `extraPathsFrom` as a string using `lib.optionalString` instead of a singleton list using `lib.optional` to align with the current behavior of the Perl build script.
Merge derivationArgs.passthru into the passthru chain so that passthru attributes passed via derivationArgs are not silently dropped.
- includeClosure -> includeClosures (matching the actual argument name)
- extraPrefix default: [ ] -> "" (matching the actual default)
- Fix broken sentence about ignoreSingleFileOutputs
buildEnv stashes `paths` into `passthru.paths` (rather than passing it directly as a derivation attribute) to prevent unexpected string context pollution. Verify that:
- paths are accessible via passthru.paths
- passthru.paths can be overridden with overrideAttrs
This is the core new capability of the PR: `buildEnv` now accepts a function `(finalAttrs: { ... })` enabling self-referencing attributes.

Verify that finalAttrs.name is accessible from within the argument function.
Verify that overrideAttrs on a buildEnv result:

- Can modify passthru attributes
- Preserves the derivation name
- Produces a different derivation when a build-affecting attribute (postBuild) is changed
buildEnv merges passthru from three sources:
  `{ inherit paths; } // derivationArgs.passthru // passthru`

Verify that:
- Auto-injected paths are present in passthru
- derivationArgs.passthru attributes are preserved
- Direct passthru attributes are preserved
- Direct passthru takes precedence over derivationArgs.passthru
Verify that:
- derivationArgs attributes reach the underlying mkDerivation (tested via allowSubstitutes overriding the default false)
- The backward-compat layer for top-level nativeBuildInputs still works (these are forwarded through compatArgs)
Add derivations that actually build a buildEnv and verify the output tree.
Exposed as passthru.buildTests so they can be built individually or all at once.

- basic-symlinking: single package produces correct symlink structure
- pathsToLink: only specified subdirectories appear in output
- extraPrefix: output is rooted under the specified prefix
- postBuild: shell commands execute after the symlink tree
- ignoreCollisions: duplicate paths succeed when ignoreCollisions=true
Copy link
Member

Choose a reason for hiding this comment

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

This file does not look like it belongs in here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I guess it is a leftover during some git-rebase.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Noticed that the commit is authored by @philiptaron. As I have too much on my plate to catch up on these changes , I'll keep it as is.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, this is my mistake… I am occasionally a noob with git worktrees, and this is me reusing one that I shouldn't have.

@ShamrockLee
Copy link
Contributor Author

I'm tided up with my research and thesis. Sorry for leaving halfway. Many thanks to @philiptaron for pushing this PR.

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

Labels

6.topic: python Python is a high-level, general-purpose programming language. 8.has: changelog This PR adds or changes release notes 8.has: documentation This PR adds or changes documentation 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: 501+ This PR causes many rebuilds on Linux and should normally target the staging branches. 10.rebuild-linux: 5001+ This PR causes many rebuilds on Linux and must target the staging branches. 12.approvals: 1 This PR was reviewed and approved by one person.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants