stdenv: Fix overriding + `overrideAttrs`

The old stdenv adapters were subtly wrong in two ways:

- `overrideAttrs` leaked the original, unoverridden `mkDerivation`.

- `stdenv.override` would throw away any manually-set `mkDerivation`
from a stdenv reverting to the original.

Now, `mkDerivation` is controlled (nearly directly) via an argument, and
always correctly closes over the final ("self") stdenv. This means the
adapters can work entirely via `.override` without any manual `stdenv //
...`, and both those issues are fixed.

Note hashes are changed, because stdenvs no previously overridden like
`stdenvNoCC` and `crossLibcStdenv` now are. I had to add some
`dontDisableStatic = true` accordingly. The flip side however is that
since the overrides compose, we no longer need to override anything but
the default `stdenv` from which all the others are created.

+94 -84
+1
pkgs/os-specific/linux/musl/default.nix
··· 82 82 outputs = [ "out" "dev" ]; 83 83 84 84 dontDisableStatic = true; 85 + dontAddStaticConfigureFlags = true; 85 86 separateDebugInfo = true; 86 87 87 88 NIX_DONT_SET_RPATH = true;
+76 -49
pkgs/stdenv/adapters.nix
··· 2 2 a new stdenv with different behaviour, e.g. using a different C 3 3 compiler. */ 4 4 5 - pkgs: 5 + { lib, pkgs, config }: 6 + 7 + let 8 + # N.B. Keep in sync with default arg for stdenv/generic. 9 + defaultMkDerivationFromStdenv = import ./generic/make-derivation.nix { inherit lib config; }; 10 + 11 + # Low level function to help with overriding `mkDerivationFromStdenv`. One 12 + # gives it the old stdenv arguments and a "continuation" function, and 13 + # underneath the final stdenv argument it yields to the continuation to do 14 + # whatever it wants with old `mkDerivation` (old `mkDerivationFromStdenv` 15 + # applied to the *new, final* stdenv) provided for convenience. 16 + withOldMkDerivation = stdenvSuperArgs: k: stdenvSelf: let 17 + mkDerivationFromStdenv-super = stdenvSuperArgs.mkDerivationFromStdenv or defaultMkDerivationFromStdenv; 18 + mkDerivationSuper = mkDerivationFromStdenv-super stdenvSelf; 19 + in 20 + k stdenvSelf mkDerivationSuper; 21 + 22 + # Wrap the original `mkDerivation` providing extra args to it. 23 + extendMkDerivationArgs = old: f: withOldMkDerivation old (_: mkDerivationSuper: args: 24 + mkDerivationSuper (args // f args)); 25 + 26 + # Wrap the original `mkDerivation` transforming the result. 27 + overrideMkDerivationResult = old: f: withOldMkDerivation old (_: mkDerivationSuper: args: 28 + f (mkDerivationSuper args)); 29 + in 6 30 7 31 rec { 8 32 ··· 31 55 32 56 # Return a modified stdenv that tries to build statically linked 33 57 # binaries. 34 - makeStaticBinaries = stdenv: 35 - let stdenv' = if stdenv.hostPlatform.libc != "glibc" then stdenv else 36 - stdenv.override (prev: { 37 - extraBuildInputs = (prev.extraBuildInputs or []) ++ [ 38 - stdenv.glibc.static 39 - ]; 40 - }); 41 - in stdenv' // 42 - { mkDerivation = args: 43 - if stdenv'.hostPlatform.isDarwin 58 + makeStaticBinaries = stdenv0: 59 + stdenv0.override (old: { 60 + mkDerivationFromStdenv = withOldMkDerivation old (stdenv: mkDerivationSuper: args: 61 + if stdenv.hostPlatform.isDarwin 44 62 then throw "Cannot build fully static binaries on Darwin/macOS" 45 - else stdenv'.mkDerivation (args // { 63 + else mkDerivationSuper (args // { 46 64 NIX_CFLAGS_LINK = toString (args.NIX_CFLAGS_LINK or "") + " -static"; 47 - } // pkgs.lib.optionalAttrs (!(args.dontAddStaticConfigureFlags or false)) { 65 + } // lib.optionalAttrs (!(args.dontAddStaticConfigureFlags or false)) { 48 66 configureFlags = (args.configureFlags or []) ++ [ 49 67 "--disable-shared" # brrr... 50 68 ]; 51 - }); 52 - }; 69 + })); 70 + } // lib.optionalAttrs (stdenv0.hostPlatform.libc == "libc") { 71 + extraBuildInputs = (old.extraBuildInputs or []) ++ [ 72 + stdenv0.glibc.static 73 + ]; 74 + }); 53 75 54 76 55 77 # Return a modified stdenv that builds static libraries instead of 56 78 # shared libraries. 57 - makeStaticLibraries = stdenv: stdenv // 58 - { mkDerivation = args: stdenv.mkDerivation (args // { 79 + makeStaticLibraries = stdenv: 80 + stdenv.override (old: { 81 + mkDerivationFromStdenv = extendMkDerivationArgs old (args: { 59 82 dontDisableStatic = true; 60 - } // pkgs.lib.optionalAttrs (!(args.dontAddStaticConfigureFlags or false)) { 83 + } // lib.optionalAttrs (!(args.dontAddStaticConfigureFlags or false)) { 61 84 configureFlags = (args.configureFlags or []) ++ [ 62 85 "--enable-static" 63 86 "--disable-shared" ··· 65 88 cmakeFlags = (args.cmakeFlags or []) ++ [ "-DBUILD_SHARED_LIBS:BOOL=OFF" ]; 66 89 mesonFlags = (args.mesonFlags or []) ++ [ "-Ddefault_library=static" ]; 67 90 }); 68 - }; 91 + }); 69 92 70 93 71 94 /* Modify a stdenv so that all buildInputs are implicitly propagated to 72 95 consuming derivations 73 96 */ 74 - propagateBuildInputs = stdenv: stdenv // 75 - { mkDerivation = args: stdenv.mkDerivation (args // { 97 + propagateBuildInputs = stdenv: 98 + stdenv.override (old: { 99 + mkDerivationFromStdenv = extendMkDerivationArgs old (args: { 76 100 propagatedBuildInputs = (args.propagatedBuildInputs or []) ++ (args.buildInputs or []); 77 101 buildInputs = []; 78 102 }); 79 - }; 103 + }); 80 104 81 105 82 106 /* Modify a stdenv so that the specified attributes are added to ··· 88 112 { NIX_CFLAGS_COMPILE = "-O0"; } 89 113 stdenv; 90 114 */ 91 - addAttrsToDerivation = extraAttrs: stdenv: stdenv // 92 - { mkDerivation = args: stdenv.mkDerivation (args // extraAttrs); }; 115 + addAttrsToDerivation = extraAttrs: stdenv: stdenv.override (old: { 116 + mkDerivationFromStdenv = extendMkDerivationArgs old (_: extraAttrs); 117 + }); 93 118 94 119 95 120 /* Return a modified stdenv that builds packages with GCC's coverage ··· 110 135 # remove all maintainers. 111 136 defaultStdenv = replaceMaintainersField allStdenvs.stdenv pkgs []; 112 137 */ 113 - replaceMaintainersField = stdenv: pkgs: maintainers: stdenv // 114 - { mkDerivation = args: 115 - pkgs.lib.recursiveUpdate 116 - (stdenv.mkDerivation args) 117 - { meta.maintainers = maintainers; }; 118 - }; 138 + replaceMaintainersField = stdenv: pkgs: maintainers: 139 + stdenv.override (old: { 140 + mkDerivationFromStdenv = overrideMkDerivationResult (pkg: 141 + lib.recursiveUpdate pkg { meta.maintainers = maintainers; }); 142 + }); 119 143 120 144 121 145 /* Use the trace output to report all processed derivations with their 122 146 license name. 123 147 */ 124 - traceDrvLicenses = stdenv: stdenv // 125 - { mkDerivation = args: 148 + traceDrvLicenses = stdenv: 149 + stdenv.override (old: { 150 + mkDerivationFromStdenv = overrideMkDerivationResult (pkg: 126 151 let 127 - pkg = stdenv.mkDerivation args; 128 152 printDrvPath = val: let 129 153 drvPath = builtins.unsafeDiscardStringContext pkg.drvPath; 130 154 license = pkg.meta.license or null; ··· 133 157 in pkg // { 134 158 outPath = printDrvPath pkg.outPath; 135 159 drvPath = printDrvPath pkg.drvPath; 136 - }; 137 - }; 160 + }); 161 + }); 138 162 139 163 140 164 /* Abort if the license predicate is not verified for a derivation ··· 152 176 use it by patching the all-packages.nix file or by using the override 153 177 feature of ~/.config/nixpkgs/config.nix . 154 178 */ 155 - validateLicenses = licensePred: stdenv: stdenv // 156 - { mkDerivation = args: 179 + validateLicenses = licensePred: stdenv: 180 + stdenv.override (old: { 181 + mkDerivationFromStdenv = overrideMkDerivationResult (pkg: 157 182 let 158 - pkg = stdenv.mkDerivation args; 159 183 drv = builtins.unsafeDiscardStringContext pkg.drvPath; 160 184 license = 161 185 pkg.meta.license or ··· 175 199 in pkg // { 176 200 outPath = validate pkg.outPath; 177 201 drvPath = validate pkg.drvPath; 178 - }; 179 - }; 202 + }); 203 + }); 180 204 181 205 182 206 /* Modify a stdenv so that it produces debug builds; that is, 183 207 binaries have debug info, and compiler optimisations are 184 208 disabled. */ 185 - keepDebugInfo = stdenv: stdenv // 186 - { mkDerivation = args: stdenv.mkDerivation (args // { 209 + keepDebugInfo = stdenv: 210 + stdenv.override (old: { 211 + mkDerivationFromStdenv = extendMkDerivationArgs old (args: { 187 212 dontStrip = true; 188 213 NIX_CFLAGS_COMPILE = toString (args.NIX_CFLAGS_COMPILE or "") + " -ggdb -Og"; 189 214 }); 190 - }; 215 + }); 191 216 192 217 193 218 /* Modify a stdenv so that it uses the Gold linker. */ 194 - useGoldLinker = stdenv: stdenv // 195 - { mkDerivation = args: stdenv.mkDerivation (args // { 219 + useGoldLinker = stdenv: 220 + stdenv.override (old: { 221 + mkDerivationFromStdenv = extendMkDerivationArgs old (args: { 196 222 NIX_CFLAGS_LINK = toString (args.NIX_CFLAGS_LINK or "") + " -fuse-ld=gold"; 197 223 }); 198 - }; 224 + }); 199 225 200 226 201 227 /* Modify a stdenv so that it builds binaries optimized specifically 202 228 for the machine they are built on. 203 229 204 230 WARNING: this breaks purity! */ 205 - impureUseNativeOptimizations = stdenv: stdenv // 206 - { mkDerivation = args: stdenv.mkDerivation (args // { 231 + impureUseNativeOptimizations = stdenv: 232 + stdenv.override (old: { 233 + mkDerivationFromStdenv = extendMkDerivationArgs old (args: { 207 234 NIX_CFLAGS_COMPILE = toString (args.NIX_CFLAGS_COMPILE or "") + " -march=native"; 208 235 NIX_ENFORCE_NO_NATIVE = false; 209 236 210 237 preferLocalBuild = true; 211 238 allowSubstitutes = false; 212 239 }); 213 - }; 240 + }); 214 241 }
+5 -3
pkgs/stdenv/generic/default.nix
··· 48 48 49 49 , # The platform which build tools (especially compilers) build for in this stage, 50 50 targetPlatform 51 + 52 + , # The implementation of `mkDerivation`, parameterized with the final stdenv so we can tie the knot. 53 + # This is convient to have as a parameter so the stdenv "adapters" work better 54 + mkDerivationFromStdenv ? import ./make-derivation.nix { inherit lib config; } 51 55 }: 52 56 53 57 let ··· 155 159 # to correct type of machine. 156 160 inherit (hostPlatform) system; 157 161 158 - inherit (import ./make-derivation.nix { 159 - inherit lib config stdenv; 160 - }) mkDerivation; 162 + mkDerivation = mkDerivationFromStdenv stdenv; 161 163 162 164 inherit fetchurlBoot; 163 165
+6 -7
pkgs/stdenv/generic/make-derivation.nix
··· 1 - { lib, config, stdenv }: 1 + { lib, config }: 2 + 3 + stdenv: 2 4 3 5 let 4 6 checkMeta = import ./check-meta.nix { ··· 7 9 # to build it. This is a bit confusing for cross compilation. 8 10 inherit (stdenv) hostPlatform; 9 11 }; 10 - in rec { 12 + in 11 13 # `mkDerivation` wraps the builtin `derivation` function to 12 14 # produce derivations that use this stdenv and its shell. 13 15 # ··· 18 20 # 19 21 # * https://nixos.org/nix/manual/#ssec-derivation 20 22 # Explanation about derivations in general 21 - mkDerivation = 22 23 { 23 24 24 25 # These types of dependencies are all exhaustively documented in ··· 373 374 lib.extendDerivation 374 375 validity.handled 375 376 ({ 376 - overrideAttrs = f: mkDerivation (attrs // (f attrs)); 377 + overrideAttrs = f: stdenv.mkDerivation (attrs // (f attrs)); 377 378 378 379 # A derivation that always builds successfully and whose runtime 379 380 # dependencies are the original derivations build time dependencies ··· 406 407 # should be made available to Nix expressions using the 407 408 # derivation (e.g., in assertions). 408 409 passthru) 409 - (derivation derivationArg); 410 - 411 - } 410 + (derivation derivationArg)
+6 -1
pkgs/top-level/stage.nix
··· 65 65 66 66 let 67 67 stdenvAdapters = self: super: 68 - let res = import ../stdenv/adapters.nix self; in res // { 68 + let 69 + res = import ../stdenv/adapters.nix { 70 + inherit lib config; 71 + pkgs = self; 72 + }; 73 + in res // { 69 74 stdenvAdapters = res; 70 75 }; 71 76
-24
pkgs/top-level/static.nix
··· 35 35 }; 36 36 37 37 staticAdapters = 38 - # makeStaticDarwin must go first so that the extraBuildInputs 39 - # override does not recreate mkDerivation, removing subsequent 40 - # adapters. 41 38 optional super.stdenv.hostPlatform.isDarwin makeStaticDarwin 42 39 43 40 ++ [ makeStaticLibraries propagateBuildInputs ] ··· 80 77 }); 81 78 }; 82 79 83 - llvmStaticAdapter = llvmPackages: 84 - llvmPackages // { 85 - stdenv = foldl (flip id) llvmPackages.stdenv staticAdapters; 86 - libcxxStdenv = foldl (flip id) llvmPackages.libcxxStdenv staticAdapters; 87 - }; 88 - 89 80 in { 90 81 stdenv = foldl (flip id) super.stdenv staticAdapters; 91 - 92 - gcc49Stdenv = foldl (flip id) super.gcc49Stdenv staticAdapters; 93 - gcc6Stdenv = foldl (flip id) super.gcc6Stdenv staticAdapters; 94 - gcc7Stdenv = foldl (flip id) super.gcc7Stdenv staticAdapters; 95 - gcc8Stdenv = foldl (flip id) super.gcc8Stdenv staticAdapters; 96 - gcc9Stdenv = foldl (flip id) super.gcc9Stdenv staticAdapters; 97 - 98 - llvmPackages_5 = llvmStaticAdapter super.llvmPackages_5; 99 - llvmPackages_6 = llvmStaticAdapter super.llvmPackages_6; 100 - llvmPackages_7 = llvmStaticAdapter super.llvmPackages_7; 101 - llvmPackages_8 = llvmStaticAdapter super.llvmPackages_8; 102 - llvmPackages_9 = llvmStaticAdapter super.llvmPackages_9; 103 - llvmPackages_10 = llvmStaticAdapter super.llvmPackages_10; 104 - llvmPackages_11 = llvmStaticAdapter super.llvmPackages_11; 105 - llvmPackages_12 = llvmStaticAdapter super.llvmPackages_12; 106 82 107 83 boost = super.boost.override { 108 84 # Don’t use new stdenv for boost because it doesn’t like the