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}