stdenv: Improve performance

| stat                   | before          | after           | Δ               | Δ%      |
|------------------------|-----------------|-----------------|-----------------|---------|
| cpuTime                | 513.67          | 507.77          | ↘ 5.90          | -1.15%  |
| envs-bytes             | 20,682,847,968  | 20,628,961,616  | ↘ 53,886,352    | -0.26%  |
| envs-elements          | 1,054,735,104   | 1,051,395,620   | ↘ 3,339,484     | -0.32%  |
| envs-number            | 765,310,446     | 763,612,291     | ↘ 1,698,155     | -0.22%  |
| gc-heapSize            | 53,439,602,688  | 51,711,545,344  | ↘ 1,728,057,344 | -3.23%  |
| gc-totalBytes          | 113,062,066,672 | 112,139,998,240 | ↘ 922,068,432   | -0.82%  |
| list-bytes             | 3,118,249,784   | 3,118,249,784   | 0               |         |
| list-concats           | 52,834,140      | 52,834,140      | 0               |         |
| list-elements          | 389,781,223     | 389,781,223     | 0               |         |
| nrAvoided              | 968,097,988     | 991,889,795     | ↗ 23,791,807    | 2.46%   |
| nrFunctionCalls        | 697,259,792     | 697,259,792     | 0               |         |
| nrLookups              | 510,257,062     | 338,275,331     | ↘ 171,981,731   | -33.70% |
| nrOpUpdateValuesCopied | 1,446,690,216   | 1,446,690,216   | 0               |         |
| nrOpUpdates            | 68,504,034      | 68,504,034      | 0               |         |
| nrPrimOpCalls          | 429,464,805     | 429,464,805     | 0               |         |
| nrThunks               | 1,009,240,391   | 982,109,100     | ↘ 27,131,291    | -2.69%  |
| sets-bytes             | 33,524,722,928  | 33,524,722,928  | 0               |         |
| sets-elements          | 1,938,309,212   | 1,938,309,212   | 0               |         |
| sets-number            | 156,985,971     | 156,985,971     | 0               |         |
| sizes-Attr             | 16              | 16              | 0               |         |
| sizes-Bindings         | 16              | 16              | 0               |         |
| sizes-Env              | 16              | 16              | 0               |         |
| sizes-Value            | 24              | 24              | 0               |         |
| symbols-bytes          | 2,151,298       | 2,151,298       | 0               |         |
| symbols-number         | 159,707         | 159,707         | 0               |         |
| values-bytes           | 30,218,194,248  | 29,567,043,264  | ↘ 651,150,984   | -2.15%  |
| values-number          | 1,259,091,427   | 1,231,960,136   | ↘ 27,131,291    | -2.15%  |

> Accessing the lexical scope directly should be more efficient, yes, because it changes from a binary search (many lookups) to just two memory accesses
> correction: one short linked list + one array access
> oh and you had to do the lexical scope lookup anyway for lib itself
> so it really does save a binary search at basically no extra cost

- roberth

after seeing the stats

> Oooh nice. I did not consider that more of the maybeThunk optimization becomes effective (nrAvoided). Those lookups also caused allocations!

- roberth

Left `lib.generators` and `lib.strings` alone because they're only used
once.
This commit is contained in:
Artturin 2023-11-11 19:15:20 +02:00
parent f2bd8adf7b
commit c3c31aa798

View File

@ -3,6 +3,43 @@
stdenv:
let
# Lib attributes are inherited to the lexical scope for performance reasons.
inherit (lib)
any
assertMsg
attrNames
boolToString
chooseDevOutputs
concatLists
concatMap
concatMapStrings
concatStringsSep
elem
elemAt
extendDerivation
filter
findFirst
flip
head
imap1
isAttrs
isBool
isDerivation
isInt
isList
isString
mapAttrs
mapNullable
optional
optionalAttrs
optionalString
optionals
remove
splitString
subtractLists
unique
;
checkMeta = import ./check-meta.nix {
inherit lib config;
# Nix itself uses the `system` field of a derivation to decide where
@ -115,7 +152,7 @@ let
# Including it then would cause needless mass rebuilds.
#
# TODO(@Ericson2314): Make [ "build" "host" ] always the default / resolve #87909
configurePlatforms ? lib.optionals
configurePlatforms ? optionals
(stdenv.hostPlatform != stdenv.buildPlatform || config.configurePlatformsByDefault)
[ "build" "host" ]
@ -168,7 +205,7 @@ let
# Policy on acceptable hash types in nixpkgs
assert attrs ? outputHash -> (
let algo =
attrs.outputHashAlgo or (lib.head (lib.splitString "-" attrs.outputHash));
attrs.outputHashAlgo or (head (splitString "-" attrs.outputHash));
in
if algo == "md5" then
throw "Rejected insecure ${algo} hash '${attrs.outputHash}'"
@ -183,12 +220,12 @@ let
doInstallCheck' = doInstallCheck && stdenv.buildPlatform.canExecute stdenv.hostPlatform;
separateDebugInfo' = separateDebugInfo && stdenv.hostPlatform.isLinux;
outputs' = outputs ++ lib.optional separateDebugInfo' "debug";
outputs' = outputs ++ optional separateDebugInfo' "debug";
# Turn a derivation into its outPath without a string context attached.
# See the comment at the usage site.
unsafeDerivationToUntrackedOutpath = drv:
if lib.isDerivation drv
if isDerivation drv
then builtins.unsafeDiscardStringContext drv.outPath
else drv;
@ -198,9 +235,9 @@ let
++ depsTargetTarget ++ depsTargetTargetPropagated) == 0;
dontAddHostSuffix = attrs ? outputHash && !noNonNativeDeps || !stdenv.hasCC;
hardeningDisable' = if lib.any (x: x == "fortify") hardeningDisable
hardeningDisable' = if any (x: x == "fortify") hardeningDisable
# disabling fortify implies fortify3 should also be disabled
then lib.unique (hardeningDisable ++ [ "fortify3" ])
then unique (hardeningDisable ++ [ "fortify3" ])
else hardeningDisable;
supportedHardeningFlags = [ "fortify" "fortify3" "stackprotector" "pie" "pic" "strictoverflow" "format" "relro" "bindnow" ];
# Musl-based platforms will keep "pie", other platforms will not.
@ -212,19 +249,19 @@ let
# - static armv7l, where compilation fails.
!(stdenv.hostPlatform.isAarch && stdenv.hostPlatform.isStatic)
then supportedHardeningFlags
else lib.remove "pie" supportedHardeningFlags;
else remove "pie" supportedHardeningFlags;
enabledHardeningOptions =
if builtins.elem "all" hardeningDisable'
then []
else lib.subtractLists hardeningDisable' (defaultHardeningFlags ++ hardeningEnable);
else subtractLists hardeningDisable' (defaultHardeningFlags ++ hardeningEnable);
# hardeningDisable additionally supports "all".
erroneousHardeningFlags = lib.subtractLists supportedHardeningFlags (hardeningEnable ++ lib.remove "all" hardeningDisable);
erroneousHardeningFlags = subtractLists supportedHardeningFlags (hardeningEnable ++ remove "all" hardeningDisable);
checkDependencyList = checkDependencyList' [];
checkDependencyList' = positions: name: deps: lib.flip lib.imap1 deps (index: dep:
if lib.isDerivation dep || dep == null || builtins.isString dep || builtins.isPath dep then dep
else if lib.isList dep then checkDependencyList' ([index] ++ positions) name dep
else throw "Dependency is not of a valid type: ${lib.concatMapStrings (ix: "element ${toString ix} of ") ([index] ++ positions)}${name} for ${attrs.name or attrs.pname}");
checkDependencyList' = positions: name: deps: flip imap1 deps (index: dep:
if isDerivation dep || dep == null || builtins.isString dep || builtins.isPath dep then dep
else if isList dep then checkDependencyList' ([index] ++ positions) name dep
else throw "Dependency is not of a valid type: ${concatMapStrings (ix: "element ${toString ix} of ") ([index] ++ positions)}${name} for ${attrs.name or attrs.pname}");
in if builtins.length erroneousHardeningFlags != 0
then abort ("mkDerivation was called with unsupported hardening flags: " + lib.generators.toPretty {} {
inherit erroneousHardeningFlags hardeningDisable hardeningEnable supportedHardeningFlags;
@ -233,20 +270,20 @@ else let
doCheck = doCheck';
doInstallCheck = doInstallCheck';
buildInputs' = buildInputs
++ lib.optionals doCheck checkInputs
++ lib.optionals doInstallCheck installCheckInputs;
++ optionals doCheck checkInputs
++ optionals doInstallCheck installCheckInputs;
nativeBuildInputs' = nativeBuildInputs
++ lib.optional separateDebugInfo' ../../build-support/setup-hooks/separate-debug-info.sh
++ lib.optional stdenv.hostPlatform.isWindows ../../build-support/setup-hooks/win-dll-link.sh
++ lib.optionals doCheck nativeCheckInputs
++ lib.optionals doInstallCheck nativeInstallCheckInputs;
++ optional separateDebugInfo' ../../build-support/setup-hooks/separate-debug-info.sh
++ optional stdenv.hostPlatform.isWindows ../../build-support/setup-hooks/win-dll-link.sh
++ optionals doCheck nativeCheckInputs
++ optionals doInstallCheck nativeInstallCheckInputs;
outputs = outputs';
references = nativeBuildInputs ++ buildInputs
++ propagatedNativeBuildInputs ++ propagatedBuildInputs;
dependencies = map (map lib.chooseDevOutputs) [
dependencies = map (map chooseDevOutputs) [
[
(map (drv: drv.__spliced.buildBuild or drv) (checkDependencyList "depsBuildBuild" depsBuildBuild))
(map (drv: drv.__spliced.buildHost or drv) (checkDependencyList "nativeBuildInputs" nativeBuildInputs'))
@ -260,7 +297,7 @@ else let
(map (drv: drv.__spliced.targetTarget or drv) (checkDependencyList "depsTargetTarget" depsTargetTarget))
]
];
propagatedDependencies = map (map lib.chooseDevOutputs) [
propagatedDependencies = map (map chooseDevOutputs) [
[
(map (drv: drv.__spliced.buildBuild or drv) (checkDependencyList "depsBuildBuildPropagated" depsBuildBuildPropagated))
(map (drv: drv.__spliced.buildHost or drv) (checkDependencyList "propagatedNativeBuildInputs" propagatedNativeBuildInputs))
@ -276,26 +313,26 @@ else let
];
computedSandboxProfile =
lib.concatMap (input: input.__propagatedSandboxProfile or [])
concatMap (input: input.__propagatedSandboxProfile or [])
(stdenv.extraNativeBuildInputs
++ stdenv.extraBuildInputs
++ lib.concatLists dependencies);
++ concatLists dependencies);
computedPropagatedSandboxProfile =
lib.concatMap (input: input.__propagatedSandboxProfile or [])
(lib.concatLists propagatedDependencies);
concatMap (input: input.__propagatedSandboxProfile or [])
(concatLists propagatedDependencies);
computedImpureHostDeps =
lib.unique (lib.concatMap (input: input.__propagatedImpureHostDeps or [])
unique (concatMap (input: input.__propagatedImpureHostDeps or [])
(stdenv.extraNativeBuildInputs
++ stdenv.extraBuildInputs
++ lib.concatLists dependencies));
++ concatLists dependencies));
computedPropagatedImpureHostDeps =
lib.unique (lib.concatMap (input: input.__propagatedImpureHostDeps or [])
(lib.concatLists propagatedDependencies));
unique (concatMap (input: input.__propagatedImpureHostDeps or [])
(concatLists propagatedDependencies));
envIsExportable = lib.isAttrs env && !lib.isDerivation env;
envIsExportable = isAttrs env && !isDerivation env;
derivationArg =
(removeAttrs attrs
@ -306,8 +343,8 @@ else let
"__darwinAllowLocalNetworking"
"__impureHostDeps" "__propagatedImpureHostDeps"
"sandboxProfile" "propagatedSandboxProfile"]
++ lib.optional (__structuredAttrs || envIsExportable) "env"))
// (lib.optionalAttrs (attrs ? name || (attrs ? pname && attrs ? version)) {
++ optional (__structuredAttrs || envIsExportable) "env"))
// (optionalAttrs (attrs ? name || (attrs ? pname && attrs ? version)) {
name =
let
# Indicate the host platform of the derivation if cross compiling.
@ -315,7 +352,7 @@ else let
# 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 = lib.optionalString
hostSuffix = optionalString
(stdenv.hostPlatform != stdenv.buildPlatform && !dontAddHostSuffix)
"-${stdenv.hostPlatform.config}";
@ -324,17 +361,17 @@ else let
# nix and nixStatic. This should be also achieved by moving the
# hostSuffix before the version, so we could contemplate removing
# it again.
staticMarker = lib.optionalString stdenv.hostPlatform.isStatic "-static";
staticMarker = optionalString stdenv.hostPlatform.isStatic "-static";
in
lib.strings.sanitizeDerivationName (
if attrs ? name
then attrs.name + hostSuffix
else
# we cannot coerce null to a string below
assert lib.assertMsg (attrs ? version && attrs.version != null) "The version attribute cannot be null.";
assert assertMsg (attrs ? version && attrs.version != null) "The version attribute cannot be null.";
"${attrs.pname}${staticMarker}${hostSuffix}-${attrs.version}"
);
}) // lib.optionalAttrs __structuredAttrs { env = checkedEnv; } // {
}) // optionalAttrs __structuredAttrs { env = checkedEnv; } // {
builder = attrs.realBuilder or stdenv.shell;
args = attrs.args or ["-e" (attrs.builder or ./default-builder.sh)];
inherit stdenv;
@ -351,22 +388,22 @@ else let
__ignoreNulls = true;
inherit __structuredAttrs strictDeps;
depsBuildBuild = lib.elemAt (lib.elemAt dependencies 0) 0;
nativeBuildInputs = lib.elemAt (lib.elemAt dependencies 0) 1;
depsBuildTarget = lib.elemAt (lib.elemAt dependencies 0) 2;
depsHostHost = lib.elemAt (lib.elemAt dependencies 1) 0;
buildInputs = lib.elemAt (lib.elemAt dependencies 1) 1;
depsTargetTarget = lib.elemAt (lib.elemAt dependencies 2) 0;
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 = lib.elemAt (lib.elemAt propagatedDependencies 0) 0;
propagatedNativeBuildInputs = lib.elemAt (lib.elemAt propagatedDependencies 0) 1;
depsBuildTargetPropagated = lib.elemAt (lib.elemAt propagatedDependencies 0) 2;
depsHostHostPropagated = lib.elemAt (lib.elemAt propagatedDependencies 1) 0;
propagatedBuildInputs = lib.elemAt (lib.elemAt propagatedDependencies 1) 1;
depsTargetTargetPropagated = lib.elemAt (lib.elemAt propagatedDependencies 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 = let inherit (lib) optional elem; in
configureFlags =
configureFlags
++ optional (elem "build" configurePlatforms) "--build=${stdenv.buildPlatform.config}"
++ optional (elem "host" configurePlatforms) "--host=${stdenv.hostPlatform.config}"
@ -374,21 +411,21 @@ else let
cmakeFlags =
cmakeFlags
++ lib.optionals (stdenv.hostPlatform != stdenv.buildPlatform) ([
"-DCMAKE_SYSTEM_NAME=${lib.findFirst lib.isString "Generic" (lib.optional (!stdenv.hostPlatform.isRedox) stdenv.hostPlatform.uname.system)}"
] ++ lib.optionals (stdenv.hostPlatform.uname.processor != null) [
++ optionals (stdenv.hostPlatform != stdenv.buildPlatform) ([
"-DCMAKE_SYSTEM_NAME=${findFirst isString "Generic" (optional (!stdenv.hostPlatform.isRedox) stdenv.hostPlatform.uname.system)}"
] ++ optionals (stdenv.hostPlatform.uname.processor != null) [
"-DCMAKE_SYSTEM_PROCESSOR=${stdenv.hostPlatform.uname.processor}"
] ++ lib.optionals (stdenv.hostPlatform.uname.release != null) [
] ++ optionals (stdenv.hostPlatform.uname.release != null) [
"-DCMAKE_SYSTEM_VERSION=${stdenv.hostPlatform.uname.release}"
] ++ lib.optionals (stdenv.hostPlatform.isDarwin) [
] ++ optionals (stdenv.hostPlatform.isDarwin) [
"-DCMAKE_OSX_ARCHITECTURES=${stdenv.hostPlatform.darwinArch}"
] ++ lib.optionals (stdenv.buildPlatform.uname.system != null) [
] ++ optionals (stdenv.buildPlatform.uname.system != null) [
"-DCMAKE_HOST_SYSTEM_NAME=${stdenv.buildPlatform.uname.system}"
] ++ lib.optionals (stdenv.buildPlatform.uname.processor != null) [
] ++ optionals (stdenv.buildPlatform.uname.processor != null) [
"-DCMAKE_HOST_SYSTEM_PROCESSOR=${stdenv.buildPlatform.uname.processor}"
] ++ lib.optionals (stdenv.buildPlatform.uname.release != null) [
] ++ optionals (stdenv.buildPlatform.uname.release != null) [
"-DCMAKE_HOST_SYSTEM_VERSION=${stdenv.buildPlatform.uname.release}"
] ++ lib.optionals (stdenv.buildPlatform.canExecute stdenv.hostPlatform) [
] ++ optionals (stdenv.buildPlatform.canExecute stdenv.hostPlatform) [
"-DCMAKE_CROSSCOMPILING_EMULATOR=env"
]);
@ -402,7 +439,7 @@ else let
crossFile = builtins.toFile "cross-file.conf" ''
[properties]
needs_exe_wrapper = ${lib.boolToString (!stdenv.buildPlatform.canExecute stdenv.hostPlatform)}
needs_exe_wrapper = ${boolToString (!stdenv.buildPlatform.canExecute stdenv.hostPlatform)}
[host_machine]
system = '${stdenv.targetPlatform.parsed.kernel.name}'
@ -413,7 +450,7 @@ else let
[binaries]
llvm-config = 'llvm-config-native'
'';
crossFlags = lib.optionals (stdenv.hostPlatform != stdenv.buildPlatform) [ "--cross-file=${crossFile}" ];
crossFlags = optionals (stdenv.hostPlatform != stdenv.buildPlatform) [ "--cross-file=${crossFile}" ];
in crossFlags ++ mesonFlags;
inherit patches;
@ -421,28 +458,28 @@ else let
inherit doCheck doInstallCheck;
inherit outputs;
} // lib.optionalAttrs (__contentAddressed) {
} // optionalAttrs (__contentAddressed) {
inherit __contentAddressed;
# Provide default values for outputHashMode and outputHashAlgo because
# most people won't care about these anyways
outputHashAlgo = attrs.outputHashAlgo or "sha256";
outputHashMode = attrs.outputHashMode or "recursive";
} // lib.optionalAttrs (enableParallelBuilding) {
} // optionalAttrs (enableParallelBuilding) {
inherit enableParallelBuilding;
enableParallelChecking = attrs.enableParallelChecking or true;
enableParallelInstalling = attrs.enableParallelInstalling or true;
} // lib.optionalAttrs (hardeningDisable != [] || hardeningEnable != [] || stdenv.hostPlatform.isMusl) {
} // optionalAttrs (hardeningDisable != [] || hardeningEnable != [] || stdenv.hostPlatform.isMusl) {
NIX_HARDENING_ENABLE = enabledHardeningOptions;
} // lib.optionalAttrs (stdenv.hostPlatform.isx86_64 && stdenv.hostPlatform ? gcc.arch) {
} // optionalAttrs (stdenv.hostPlatform.isx86_64 && stdenv.hostPlatform ? gcc.arch) {
requiredSystemFeatures = attrs.requiredSystemFeatures or [] ++ [ "gccarch-${stdenv.hostPlatform.gcc.arch}" ];
} // lib.optionalAttrs (stdenv.buildPlatform.isDarwin) {
} // optionalAttrs (stdenv.buildPlatform.isDarwin) {
inherit __darwinAllowLocalNetworking;
# TODO: remove lib.unique once nix has a list canonicalization primitive
# TODO: remove `unique` once nix has a list canonicalization primitive
__sandboxProfile =
let profiles = [ stdenv.extraSandboxProfile ] ++ computedSandboxProfile ++ computedPropagatedSandboxProfile ++ [ propagatedSandboxProfile sandboxProfile ];
final = lib.concatStringsSep "\n" (lib.filter (x: x != "") (lib.unique profiles));
final = concatStringsSep "\n" (filter (x: x != "") (unique profiles));
in final;
__propagatedSandboxProfile = lib.unique (computedPropagatedSandboxProfile ++ [ propagatedSandboxProfile ]);
__propagatedSandboxProfile = unique (computedPropagatedSandboxProfile ++ [ propagatedSandboxProfile ]);
__impureHostDeps = computedImpureHostDeps ++ computedPropagatedImpureHostDeps ++ __propagatedImpureHostDeps ++ __impureHostDeps ++ stdenv.__extraImpureHostDeps ++ [
"/dev/zero"
"/dev/random"
@ -469,21 +506,21 @@ else let
# to be built eventually, we would still like to get the error early and without
# having to wait while nix builds a derivation that might not be used.
# See also https://github.com/NixOS/nix/issues/4629
lib.optionalAttrs (attrs ? disallowedReferences) {
optionalAttrs (attrs ? disallowedReferences) {
disallowedReferences =
map unsafeDerivationToUntrackedOutpath attrs.disallowedReferences;
} //
lib.optionalAttrs (attrs ? disallowedRequisites) {
optionalAttrs (attrs ? disallowedRequisites) {
disallowedRequisites =
map unsafeDerivationToUntrackedOutpath attrs.disallowedRequisites;
} //
lib.optionalAttrs (attrs ? allowedReferences) {
optionalAttrs (attrs ? allowedReferences) {
allowedReferences =
lib.mapNullable unsafeDerivationToUntrackedOutpath attrs.allowedReferences;
mapNullable unsafeDerivationToUntrackedOutpath attrs.allowedReferences;
} //
lib.optionalAttrs (attrs ? allowedRequisites) {
optionalAttrs (attrs ? allowedRequisites) {
allowedRequisites =
lib.mapNullable unsafeDerivationToUntrackedOutpath attrs.allowedRequisites;
mapNullable unsafeDerivationToUntrackedOutpath attrs.allowedRequisites;
};
meta = checkMeta.commonMeta { inherit validity attrs pos references; };
@ -491,20 +528,20 @@ else let
checkedEnv =
let
overlappingNames = lib.attrNames (builtins.intersectAttrs env derivationArg);
overlappingNames = attrNames (builtins.intersectAttrs env derivationArg);
in
assert lib.assertMsg envIsExportable
assert assertMsg envIsExportable
"When using structured attributes, `env` must be an attribute set of environment variables.";
assert lib.assertMsg (overlappingNames == [ ])
"The env attribute set cannot contain any attributes passed to derivation. The following attributes are overlapping: ${lib.concatStringsSep ", " overlappingNames}";
lib.mapAttrs
(n: v: assert lib.assertMsg (lib.isString v || lib.isBool v || lib.isInt v || lib.isDerivation v)
assert assertMsg (overlappingNames == [ ])
"The env attribute set cannot contain any attributes passed to derivation. The following attributes are overlapping: ${concatStringsSep ", " overlappingNames}";
mapAttrs
(n: v: assert assertMsg (isString v || isBool v || isInt v || isDerivation v)
"The env attribute set can only contain derivation, string, boolean or integer attributes. The ${n} attribute is of type ${builtins.typeOf v}."; v)
env;
in
lib.extendDerivation
extendDerivation
validity.handled
({
# A derivation that always builds successfully and whose runtime
@ -553,7 +590,7 @@ lib.extendDerivation
# should be made available to Nix expressions using the
# derivation (e.g., in assertions).
passthru)
(derivation (derivationArg // lib.optionalAttrs envIsExportable checkedEnv));
(derivation (derivationArg // optionalAttrs envIsExportable checkedEnv));
in
fnOrAttrs: