1/* 2 This file contains various functions that take a stdenv and return 3 a new stdenv with different behaviour, e.g. using a different C 4 compiler. 5*/ 6 7{ 8 lib, 9 pkgs, 10 config, 11}: 12 13let 14 # N.B. Keep in sync with default arg for stdenv/generic. 15 defaultMkDerivationFromStdenv = 16 stdenv: (import ./generic/make-derivation.nix { inherit lib config; } stdenv).mkDerivation; 17 18 # Low level function to help with overriding `mkDerivationFromStdenv`. One 19 # gives it the old stdenv arguments and a "continuation" function, and 20 # underneath the final stdenv argument it yields to the continuation to do 21 # whatever it wants with old `mkDerivation` (old `mkDerivationFromStdenv` 22 # applied to the *new, final* stdenv) provided for convenience. 23 withOldMkDerivation = 24 stdenvSuperArgs: k: stdenvSelf: 25 let 26 mkDerivationFromStdenv-super = 27 stdenvSuperArgs.mkDerivationFromStdenv or defaultMkDerivationFromStdenv; 28 mkDerivationSuper = mkDerivationFromStdenv-super stdenvSelf; 29 in 30 k stdenvSelf mkDerivationSuper; 31 32 # Wrap the original `mkDerivation` providing extra args to it. 33 extendMkDerivationArgs = 34 old: f: 35 withOldMkDerivation old ( 36 _: mkDerivationSuper: args: 37 (mkDerivationSuper args).overrideAttrs f 38 ); 39 40 # Wrap the original `mkDerivation` transforming the result. 41 overrideMkDerivationResult = 42 old: f: 43 withOldMkDerivation old ( 44 _: mkDerivationSuper: args: 45 f (mkDerivationSuper args) 46 ); 47in 48 49rec { 50 51 # Override the compiler in stdenv for specific packages. 52 overrideCC = 53 stdenv: cc: 54 stdenv.override { 55 allowedRequisites = null; 56 cc = cc; 57 hasCC = cc != null; 58 }; 59 60 # Add some arbitrary packages to buildInputs for specific packages. 61 # Used to override packages in stdenv like Make. Should not be used 62 # for other dependencies. 63 overrideInStdenv = 64 stdenv: pkgs: 65 stdenv.override (prev: { 66 allowedRequisites = null; 67 extraBuildInputs = (prev.extraBuildInputs or [ ]) ++ pkgs; 68 }); 69 70 # Override the libc++ dynamic library used in the stdenv to use the one from the platform’s 71 # default stdenv. This allows building packages and linking dependencies with different 72 # compiler versions while still using the same libc++ implementation for compatibility. 73 # 74 # Note that this adapter still uses the headers from the new stdenv’s libc++. This is necessary 75 # because older compilers may not be able to parse the headers from the default stdenv’s libc++. 76 overrideLibcxx = 77 stdenv: 78 assert stdenv.cc.libcxx != null; 79 assert pkgs.stdenv.cc.libcxx != null; 80 # only unified libcxx / libcxxabi stdenv's are supported 81 assert lib.versionAtLeast pkgs.stdenv.cc.libcxx.version "12"; 82 assert lib.versionAtLeast stdenv.cc.libcxx.version "12"; 83 let 84 llvmLibcxxVersion = lib.getVersion llvmLibcxx; 85 86 stdenvLibcxx = pkgs.stdenv.cc.libcxx; 87 llvmLibcxx = stdenv.cc.libcxx; 88 89 libcxx = 90 pkgs.runCommand "${stdenvLibcxx.name}-${llvmLibcxxVersion}" 91 { 92 outputs = [ 93 "out" 94 "dev" 95 ]; 96 isLLVM = true; 97 } 98 '' 99 mkdir -p "$dev/nix-support" 100 ln -s '${stdenvLibcxx}' "$out" 101 echo '${stdenvLibcxx}' > "$dev/nix-support/propagated-build-inputs" 102 ln -s '${lib.getDev llvmLibcxx}/include' "$dev/include" 103 ''; 104 in 105 overrideCC stdenv ( 106 stdenv.cc.override { 107 inherit libcxx; 108 extraPackages = [ 109 pkgs.buildPackages.targetPackages."llvmPackages_${lib.versions.major llvmLibcxxVersion}".compiler-rt 110 ]; 111 } 112 ); 113 114 # Override the setup script of stdenv. Useful for testing new 115 # versions of the setup script without causing a rebuild of 116 # everything. 117 # 118 # Example: 119 # randomPkg = import ../bla { ... 120 # stdenv = overrideSetup stdenv ../stdenv/generic/setup-latest.sh; 121 # }; 122 overrideSetup = stdenv: setupScript: stdenv.override { inherit setupScript; }; 123 124 # Return a modified stdenv that tries to build statically linked 125 # binaries. 126 makeStaticBinaries = 127 stdenv0: 128 stdenv0.override ( 129 old: 130 { 131 mkDerivationFromStdenv = withOldMkDerivation old ( 132 stdenv: mkDerivationSuper: args: 133 if stdenv.hostPlatform.isDarwin then 134 throw "Cannot build fully static binaries on Darwin/macOS" 135 else 136 (mkDerivationSuper args).overrideAttrs ( 137 args: 138 ( 139 if (args.__structuredAttrs or false) || (args ? env.NIX_CFLAGS_LINK) then 140 { 141 env = (args.env or { }) // { 142 NIX_CFLAGS_LINK = toString (args.env.NIX_CFLAGS_LINK or "") + " -static"; 143 }; 144 } 145 else 146 { 147 NIX_CFLAGS_LINK = toString (args.NIX_CFLAGS_LINK or "") + " -static"; 148 } 149 ) 150 // lib.optionalAttrs (!(args.dontAddStaticConfigureFlags or false)) { 151 configureFlags = (args.configureFlags or [ ]) ++ [ 152 "--disable-shared" # brrr... 153 ]; 154 cmakeFlags = (args.cmakeFlags or [ ]) ++ [ "-DCMAKE_SKIP_INSTALL_RPATH=On" ]; 155 } 156 ) 157 ); 158 } 159 // lib.optionalAttrs (stdenv0.hostPlatform.libc == "glibc") { 160 extraBuildInputs = (old.extraBuildInputs or [ ]) ++ [ 161 pkgs.glibc.static 162 ]; 163 } 164 ); 165 166 # Return a modified stdenv that builds static libraries instead of 167 # shared libraries. 168 makeStaticLibraries = 169 stdenv: 170 stdenv.override (old: { 171 mkDerivationFromStdenv = extendMkDerivationArgs old ( 172 args: 173 { 174 dontDisableStatic = true; 175 } 176 // lib.optionalAttrs (!(args.dontAddStaticConfigureFlags or false)) { 177 configureFlags = (args.configureFlags or [ ]) ++ [ 178 "--enable-static" 179 "--disable-shared" 180 ]; 181 cmakeFlags = (args.cmakeFlags or [ ]) ++ [ "-DBUILD_SHARED_LIBS:BOOL=OFF" ]; 182 mesonFlags = (args.mesonFlags or [ ]) ++ [ 183 "-Ddefault_library=static" 184 "-Ddefault_both_libraries=static" 185 ]; 186 } 187 ); 188 }); 189 190 # Best effort static binaries. Will still be linked to libSystem, 191 # but more portable than Nix store binaries. 192 makeStaticDarwin = 193 stdenv: 194 stdenv.override (old: { 195 mkDerivationFromStdenv = withOldMkDerivation old ( 196 stdenv: mkDerivationSuper: args: 197 (mkDerivationSuper args).overrideAttrs ( 198 prevAttrs: 199 if prevAttrs ? env.NIX_CFLAGS_LINK then 200 { 201 env = prevAttrs.env // { 202 NIX_CFLAGS_LINK = 203 toString (args.env.NIX_CFLAGS_LINK or "") 204 + lib.optionalString (stdenv.cc.isGNU or false) " -static-libgcc"; 205 }; 206 } 207 else 208 { 209 NIX_CFLAGS_LINK = 210 toString (prevAttrs.NIX_CFLAGS_LINK or "") 211 + lib.optionalString (stdenv.cc.isGNU or false) " -static-libgcc"; 212 } 213 ) 214 ); 215 }); 216 217 # Puts all the other ones together 218 makeStatic = 219 stdenv: 220 lib.foldl (lib.flip lib.id) stdenv ( 221 lib.optional stdenv.hostPlatform.isDarwin makeStaticDarwin 222 223 ++ [ 224 makeStaticLibraries 225 propagateBuildInputs 226 ] 227 228 # Apple does not provide a static version of libSystem or crt0.o 229 # So we can’t build static binaries without extensive hacks. 230 ++ lib.optional (!stdenv.hostPlatform.isDarwin) makeStaticBinaries 231 ); 232 233 /* 234 Modify a stdenv so that all buildInputs are implicitly propagated to 235 consuming derivations 236 */ 237 propagateBuildInputs = 238 stdenv: 239 stdenv.override (old: { 240 mkDerivationFromStdenv = extendMkDerivationArgs old (args: { 241 propagatedBuildInputs = (args.propagatedBuildInputs or [ ]) ++ (args.buildInputs or [ ]); 242 buildInputs = [ ]; 243 }); 244 }); 245 246 /* 247 Modify a stdenv so that the specified attributes are added to 248 every derivation returned by its mkDerivation function. 249 250 Example: 251 stdenvNoOptimise = 252 addAttrsToDerivation 253 { env.NIX_CFLAGS_COMPILE = "-O0"; } 254 stdenv; 255 */ 256 addAttrsToDerivation = 257 extraAttrs: stdenv: 258 stdenv.override (old: { 259 mkDerivationFromStdenv = extendMkDerivationArgs old (_: extraAttrs); 260 }); 261 262 /* 263 Use the trace output to report all processed derivations with their 264 license name. 265 */ 266 traceDrvLicenses = 267 stdenv: 268 stdenv.override (old: { 269 mkDerivationFromStdenv = overrideMkDerivationResult ( 270 pkg: 271 let 272 printDrvPath = 273 val: 274 let 275 drvPath = builtins.unsafeDiscardStringContext pkg.drvPath; 276 license = pkg.meta.license or null; 277 in 278 builtins.trace "@:drv:${toString drvPath}:${builtins.toString license}:@" val; 279 in 280 pkg 281 // { 282 outPath = printDrvPath pkg.outPath; 283 drvPath = printDrvPath pkg.drvPath; 284 } 285 ); 286 }); 287 288 /* 289 Modify a stdenv so that it produces debug builds; that is, 290 binaries have debug info, and compiler optimisations are 291 disabled. 292 */ 293 keepDebugInfo = 294 stdenv: 295 stdenv.override (old: { 296 mkDerivationFromStdenv = extendMkDerivationArgs old (args: { 297 dontStrip = true; 298 env = (args.env or { }) // { 299 NIX_CFLAGS_COMPILE = toString (args.env.NIX_CFLAGS_COMPILE or "") + " -ggdb -Og"; 300 NIX_RUSTFLAGS = toString (args.env.NIX_RUSTFLAGS or "") + " -g -C opt-level=0 -C strip=none"; 301 }; 302 }); 303 }); 304 305 # Modify a stdenv so that it uses the Gold linker. 306 useGoldLinker = 307 stdenv: 308 stdenv.override (old: { 309 mkDerivationFromStdenv = extendMkDerivationArgs old (args: { 310 NIX_CFLAGS_LINK = toString (args.NIX_CFLAGS_LINK or "") + " -fuse-ld=gold"; 311 }); 312 }); 313 314 /* 315 Copy the libstdc++ from the model stdenv to the target stdenv. 316 317 TODO(@connorbaker): 318 This interface provides behavior which should be revisited prior to the 319 release of 24.05. For a more detailed explanation and discussion, see 320 https://github.com/NixOS/nixpkgs/issues/283517. 321 */ 322 useLibsFrom = 323 modelStdenv: targetStdenv: 324 let 325 ccForLibs = modelStdenv.cc.cc; 326 /* 327 NOTE(@connorbaker): 328 This assumes targetStdenv.cc is a cc-wrapper. 329 */ 330 cc = targetStdenv.cc.override { 331 /* 332 NOTE(originally by rrbutani): 333 Normally the `useCcForLibs`/`gccForLibs` mechanism is used to get a 334 clang based `cc` to use `libstdc++` (from gcc). 335 336 Here we (ab)use it to use a `libstdc++` from a different `gcc` than our 337 `cc`. 338 339 Note that this does not inhibit our `cc`'s lib dir from being added to 340 cflags/ldflags (see `cc_solib` in `cc-wrapper`) but this is okay: our 341 `gccForLibs`'s paths should take precedence. 342 */ 343 useCcForLibs = true; 344 gccForLibs = ccForLibs; 345 }; 346 in 347 overrideCC targetStdenv cc; 348 349 useMoldLinker = 350 stdenv: 351 if stdenv.targetPlatform.isDarwin then 352 throw "Mold can't be used to emit Mach-O (Darwin) binaries" 353 else 354 let 355 bintools = stdenv.cc.bintools.override { 356 extraBuildCommands = '' 357 wrap ld.mold ${../build-support/bintools-wrapper/ld-wrapper.sh} ${pkgs.buildPackages.mold}/bin/ld.mold 358 wrap ${stdenv.cc.bintools.targetPrefix}ld.mold ${../build-support/bintools-wrapper/ld-wrapper.sh} ${pkgs.buildPackages.mold}/bin/ld.mold 359 wrap ${stdenv.cc.bintools.targetPrefix}ld ${../build-support/bintools-wrapper/ld-wrapper.sh} ${pkgs.buildPackages.mold}/bin/ld.mold 360 ''; 361 }; 362 in 363 stdenv.override ( 364 old: 365 { 366 allowedRequisites = null; 367 cc = stdenv.cc.override { inherit bintools; }; 368 # gcc >12.1.0 supports '-fuse-ld=mold' 369 # the wrap ld above in bintools supports gcc <12.1.0 and shouldn't harm >12.1.0 370 # https://github.com/rui314/mold#how-to-use 371 } 372 // 373 lib.optionalAttrs 374 (stdenv.cc.isClang || (stdenv.cc.isGNU && lib.versionAtLeast stdenv.cc.version "12")) 375 { 376 mkDerivationFromStdenv = extendMkDerivationArgs old (args: { 377 NIX_CFLAGS_LINK = toString (args.NIX_CFLAGS_LINK or "") + " -fuse-ld=mold"; 378 }); 379 } 380 ); 381 382 /* 383 Modify a stdenv so that it builds binaries optimized specifically 384 for the machine they are built on. 385 386 WARNING: this breaks purity! 387 */ 388 impureUseNativeOptimizations = 389 stdenv: 390 stdenv.override (old: { 391 mkDerivationFromStdenv = extendMkDerivationArgs old (args: { 392 env = (args.env or { }) // { 393 NIX_CFLAGS_COMPILE = toString (args.env.NIX_CFLAGS_COMPILE or "") + " -march=native"; 394 }; 395 396 NIX_ENFORCE_NO_NATIVE = false; 397 398 preferLocalBuild = true; 399 allowSubstitutes = false; 400 }); 401 }); 402 403 /* 404 Modify a stdenv so that it builds binaries with the specified list of 405 compilerFlags appended and passed to the compiler. 406 407 This example would recompile every derivation on the system with 408 -funroll-loops and -O3 passed to each gcc invocation. 409 410 Example: 411 nixpkgs.overlays = [ 412 (self: super: { 413 stdenv = super.withCFlags [ "-funroll-loops" "-O3" ] super.stdenv; 414 }) 415 ]; 416 */ 417 withCFlags = 418 compilerFlags: stdenv: 419 stdenv.override (old: { 420 mkDerivationFromStdenv = extendMkDerivationArgs old (args: { 421 env = (args.env or { }) // { 422 NIX_CFLAGS_COMPILE = toString (args.env.NIX_CFLAGS_COMPILE or "") + " ${toString compilerFlags}"; 423 }; 424 }); 425 }); 426 427 withDefaultHardeningFlags = 428 defaultHardeningFlags: stdenv: 429 let 430 bintools = 431 let 432 bintools' = stdenv.cc.bintools; 433 in 434 if bintools' ? override then 435 (bintools'.override { 436 inherit defaultHardeningFlags; 437 }) 438 else 439 bintools'; 440 in 441 stdenv.override (old: { 442 cc = 443 if stdenv.cc == null then 444 null 445 else 446 stdenv.cc.override { 447 inherit bintools; 448 }; 449 allowedRequisites = lib.mapNullable (rs: rs ++ [ bintools ]) (stdenv.allowedRequisites or null); 450 }); 451}