From c3c31aa798551a2808981d14537d04de8f51dff8 Mon Sep 17 00:00:00 2001 From: Artturin Date: Sat, 11 Nov 2023 19:15:20 +0200 Subject: [PATCH] stdenv: Improve performance MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit | 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. --- pkgs/stdenv/generic/make-derivation.nix | 201 ++++++++++++++---------- 1 file changed, 119 insertions(+), 82 deletions(-) diff --git a/pkgs/stdenv/generic/make-derivation.nix b/pkgs/stdenv/generic/make-derivation.nix index d235ffefaab4..8c52eb10c3cf 100644 --- a/pkgs/stdenv/generic/make-derivation.nix +++ b/pkgs/stdenv/generic/make-derivation.nix @@ -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: