buildEnv: construct with structured attributes and pass chosenOutputs directly

This commit is contained in:
Yueh-Shun Li
2025-08-17 22:50:58 +08:00
committed by Philip Taron
parent 0efccf1dd9
commit ed96bfd307
6 changed files with 85 additions and 76 deletions
@@ -12,6 +12,8 @@ For example, [`python.withPackage`](#attributes-on-interpreters-packages) is bas
Unless otherwise noted, arguments can be overridden directly using [`<pkg>.overrideAttrs`](#sec-pkg-overrideAttrs).
`buildEnv` enforces [structured attributes (`{ __structuredAttrs = true; }`)](https://nix.dev/manual/nix/2.18/language/advanced-attributes.html#adv-attr-structuredAttrs).
- `name` or `pname` and `version` (required):
The name of the environment.
+2
View File
@@ -80,6 +80,8 @@
- Note that the above `nodePackages` removal also coincides with the removal of `node2nix` and its tooling, which have been deprecated for a long time.
- `buildEnv`-constructed packages now take only [structured attributes (`{ __structuredAttrs = true; }`)](https://nix.dev/manual/nix/2.18/language/advanced-attributes.html#adv-attr-structuredAttrs).
- `xfce.mkXfceDerivation` has been deprecated (i.e. conditioned behind `nixpkgs.config.allowAliases`)
and will be removed in NixOS 26.11, please use `stdenv.mkDerivation` directly. You can migrate by
adding `pkg-config`, `xfce4-dev-tools`, and `wrapGAppsHook3` to your `nativeBuildInputs` and
+38 -26
View File
@@ -12,10 +12,35 @@ STDOUT->autoflush(1);
$SIG{__WARN__} = sub { warn "pkgs.buildEnv warning: ", @_ };
$SIG{__DIE__} = sub { die "pkgs.buildEnv error: ", @_ };
my $out = $ENV{"out"};
my $extraPrefix = $ENV{"extraPrefix"};
my $out;
my $extraPrefix;
my @pathsToLink;
my $ignoreCollisions;
my $checkCollisionContents;
my $ignoreSingleFileOutputs;
my @chosenOutputsRefs;
my $extraPathsFrom;
my $manifest;
my @pathsToLink = @{decode_json $ENV{"pathsToLinkJSON"}};
if ($ENV{"NIX_ATTRS_JSON_FILE"} // "") {
open FILE, $ENV{"NIX_ATTRS_JSON_FILE"} or die "cannot open structured attrs JSON file $ENV{NIX_ATTRS_JSON_FILE}: $!";
my $json_text = do { local $/; <FILE> };
my $attrsFromJSONRef = decode_json $json_text;
close FILE;
my $outputsRef = $attrsFromJSONRef->{"outputs"};
$out = $outputsRef->{"out"} // (values %{$outputsRef})[0];
$extraPrefix = $attrsFromJSONRef->{"extraPrefix"};
@pathsToLink = @{$attrsFromJSONRef->{"pathsToLink"}};
$ignoreCollisions = $attrsFromJSONRef->{"ignoreCollisions"};
$checkCollisionContents = $attrsFromJSONRef->{"checkCollisionContents"};
$ignoreSingleFileOutputs = $attrsFromJSONRef->{"ignoreSingleFileOutputs"};
@chosenOutputsRefs = @{$attrsFromJSONRef->{"chosenOutputs"}};
$extraPathsFrom = $attrsFromJSONRef->{"extraPathsFrom"};
$manifest = $attrsFromJSONRef->{"manifest"};
} else {
die "missing required environment variable NIX_ATTRS_JSON_FILE";
}
sub isInPathsToLink($path) {
$path = "/" if $path eq "";
@@ -210,26 +235,15 @@ sub addPkg($pkgDir, $ignoreCollisions, $checkCollisionContents, $priority, $igno
}
}
# Read packages list.
my $pkgs;
if (exists $ENV{"pkgsPath"}) {
open FILE, $ENV{"pkgsPath"};
$pkgs = <FILE>;
close FILE;
} else {
$pkgs = $ENV{"pkgs"}
}
# Symlink to the packages that have been installed explicitly by the
# user.
for my $pkg (@{decode_json $pkgs}) {
for my $pkg (@chosenOutputsRefs) {
for my $path (@{$pkg->{paths}}) {
addPkg($path,
$ENV{"ignoreCollisions"} eq "1",
$ENV{"checkCollisionContents"} eq "1",
$ignoreCollisions,
$checkCollisionContents,
$pkg->{priority},
$ENV{"ignoreSingleFileOutputs"} eq "1")
$ignoreSingleFileOutputs)
if -e $path;
}
}
@@ -244,21 +258,20 @@ while (scalar(keys %postponed) > 0) {
my @pkgDirs = keys %postponed;
%postponed = ();
foreach my $pkgDir (sort @pkgDirs) {
addPkg($pkgDir, 2, $ENV{"checkCollisionContents"} eq "1", $priorityCounter++, $ENV{"ignoreSingleFileOutputs"} eq "1");
addPkg($pkgDir, 2, $checkCollisionContents, $priorityCounter++, $ignoreSingleFileOutputs);
}
}
my $extraPathsFilePath = $ENV{"extraPathsFrom"};
if ($extraPathsFilePath) {
open FILE, $extraPathsFilePath or die "cannot open extra paths file $extraPathsFilePath: $!";
if ($extraPathsFrom) {
open FILE, $extraPathsFrom or die "cannot open extra paths file $extraPathsFrom: $!";
while(my $line = <FILE>) {
chomp $line;
addPkg($line,
$ENV{"ignoreCollisions"} eq "1",
$ENV{"checkCollisionContents"} eq "1",
$ignoreCollisions,
$checkCollisionContents,
1000,
$ENV{"ignoreSingleFileOutputs"} eq "1")
$ignoreSingleFileOutputs)
if -d $line;
}
@@ -286,7 +299,6 @@ foreach my $relName (sort keys %symlinks) {
print STDERR "created $nrLinks symlinks in user environment\n";
my $manifest = $ENV{"manifest"};
if ($manifest) {
symlink($manifest, "$out/manifest") or die "cannot create manifest";
}
+29 -34
View File
@@ -92,6 +92,25 @@ lib.makeOverridable (
// lib.optionalAttrs (args ? buildInputs) {
inherit buildInputs;
};
in
compatArgs
// derivationArgs
// {
# Explicitly opt in: builder.pl reads all configuration from file $ENV["NIX_ATTRS_JSON_FILE"].
__structuredAttrs = true;
inherit
extraOutputsToInstall
manifest
ignoreCollisions
checkCollisionContents
ignoreSingleFileOutputs
includeClosures
meta
pathsToLink
extraPrefix
postBuild
;
chosenOutputs = map (drv: {
paths =
@@ -115,43 +134,19 @@ lib.makeOverridable (
# Silently use the original `paths` if `passthru.paths` is missing.
}) finalAttrs.passthru.paths or paths;
pathsForClosure = lib.pipe chosenOutputs [
(map (p: p.paths))
lib.flatten
(lib.remove null)
];
in
compatArgs
// derivationArgs
// {
inherit
extraOutputsToInstall
manifest
ignoreCollisions
checkCollisionContents
ignoreSingleFileOutputs
includeClosures
meta
pathsToLink
extraPrefix
postBuild
;
pathsToLinkJSON = builtins.toJSON pathsToLink;
pkgs = builtins.toJSON chosenOutputs;
extraPathsFrom = lib.optionalString finalAttrs.includeClosures (writeClosure pathsForClosure);
# Explicitly opt out: builder.pl reads all configuration from %ENV,
# which is fundamentally incompatible with __structuredAttrs = true.
__structuredAttrs = false;
extraPathsFrom = lib.optionalString finalAttrs.includeClosures (
let
pathsForClosure = lib.pipe finalAttrs.chosenOutputs [
(map (p: p.paths))
lib.flatten
(lib.remove null)
];
in
writeClosure pathsForClosure
);
preferLocalBuild = derivationArgs.preferLocalBuild or true;
allowSubstitutes = derivationArgs.allowSubstitutes or false;
passAsFile = [
"buildCommand"
]
# XXX: The size is somewhat arbitrary
++ lib.optional (lib.stringLength finalAttrs.pkgs >= 128 * 1024) "pkgs"
++ derivationArgs.passAsFile or [ ];
buildCommand = ''
${buildPackages.perl}/bin/perl -w ${builder}
+10 -10
View File
@@ -275,16 +275,16 @@ let
touch $out
'';
# buildEnv explicitly sets __structuredAttrs = false because builder.pl
# reads all inputs from environment variables. Verify the build succeeds
# even when derivationArgs tries to enable structuredAttrs.
# buildEnv explicitly sets __structuredAttrs = true because builder.pl
# reads all inputs from `$NIX_ATTRS_JSON_FILE`.
# Verify the build succeeds even when derivationArgs tries to disable structuredAttrs.
structuredAttrs-overridden =
pkgs.runCommand "test-buildenv-structuredAttrs-overridden"
{
testEnv = buildEnv {
name = "test-env-structuredAttrs";
paths = [ pkgs.hello ];
derivationArgs.__structuredAttrs = true;
derivationArgs.__structuredAttrs = false;
};
}
''
@@ -313,9 +313,9 @@ let
'';
};
# buildEnv's builder.pl reads all inputs from %ENV, which is
# fundamentally incompatible with __structuredAttrs = true.
# buildEnv explicitly forces __structuredAttrs = false.
# buildEnv's builder.pl reads all inputs from `$NIX_ATTRS_JSON_FILE`,
# which requires __structuredAttrs = true.
# buildEnv explicitly forces __structuredAttrs = true.
tests-structuredAttrs = {
testStructuredAttrsExplicitlyFalse = {
expr =
@@ -323,7 +323,7 @@ let
name = "test-env";
paths = [ ];
}).__structuredAttrs;
expected = false;
expected = true;
};
testStructuredAttrsCantBeOverriddenViaDerivationArgs = {
@@ -331,9 +331,9 @@ let
(buildEnv {
name = "test-env";
paths = [ ];
derivationArgs.__structuredAttrs = true;
derivationArgs.__structuredAttrs = false;
}).__structuredAttrs;
expected = false;
expected = true;
};
};
@@ -123,15 +123,13 @@ installtl_do_path_adjustments () {
ln -s "$texmfdist" "$out"/share/texmf
# generate other outputs
local otherOutput otherOutputName
local otherOutputs="$otherOutputs"
for otherOutputName in $outputs ; do
local otherOutputIdx=0 otherOutputName
for otherOutputName in "${!outputs[@]}" ; do
if [[ $otherOutputName == out ]] ; then
continue
fi
otherOutput="${otherOutputs%% *}"
otherOutputs="${otherOutputs#* }"
ln -s "$otherOutput" "${!otherOutputName}"
ln -s "${otherOutputs[$otherOutputIdx]}" "${outputs[$otherOutputName]}"
((otherOutputIdx++)) || true
done
}