stdenv, trivial-builders: replace // optionalAttrs with nullable attr names

Follow-up to #430969. Replace remaining `// optionalAttrs` patterns with
`${if cond then "name" else null} = value;` to avoid per-derivation
closure allocations and `//` merge operations.

Changes:
- make-derivation.nix: inline Darwin, Windows, outputChecks blocks and
  the `env'`/`__structuredAttrs` patterns in mkDerivationSimple
- trivial-builders: inline optionalAttrs in runCommandWith and symlinkJoin
This commit is contained in:
Aliaksandr
2026-04-05 01:34:28 +03:00
parent 99dd1abc8e
commit 3bdc867a84
2 changed files with 214 additions and 199 deletions
@@ -80,8 +80,8 @@ rec {
inherit buildCommand name;
passAsFile = [ "buildCommand" ] ++ (derivationArgs.passAsFile or [ ]);
}
// lib.optionalAttrs (!derivationArgs ? meta) {
pos =
// {
${if !derivationArgs ? meta then "pos" else null} =
let
args = builtins.attrNames derivationArgs;
in
@@ -89,11 +89,9 @@ rec {
builtins.unsafeGetAttrPos (builtins.head args) derivationArgs
else
null;
${if runLocal then "preferLocalBuild" else null} = true;
${if runLocal then "allowSubstitutes" else null} = false;
}
// (lib.optionalAttrs runLocal {
preferLocalBuild = true;
allowSubstitutes = false;
})
// removeAttrs derivationArgs [ "passAsFile" ]
);
@@ -567,8 +565,8 @@ rec {
${postBuild}
'';
}
// lib.optionalAttrs (!args ? meta) {
pos =
// {
${if !args ? meta then "pos" else null} =
if args ? pname then
builtins.unsafeGetAttrPos "pname" args
else
+208 -191
View File
@@ -1,3 +1,11 @@
# PERF: This file is evaluated for every derivation in the build closure.
# Avoid `// optionalAttrs` — each call allocates a closure, an intermediate
# attrset, and a `//` merge. Use nullable attribute names instead:
#
# ${if cond then "name" else null} = value;
#
# See https://github.com/NixOS/nixpkgs/pull/430969 for measurements.
{ lib, config }:
stdenv:
@@ -28,7 +36,6 @@ let
mapAttrs
mapNullable
optional
optionalAttrs
optionalString
optionals
pipe
@@ -530,220 +537,228 @@ let
]
];
derivationArg =
removeAttrs attrs removedOrReplacedAttrNames
// {
${if (attrs ? name || (attrs ? pname && attrs ? version)) then "name" else null} =
let
# Indicate the host platform of the derivation if cross compiling.
# Fixed-output derivations like source tarballs shouldn't get a host
# suffix. But we have some weird ones with run-time deps that are
# just used for their side-affects. Those might as well since the
# hash can't be the same. See #32986.
hostSuffix = optionalString (!dontAddHostSuffix) stdenvHostSuffix;
derivationArg = removeAttrs attrs removedOrReplacedAttrNames // {
${if (attrs ? name || (attrs ? pname && attrs ? version)) then "name" else null} =
let
# Indicate the host platform of the derivation if cross compiling.
# Fixed-output derivations like source tarballs shouldn't get a host
# suffix. But we have some weird ones with run-time deps that are
# just used for their side-affects. Those might as well since the
# hash can't be the same. See #32986.
hostSuffix = optionalString (!dontAddHostSuffix) stdenvHostSuffix;
# Disambiguate statically built packages. This was originally
# introduce as a means to prevent nix-env to get confused between
# nix and nixStatic. This should be also achieved by moving the
# hostSuffix before the version, so we could contemplate removing
# it again.
staticMarker = stdenvStaticMarker;
in
lib.strings.sanitizeDerivationName (
if attrs ? name then
attrs.name + hostSuffix
else
# we cannot coerce null to a string below
assert assertMsg (
attrs ? version && attrs.version != null
) "The `version` attribute cannot be null.";
"${attrs.pname}${staticMarker}${hostSuffix}-${attrs.version}"
);
builder = attrs.realBuilder or stdenvShell;
args =
attrs.args or [
"-e"
./source-stdenv.sh
(attrs.builder or ./default-builder.sh)
];
inherit stdenv;
# The `system` attribute of a derivation has special meaning to Nix.
# Derivations set it to choose what sort of machine could be used to
# execute the build, The build platform entirely determines this,
# indeed more finely than Nix knows or cares about. The `system`
# attribute of `buildPlatform` matches Nix's degree of specificity.
# exactly.
system = buildPlatformSystem;
inherit userHook;
__ignoreNulls = true;
inherit __structuredAttrs strictDeps;
depsBuildBuild = elemAt (elemAt dependencies 0) 0;
nativeBuildInputs = elemAt (elemAt dependencies 0) 1;
depsBuildTarget = elemAt (elemAt dependencies 0) 2;
depsHostHost = elemAt (elemAt dependencies 1) 0;
buildInputs = elemAt (elemAt dependencies 1) 1;
depsTargetTarget = elemAt (elemAt dependencies 2) 0;
depsBuildBuildPropagated = elemAt (elemAt propagatedDependencies 0) 0;
propagatedNativeBuildInputs = elemAt (elemAt propagatedDependencies 0) 1;
depsBuildTargetPropagated = elemAt (elemAt propagatedDependencies 0) 2;
depsHostHostPropagated = elemAt (elemAt propagatedDependencies 1) 0;
propagatedBuildInputs = elemAt (elemAt propagatedDependencies 1) 1;
depsTargetTargetPropagated = elemAt (elemAt propagatedDependencies 2) 0;
# This parameter is sometimes a string, sometimes null, and sometimes a list, yuck
configureFlags =
configureFlags
++ (
if configurePlatforms == defaultConfigurePlatforms then
defaultConfigurePlatformsFlags
else
optional (elem "build" configurePlatforms) buildPlatformConfigureFlag
++ optional (elem "host" configurePlatforms) hostPlatformConfigureFlag
++ optional (elem "target" configurePlatforms) targetPlatformConfigureFlag
);
inherit patches;
inherit doCheck doInstallCheck;
inherit outputs;
# When the derivations is content addressed provide default values
# for outputHashMode and outputHashAlgo because most people won't
# care about these anyways
${if __contentAddressed then "__contentAddressed" else null} = __contentAddressed;
${if __contentAddressed then "outputHashAlgo" else null} = attrs.outputHashAlgo or "sha256";
${if __contentAddressed then "outputHashMode" else null} = attrs.outputHashMode or "recursive";
${if enableParallelBuilding then "enableParallelBuilding" else null} = enableParallelBuilding;
${if enableParallelBuilding then "enableParallelChecking" else null} =
attrs.enableParallelChecking or true;
${if enableParallelBuilding then "enableParallelInstalling" else null} =
attrs.enableParallelInstalling or true;
${
if (hardeningDisable != [ ] || hardeningEnable != [ ] || isMusl) then
"NIX_HARDENING_ENABLE"
# Disambiguate statically built packages. This was originally
# introduce as a means to prevent nix-env to get confused between
# nix and nixStatic. This should be also achieved by moving the
# hostSuffix before the version, so we could contemplate removing
# it again.
staticMarker = stdenvStaticMarker;
in
lib.strings.sanitizeDerivationName (
if attrs ? name then
attrs.name + hostSuffix
else
null
} =
lib.warnIf ((builtins.elem "pie" hardeningEnable) || (builtins.elem "pie" hardeningDisable))
"The 'pie' hardening flag has been removed in favor of enabling PIE by default in compilers and should no longer be used. PIE can be disabled with the -no-pie compiler flag, but this is usually not necessary as most build systems pass this if needed. Usage of the 'pie' hardening flag will become an error in future."
(builtins.concatStringsSep " " enabledHardeningOptions);
# we cannot coerce null to a string below
assert assertMsg (
attrs ? version && attrs.version != null
) "The `version` attribute cannot be null.";
"${attrs.pname}${staticMarker}${hostSuffix}-${attrs.version}"
);
# TODO: remove platform condition
# Enabling this check could be a breaking change as it requires to edit nix.conf
# NixOS module already sets gccarch, unsure of nix installers and other distributions
${if requiredSystemFeaturesShouldBeSet then "requiredSystemFeatures" else null} =
attrs.requiredSystemFeatures or [ ] ++ gccArchFeature;
}
// optionalAttrs buildIsDarwin (
builder = attrs.realBuilder or stdenvShell;
args =
attrs.args or [
"-e"
./source-stdenv.sh
(attrs.builder or ./default-builder.sh)
];
inherit stdenv;
# The `system` attribute of a derivation has special meaning to Nix.
# Derivations set it to choose what sort of machine could be used to
# execute the build, The build platform entirely determines this,
# indeed more finely than Nix knows or cares about. The `system`
# attribute of `buildPlatform` matches Nix's degree of specificity.
# exactly.
system = buildPlatformSystem;
inherit userHook;
__ignoreNulls = true;
inherit __structuredAttrs strictDeps;
depsBuildBuild = elemAt (elemAt dependencies 0) 0;
nativeBuildInputs = elemAt (elemAt dependencies 0) 1;
depsBuildTarget = elemAt (elemAt dependencies 0) 2;
depsHostHost = elemAt (elemAt dependencies 1) 0;
buildInputs = elemAt (elemAt dependencies 1) 1;
depsTargetTarget = elemAt (elemAt dependencies 2) 0;
depsBuildBuildPropagated = elemAt (elemAt propagatedDependencies 0) 0;
propagatedNativeBuildInputs = elemAt (elemAt propagatedDependencies 0) 1;
depsBuildTargetPropagated = elemAt (elemAt propagatedDependencies 0) 2;
depsHostHostPropagated = elemAt (elemAt propagatedDependencies 1) 0;
propagatedBuildInputs = elemAt (elemAt propagatedDependencies 1) 1;
depsTargetTargetPropagated = elemAt (elemAt propagatedDependencies 2) 0;
# This parameter is sometimes a string, sometimes null, and sometimes a list, yuck
configureFlags =
configureFlags
++ (
if configurePlatforms == defaultConfigurePlatforms then
defaultConfigurePlatformsFlags
else
optional (elem "build" configurePlatforms) buildPlatformConfigureFlag
++ optional (elem "host" configurePlatforms) hostPlatformConfigureFlag
++ optional (elem "target" configurePlatforms) targetPlatformConfigureFlag
);
inherit patches;
inherit doCheck doInstallCheck;
inherit outputs;
# When the derivations is content addressed provide default values
# for outputHashMode and outputHashAlgo because most people won't
# care about these anyways
${if __contentAddressed then "__contentAddressed" else null} = __contentAddressed;
${if __contentAddressed then "outputHashAlgo" else null} = attrs.outputHashAlgo or "sha256";
${if __contentAddressed then "outputHashMode" else null} = attrs.outputHashMode or "recursive";
${if enableParallelBuilding then "enableParallelBuilding" else null} = enableParallelBuilding;
${if enableParallelBuilding then "enableParallelChecking" else null} =
attrs.enableParallelChecking or true;
${if enableParallelBuilding then "enableParallelInstalling" else null} =
attrs.enableParallelInstalling or true;
${
if (hardeningDisable != [ ] || hardeningEnable != [ ] || isMusl) then
"NIX_HARDENING_ENABLE"
else
null
} =
lib.warnIf ((builtins.elem "pie" hardeningEnable) || (builtins.elem "pie" hardeningDisable))
"The 'pie' hardening flag has been removed in favor of enabling PIE by default in compilers and should no longer be used. PIE can be disabled with the -no-pie compiler flag, but this is usually not necessary as most build systems pass this if needed. Usage of the 'pie' hardening flag will become an error in future."
(builtins.concatStringsSep " " enabledHardeningOptions);
# TODO: remove platform condition
# Enabling this check could be a breaking change as it requires to edit nix.conf
# NixOS module already sets gccarch, unsure of nix installers and other distributions
${if requiredSystemFeaturesShouldBeSet then "requiredSystemFeatures" else null} =
attrs.requiredSystemFeatures or [ ] ++ gccArchFeature;
# -- Darwin-specific attrs --
${if buildIsDarwin then "__darwinAllowLocalNetworking" else null} = __darwinAllowLocalNetworking;
${if buildIsDarwin then "__sandboxProfile" else null} =
let
allDependencies = concatLists (concatLists dependencies);
allPropagatedDependencies = concatLists (concatLists propagatedDependencies);
computedSandboxProfile = concatMap (input: input.__propagatedSandboxProfile or [ ]) (
extraNativeBuildInputs ++ extraBuildInputs ++ allDependencies
);
computedPropagatedSandboxProfile = concatMap (
input: input.__propagatedSandboxProfile or [ ]
) allPropagatedDependencies;
profiles = [
extraSandboxProfile
]
++ computedSandboxProfile
++ computedPropagatedSandboxProfile
++ [
propagatedSandboxProfile
sandboxProfile
];
in
# TODO: remove `unique` once nix has a list canonicalization primitive
concatStringsSep "\n" (filter (x: x != "") (unique profiles));
${if buildIsDarwin then "__propagatedSandboxProfile" else null} =
let
allPropagatedDependencies = concatLists (concatLists propagatedDependencies);
computedPropagatedSandboxProfile = concatMap (
input: input.__propagatedSandboxProfile or [ ]
) allPropagatedDependencies;
in
unique (computedPropagatedSandboxProfile ++ [ propagatedSandboxProfile ]);
${if buildIsDarwin then "__impureHostDeps" else null} =
let
allDependencies = concatLists (concatLists dependencies);
allPropagatedDependencies = concatLists (concatLists propagatedDependencies);
computedImpureHostDeps = unique (
concatMap (input: input.__propagatedImpureHostDeps or [ ]) (
extraNativeBuildInputs ++ extraBuildInputs ++ allDependencies
)
);
computedPropagatedImpureHostDeps = unique (
concatMap (input: input.__propagatedImpureHostDeps or [ ]) allPropagatedDependencies
);
in
{
inherit __darwinAllowLocalNetworking;
# TODO: remove `unique` once nix has a list canonicalization primitive
__sandboxProfile =
let
profiles = [
extraSandboxProfile
]
++ computedSandboxProfile
++ computedPropagatedSandboxProfile
++ [
propagatedSandboxProfile
sandboxProfile
];
final = concatStringsSep "\n" (filter (x: x != "") (unique profiles));
in
final;
__propagatedSandboxProfile = unique (
computedPropagatedSandboxProfile ++ [ propagatedSandboxProfile ]
computedImpureHostDeps
++ computedPropagatedImpureHostDeps
++ __propagatedImpureHostDeps
++ __impureHostDeps
++ __extraImpureHostDeps
++ [
"/dev/zero"
"/dev/random"
"/dev/urandom"
"/bin/sh"
];
${if buildIsDarwin then "__propagatedImpureHostDeps" else null} =
let
allPropagatedDependencies = concatLists (concatLists propagatedDependencies);
computedPropagatedImpureHostDeps = unique (
concatMap (input: input.__propagatedImpureHostDeps or [ ]) allPropagatedDependencies
);
__impureHostDeps =
computedImpureHostDeps
++ computedPropagatedImpureHostDeps
++ __propagatedImpureHostDeps
++ __impureHostDeps
++ __extraImpureHostDeps
++ [
"/dev/zero"
"/dev/random"
"/dev/urandom"
"/bin/sh"
];
__propagatedImpureHostDeps = computedPropagatedImpureHostDeps ++ __propagatedImpureHostDeps;
}
)
// optionalAttrs (isWindows || isCygwin) {
allowedImpureDLLs =
allowedImpureDLLs
++ lib.optionals isCygwin [
"KERNEL32.dll"
];
}
// (
in
computedPropagatedImpureHostDeps ++ __propagatedImpureHostDeps;
# -- Windows/Cygwin-specific attrs --
${if isWindows || isCygwin then "allowedImpureDLLs" else null} =
allowedImpureDLLs
++ lib.optionals isCygwin [
"KERNEL32.dll"
];
# -- Output reference checks --
${if !__structuredAttrs && attrs ? disallowedReferences then "disallowedReferences" else null} =
map unsafeDerivationToUntrackedOutpath attrs.disallowedReferences;
${if !__structuredAttrs && attrs ? disallowedRequisites then "disallowedRequisites" else null} =
map unsafeDerivationToUntrackedOutpath attrs.disallowedRequisites;
${if !__structuredAttrs && attrs ? allowedReferences then "allowedReferences" else null} =
mapNullable unsafeDerivationToUntrackedOutpath attrs.allowedReferences;
${if !__structuredAttrs && attrs ? allowedRequisites then "allowedRequisites" else null} =
mapNullable unsafeDerivationToUntrackedOutpath attrs.allowedRequisites;
${if __structuredAttrs then "outputChecks" else null} =
let
attrsOutputChecks = makeOutputChecks attrs;
attrsOutputChecksFiltered = filterAttrs (_: v: v != null) attrsOutputChecks;
in
if !__structuredAttrs then
attrsOutputChecks
else
{
outputChecks = builtins.listToAttrs (
map (name: {
inherit name;
value =
let
raw = zipAttrsWith (_: builtins.concatLists) [
attrsOutputChecksFiltered
(makeOutputChecks attrs.outputChecks.${name} or { })
];
in
# separateDebugInfo = true will put all sorts of files in
# the debug output which could carry references, but
# that's "normal". Notably it symlinks to the source.
# So disable reference checking for the debug output
if separateDebugInfo' && name == "debug" then
removeAttrs raw [
"allowedReferences"
"allowedRequisites"
"disallowedReferences"
"disallowedRequisites"
]
else
raw;
}) outputs
);
}
);
builtins.listToAttrs (
map (name: {
inherit name;
value =
let
raw = zipAttrsWith (_: builtins.concatLists) [
attrsOutputChecksFiltered
(makeOutputChecks attrs.outputChecks.${name} or { })
];
in
# separateDebugInfo = true will put all sorts of files in
# the debug output which could carry references, but
# that's "normal". Notably it symlinks to the source.
# So disable reference checking for the debug output
if separateDebugInfo' && name == "debug" then
removeAttrs raw [
"allowedReferences"
"allowedRequisites"
"disallowedReferences"
"disallowedRequisites"
]
else
raw;
}) outputs
);
};
in
derivationArg;
@@ -806,7 +821,9 @@ let
let
mainProgram = meta.mainProgram or null;
env' = env // lib.optionalAttrs (mainProgram != null) { NIX_MAIN_PROGRAM = mainProgram; };
env' = env // {
${if mainProgram != null then "NIX_MAIN_PROGRAM" else null} = mainProgram;
};
derivationArg = makeDerivationArgument (
removeAttrs attrs [
@@ -815,8 +832,8 @@ let
"pos"
"env"
]
// lib.optionalAttrs __structuredAttrs { env = checkedEnv; }
// {
${if __structuredAttrs then "env" else null} = checkedEnv;
cmakeFlags = makeCMakeFlags attrs;
mesonFlags = makeMesonFlags attrs;
}