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