Clone of https://github.com/NixOS/nixpkgs.git (to stress-test knotserver)
1# The Nixpkgs CC is not directly usable, since it doesn't know where
2# the C library and standard header files are. Therefore the compiler
3# produced by that package cannot be installed directly in a user
4# environment and used from the command line. So we use a wrapper
5# script that sets up the right environment variables so that the
6# compiler and the linker just "work".
7
8{
9 name ? "",
10 lib,
11 stdenvNoCC,
12 runtimeShell,
13 cc ? null,
14 libc ? null,
15 bintools,
16 coreutils ? null,
17 apple-sdk ? null,
18 zlib ? null,
19 nativeTools,
20 noLibc ? false,
21 nativeLibc,
22 nativePrefix ? "",
23 propagateDoc ? cc != null && cc ? man,
24 extraTools ? [ ],
25 extraPackages ? [ ],
26 extraBuildCommands ? "",
27 nixSupport ? { },
28 isGNU ? false,
29 isClang ? cc.isClang or false,
30 isZig ? cc.isZig or false,
31 isArocc ? cc.isArocc or false,
32 isCcache ? cc.isCcache or false,
33 gnugrep ? null,
34 expand-response-params,
35 libcxx ? null,
36
37 # Whether or not to add `-B` and `-L` to `nix-support/cc-{c,ld}flags`
38 useCcForLibs ?
39
40 # Always add these flags for Clang, because in order to compile (most
41 # software) it needs libraries that are shipped and compiled with gcc.
42 if isClang then
43 true
44
45 # Never add these flags for a build!=host cross-compiler or a host!=target
46 # ("cross-built-native") compiler; currently nixpkgs has a special build
47 # path for these (`crossStageStatic`). Hopefully at some point that build
48 # path will be merged with this one and this conditional will be removed.
49 else if (with stdenvNoCC; buildPlatform != hostPlatform || hostPlatform != targetPlatform) then
50 false
51
52 # Never add these flags when wrapping the bootstrapFiles' compiler; it has a
53 # /usr/-like layout with everything smashed into a single outpath, so it has
54 # no trouble finding its own libraries.
55 else if (cc.passthru.isFromBootstrapFiles or false) then
56 false
57
58 # Add these flags when wrapping `xgcc` (the first compiler that nixpkgs builds)
59 else if (cc.passthru.isXgcc or false) then
60 true
61
62 # Add these flags when wrapping `stdenv.cc`
63 else if (cc.stdenv.cc.cc.passthru.isXgcc or false) then
64 true
65
66 # Do not add these flags in any other situation. This is `false` mainly to
67 # prevent these flags from being added when wrapping *old* versions of gcc
68 # (e.g. `gcc6Stdenv`), since they will cause the old gcc to get `-B` and
69 # `-L` flags pointing at the new gcc's libstdc++ headers. Example failure:
70 # https://hydra.nixos.org/build/213125495
71 else
72 false,
73
74 # the derivation at which the `-B` and `-L` flags added by `useCcForLibs` will point
75 gccForLibs ? if useCcForLibs then cc else null,
76 fortify-headers ? null,
77 includeFortifyHeaders ? null,
78}:
79
80assert nativeTools -> !propagateDoc && nativePrefix != "";
81assert !nativeTools -> cc != null && coreutils != null && gnugrep != null;
82assert !(nativeLibc && noLibc);
83assert (noLibc || nativeLibc) == (libc == null);
84
85let
86 inherit (lib)
87 attrByPath
88 concatMapStrings
89 concatStringsSep
90 escapeShellArg
91 escapeShellArgs
92 getBin
93 getDev
94 getLib
95 getName
96 getVersion
97 hasPrefix
98 mapAttrsToList
99 optional
100 optionalAttrs
101 optionals
102 optionalString
103 removePrefix
104 removeSuffix
105 replaceStrings
106 toList
107 versionAtLeast
108 ;
109
110 inherit (stdenvNoCC) hostPlatform targetPlatform;
111
112 includeFortifyHeaders' =
113 if includeFortifyHeaders != null then
114 includeFortifyHeaders
115 else
116 (targetPlatform.libc == "musl" && isGNU);
117
118 # Prefix for binaries. Customarily ends with a dash separator.
119 #
120 # TODO(@Ericson2314) Make unconditional, or optional but always true by default.
121 targetPrefix = optionalString (targetPlatform != hostPlatform) (targetPlatform.config + "-");
122
123 ccVersion = getVersion cc;
124 ccName = removePrefix targetPrefix (getName cc);
125
126 libc_bin = optionalString (libc != null) (getBin libc);
127 libc_dev = optionalString (libc != null) (getDev libc);
128 libc_lib = optionalString (libc != null) (getLib libc);
129 cc_solib = getLib cc + optionalString (targetPlatform != hostPlatform) "/${targetPlatform.config}";
130
131 # The wrapper scripts use 'cat' and 'grep', so we may need coreutils.
132 coreutils_bin = optionalString (!nativeTools) (getBin coreutils);
133
134 # The "suffix salt" is a arbitrary string added in the end of env vars
135 # defined by cc-wrapper's hooks so that multiple cc-wrappers can be used
136 # without interfering. For the moment, it is defined as the target triple,
137 # adjusted to be a valid bash identifier. This should be considered an
138 # unstable implementation detail, however.
139 suffixSalt =
140 replaceStrings [ "-" "." ] [ "_" "_" ] targetPlatform.config
141 + lib.optionalString (targetPlatform.isDarwin && targetPlatform.isStatic) "_static";
142
143 useGccForLibs =
144 useCcForLibs
145 && libcxx == null
146 && !targetPlatform.isDarwin
147 && !(targetPlatform.useLLVM or false)
148 && !(targetPlatform.useAndroidPrebuilt or false)
149 && !(targetPlatform.isiOS or false)
150 && gccForLibs != null;
151 gccForLibs_solib =
152 getLib gccForLibs + optionalString (targetPlatform != hostPlatform) "/${targetPlatform.config}";
153
154 # Analogously to cc_solib and gccForLibs_solib
155 libcxx_solib = "${getLib libcxx}/lib";
156
157 # The following two functions, `isGccArchSupported` and
158 # `isGccTuneSupported`, only handle those situations where a flag
159 # (`-march` or `-mtune`) is accepted by one compiler but rejected
160 # by another, and both compilers are relevant to nixpkgs. We are
161 # not trying to maintain a complete list of all flags accepted by
162 # all versions of all compilers ever in nixpkgs.
163 #
164 # The two main cases of interest are:
165 #
166 # - One compiler is gcc and the other is clang
167 # - One compiler is pkgs.gcc and the other is bootstrap-files.gcc
168 # -- older compilers (for example bootstrap's GCC 5) fail with
169 # -march=too-modern-cpu
170
171 isGccArchSupported =
172 arch:
173 if targetPlatform.isPower then
174 false
175 # powerpc does not allow -march=
176 else if isGNU then
177 {
178 # Generic
179 x86-64-v2 = versionAtLeast ccVersion "11.0";
180 x86-64-v3 = versionAtLeast ccVersion "11.0";
181 x86-64-v4 = versionAtLeast ccVersion "11.0";
182
183 # Intel
184 skylake = true;
185 skylake-avx512 = true;
186 cannonlake = versionAtLeast ccVersion "8.0";
187 icelake-client = versionAtLeast ccVersion "8.0";
188 icelake-server = versionAtLeast ccVersion "8.0";
189 cascadelake = versionAtLeast ccVersion "9.0";
190 cooperlake = versionAtLeast ccVersion "10.0";
191 tigerlake = versionAtLeast ccVersion "10.0";
192 knm = versionAtLeast ccVersion "8.0";
193 alderlake = versionAtLeast ccVersion "12.0";
194 sapphirerapids = versionAtLeast ccVersion "11.0";
195 emeraldrapids = versionAtLeast ccVersion "13.0";
196 sierraforest = versionAtLeast ccVersion "13.0";
197
198 # AMD
199 znver1 = true;
200 znver2 = versionAtLeast ccVersion "9.0";
201 znver3 = versionAtLeast ccVersion "11.0";
202 znver4 = versionAtLeast ccVersion "13.0";
203 znver5 = versionAtLeast ccVersion "14.0";
204
205 # LoongArch64
206 # https://gcc.gnu.org/gcc-12/changes.html#loongarch
207 # la464 was added together with loongarch64 support
208 # https://gcc.gnu.org/gcc-14/changes.html#loongarch
209 "la64v1.0" = versionAtLeast ccVersion "14.0";
210 "la64v1.1" = versionAtLeast ccVersion "14.0";
211 la664 = versionAtLeast ccVersion "14.0";
212 }
213 .${arch} or true
214 else if isClang then
215 {
216 #Generic
217 x86-64-v2 = versionAtLeast ccVersion "12.0";
218 x86-64-v3 = versionAtLeast ccVersion "12.0";
219 x86-64-v4 = versionAtLeast ccVersion "12.0";
220
221 # Intel
222 cannonlake = versionAtLeast ccVersion "5.0";
223 icelake-client = versionAtLeast ccVersion "7.0";
224 icelake-server = versionAtLeast ccVersion "7.0";
225 knm = versionAtLeast ccVersion "7.0";
226 alderlake = versionAtLeast ccVersion "16.0";
227 sapphirerapids = versionAtLeast ccVersion "12.0";
228 emeraldrapids = versionAtLeast ccVersion "16.0";
229
230 # AMD
231 znver1 = versionAtLeast ccVersion "4.0";
232 znver2 = versionAtLeast ccVersion "9.0";
233 znver3 = versionAtLeast ccVersion "12.0";
234 znver4 = versionAtLeast ccVersion "16.0";
235 znver5 = versionAtLeast ccVersion "19.1";
236
237 # LoongArch64
238 # https://releases.llvm.org/16.0.0/tools/clang/docs/ReleaseNotes.html#loongarch-support
239 # la464 was added together with loongarch64 support
240 # https://releases.llvm.org/19.1.0/tools/clang/docs/ReleaseNotes.html#loongarch-support
241 "la64v1.0" = versionAtLeast ccVersion "19.1";
242 "la64v1.1" = versionAtLeast ccVersion "19.1";
243 la664 = versionAtLeast ccVersion "19.1";
244 }
245 .${arch} or true
246 else
247 false;
248
249 isGccTuneSupported =
250 tune:
251 # for x86 -mtune= takes the same values as -march, plus two more:
252 if targetPlatform.isx86 then
253 {
254 generic = true;
255 intel = true;
256 }
257 .${tune} or (isGccArchSupported tune)
258 # on arm64, the -mtune= values are specific processors
259 else if targetPlatform.isAarch64 then
260 (
261 if isGNU then
262 {
263 cortex-a53 = true;
264 cortex-a72 = true;
265 "cortex-a72.cortex-a53" = true;
266 }
267 .${tune} or false
268 else if isClang then
269 {
270 cortex-a53 = versionAtLeast ccVersion "3.9"; # llvm dfc5d1
271 }
272 .${tune} or false
273 else
274 false
275 )
276 else if targetPlatform.isPower then
277 # powerpc does not support -march
278 true
279 else if targetPlatform.isMips then
280 # for mips -mtune= takes the same values as -march
281 isGccArchSupported tune
282 else
283 false;
284
285 # Clang does not support as many `-mtune=` values as gcc does;
286 # this function will return the best possible approximation of the
287 # provided `-mtune=` value, or `null` if none exists.
288 #
289 # Note: this function can make use of ccVersion; for example, `if
290 # versionOlder ccVersion "12" then ...`
291 findBestTuneApproximation =
292 tune:
293 let
294 guess =
295 if isClang then
296 {
297 # clang does not tune for big.LITTLE chips
298 "cortex-a72.cortex-a53" = "cortex-a72";
299 }
300 .${tune} or tune
301 else
302 tune;
303 in
304 if isGccTuneSupported guess then guess else null;
305
306 thumb = if targetPlatform.gcc.thumb then "thumb" else "arm";
307 tune =
308 if targetPlatform ? gcc.tune then findBestTuneApproximation targetPlatform.gcc.tune else null;
309
310 # Machine flags. These are necessary to support
311
312 # TODO: We should make a way to support miscellaneous machine
313 # flags and other gcc flags as well.
314
315 machineFlags =
316 # Always add -march based on cpu in triple. Sometimes there is a
317 # discrepancy (x86_64 vs. x86-64), so we provide an "arch" arg in
318 # that case.
319 optional (
320 targetPlatform ? gcc.arch
321 && !(targetPlatform.isDarwin && targetPlatform.isAarch64)
322 && isGccArchSupported targetPlatform.gcc.arch
323 ) "-march=${targetPlatform.gcc.arch}"
324 ++
325 # TODO: aarch64-darwin has mcpu incompatible with gcc
326 optional (
327 targetPlatform ? gcc.cpu && !(targetPlatform.isDarwin && targetPlatform.isAarch64)
328 ) "-mcpu=${targetPlatform.gcc.cpu}"
329 ++
330 # -mfloat-abi only matters on arm32 but we set it here
331 # unconditionally just in case. If the abi specifically sets hard
332 # vs. soft floats we use it here.
333 optional (targetPlatform ? gcc.float-abi) "-mfloat-abi=${targetPlatform.gcc.float-abi}"
334 ++ optional (targetPlatform ? gcc.fpu) "-mfpu=${targetPlatform.gcc.fpu}"
335 ++ optional (targetPlatform ? gcc.mode) "-mmode=${targetPlatform.gcc.mode}"
336 ++ optional (targetPlatform ? gcc.thumb) "-m${thumb}"
337 ++ optional (tune != null) "-mtune=${tune}"
338 ++
339 optional (targetPlatform ? gcc.strict-align)
340 "-m${optionalString (!targetPlatform.gcc.strict-align) "no-"}strict-align"
341 ++ optional (
342 targetPlatform ? gcc.cmodel
343 &&
344 # TODO: clang on powerpcspe also needs a condition: https://github.com/llvm/llvm-project/issues/71356
345 # https://releases.llvm.org/18.1.6/tools/clang/docs/ReleaseNotes.html#loongarch-support
346 ((targetPlatform.isLoongArch64 && isClang) -> versionAtLeast ccVersion "18.1")
347 ) "-mcmodel=${targetPlatform.gcc.cmodel}";
348
349 defaultHardeningFlags = bintools.defaultHardeningFlags or [ ];
350
351 # if cc.hardeningUnsupportedFlagsByTargetPlatform exists, this is
352 # called with the targetPlatform as an argument and
353 # cc.hardeningUnsupportedFlags is completely ignored - the function
354 # is responsible for including the constant hardeningUnsupportedFlags
355 # list however it sees fit.
356 ccHardeningUnsupportedFlags =
357 if cc ? hardeningUnsupportedFlagsByTargetPlatform then
358 cc.hardeningUnsupportedFlagsByTargetPlatform targetPlatform
359 else
360 (cc.hardeningUnsupportedFlags or [ ]);
361
362 darwinPlatformForCC = optionalString targetPlatform.isDarwin (
363 if (targetPlatform.darwinPlatform == "macos" && isGNU) then
364 "macosx"
365 else
366 targetPlatform.darwinPlatform
367 );
368
369 # Header files that use `__FILE__` (e.g., for error reporting) lead
370 # to unwanted references to development packages and outputs in built
371 # binaries, like C++ programs depending on GCC and Boost at runtime.
372 #
373 # We use `-fmacro-prefix-map` to avoid the store references in these
374 # situations while keeping them in compiler diagnostics and debugging
375 # and profiling output.
376 #
377 # Unfortunately, doing this with GCC runs into issues with compiler
378 # argument length limits due to <https://gcc.gnu.org/PR111527>, so we
379 # disable it there in favour of our existing patch.
380 #
381 # TODO: Drop `mangle-NIX_STORE-in-__FILE__.patch` from GCC and make
382 # this unconditional once the upstream bug is fixed.
383 useMacroPrefixMap = !isGNU;
384in
385
386assert includeFortifyHeaders' -> fortify-headers != null;
387
388# Ensure bintools matches
389assert libc_bin == bintools.libc_bin;
390assert libc_dev == bintools.libc_dev;
391assert libc_lib == bintools.libc_lib;
392assert nativeTools == bintools.nativeTools;
393assert nativeLibc == bintools.nativeLibc;
394assert nativePrefix == bintools.nativePrefix;
395
396stdenvNoCC.mkDerivation {
397 pname = targetPrefix + (if name != "" then name else "${ccName}-wrapper");
398 version = optionalString (cc != null) ccVersion;
399
400 preferLocalBuild = true;
401
402 outputs = [
403 "out"
404 ]
405 ++ optionals propagateDoc [
406 "man"
407 "info"
408 ];
409
410 # Cannot be in "passthru" due to "substituteAll"
411 inherit isArocc;
412
413 passthru = {
414 inherit targetPrefix suffixSalt;
415 # "cc" is the generic name for a C compiler, but there is no one for package
416 # providing the linker and related tools. The two we use now are GNU
417 # Binutils, and Apple's "cctools"; "bintools" as an attempt to find an
418 # unused middle-ground name that evokes both.
419 inherit bintools;
420 inherit
421 cc
422 libc
423 libcxx
424 nativeTools
425 nativeLibc
426 nativePrefix
427 isGNU
428 isClang
429 isZig
430 ;
431
432 emacsBufferSetup = pkgs: ''
433 ; We should handle propagation here too
434 (mapc
435 (lambda (arg)
436 (when (file-directory-p (concat arg "/include"))
437 (setenv "NIX_CFLAGS_COMPILE_${suffixSalt}" (concat (getenv "NIX_CFLAGS_COMPILE_${suffixSalt}") " -isystem " arg "/include"))))
438 '(${concatStringsSep " " (map (pkg: "\"${pkg}\"") pkgs)}))
439 '';
440
441 # Expose expand-response-params we are /actually/ using. In stdenv
442 # bootstrapping, expand-response-params usually comes from an earlier stage,
443 # so it is important to expose this for reference checking.
444 inherit expand-response-params;
445
446 inherit nixSupport;
447
448 inherit defaultHardeningFlags;
449 };
450
451 dontBuild = true;
452 dontConfigure = true;
453 enableParallelBuilding = true;
454
455 # TODO(@connorbaker):
456 # This is a quick fix unblock builds broken by https://github.com/NixOS/nixpkgs/pull/370750.
457 dontCheckForBrokenSymlinks = true;
458
459 unpackPhase = ''
460 src=$PWD
461 '';
462
463 wrapper = ./cc-wrapper.sh;
464
465 installPhase = ''
466 mkdir -p $out/bin $out/nix-support
467
468 wrap() {
469 local dst="$1"
470 local wrapper="$2"
471 export prog="$3"
472 export use_response_file_by_default=${if isClang && !isCcache then "1" else "0"}
473 substituteAll "$wrapper" "$out/bin/$dst"
474 chmod +x "$out/bin/$dst"
475 }
476
477 include() {
478 printf -- '%s %s\n' "$1" "$2"
479 ${lib.optionalString useMacroPrefixMap ''
480 local scrubbed="$NIX_STORE/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-''${2#"$NIX_STORE"/*-}"
481 printf -- '-fmacro-prefix-map=%s=%s\n' "$2" "$scrubbed"
482 ''}
483 }
484 ''
485
486 + (
487 if nativeTools then
488 ''
489 echo ${if targetPlatform.isDarwin then cc else nativePrefix} > $out/nix-support/orig-cc
490
491 ccPath="${if targetPlatform.isDarwin then cc else nativePrefix}/bin"
492 ''
493 else
494 ''
495 echo $cc > $out/nix-support/orig-cc
496
497 ccPath="${cc}/bin"
498 ''
499 )
500
501 # Create symlinks to everything in the bintools wrapper.
502 + ''
503 for bbin in $bintools/bin/*; do
504 mkdir -p "$out/bin"
505 ln -s "$bbin" "$out/bin/$(basename $bbin)"
506 done
507 ''
508
509 # We export environment variables pointing to the wrapped nonstandard
510 # cmds, lest some lousy configure script use those to guess compiler
511 # version.
512 + ''
513 export named_cc=${targetPrefix}cc
514 export named_cxx=${targetPrefix}c++
515
516 if [ -e $ccPath/${targetPrefix}gcc ]; then
517 wrap ${targetPrefix}gcc $wrapper $ccPath/${targetPrefix}gcc
518 ln -s ${targetPrefix}gcc $out/bin/${targetPrefix}cc
519 export named_cc=${targetPrefix}gcc
520 export named_cxx=${targetPrefix}g++
521 elif [ -e $ccPath/clang ]; then
522 wrap ${targetPrefix}clang $wrapper $ccPath/clang
523 ln -s ${targetPrefix}clang $out/bin/${targetPrefix}cc
524 export named_cc=${targetPrefix}clang
525 export named_cxx=${targetPrefix}clang++
526 elif [ -e $ccPath/arocc ]; then
527 wrap ${targetPrefix}arocc $wrapper $ccPath/arocc
528 ln -s ${targetPrefix}arocc $out/bin/${targetPrefix}cc
529 export named_cc=${targetPrefix}arocc
530 fi
531
532 if [ -e $ccPath/${targetPrefix}g++ ]; then
533 wrap ${targetPrefix}g++ $wrapper $ccPath/${targetPrefix}g++
534 ln -s ${targetPrefix}g++ $out/bin/${targetPrefix}c++
535 elif [ -e $ccPath/clang++ ]; then
536 wrap ${targetPrefix}clang++ $wrapper $ccPath/clang++
537 ln -s ${targetPrefix}clang++ $out/bin/${targetPrefix}c++
538 fi
539
540 if [ -e $ccPath/${targetPrefix}cpp ]; then
541 wrap ${targetPrefix}cpp $wrapper $ccPath/${targetPrefix}cpp
542 elif [ -e $ccPath/cpp ]; then
543 wrap ${targetPrefix}cpp $wrapper $ccPath/cpp
544 fi
545 ''
546
547 # No need to wrap gnat, gnatkr, gnatname or gnatprep; we can just symlink them in
548 + optionalString cc.langAda or false ''
549 for cmd in gnatbind gnatchop gnatclean gnatlink gnatls gnatmake; do
550 wrap ${targetPrefix}$cmd ${./gnat-wrapper.sh} $ccPath/${targetPrefix}$cmd
551 done
552
553 for cmd in gnat gnatkr gnatname gnatprep; do
554 ln -s $ccPath/${targetPrefix}$cmd $out/bin/${targetPrefix}$cmd
555 done
556
557 # this symlink points to the unwrapped gnat's output "out". It is used by
558 # our custom gprconfig compiler description to find GNAT's ada runtime. See
559 # ../../development/ada-modules/gprbuild/{boot.nix, nixpkgs-gnat.xml}
560 ln -sf ${cc} $out/nix-support/gprconfig-gnat-unwrapped
561 ''
562
563 + optionalString cc.langD or false ''
564 wrap ${targetPrefix}gdc $wrapper $ccPath/${targetPrefix}gdc
565 ''
566
567 + optionalString cc.langFortran or false ''
568 wrap ${targetPrefix}gfortran $wrapper $ccPath/${targetPrefix}gfortran
569 ln -sv ${targetPrefix}gfortran $out/bin/${targetPrefix}g77
570 ln -sv ${targetPrefix}gfortran $out/bin/${targetPrefix}f77
571 export named_fc=${targetPrefix}gfortran
572 ''
573
574 + optionalString cc.langJava or false ''
575 wrap ${targetPrefix}gcj $wrapper $ccPath/${targetPrefix}gcj
576 ''
577
578 + optionalString cc.langGo or false ''
579 wrap ${targetPrefix}gccgo $wrapper $ccPath/${targetPrefix}gccgo
580 wrap ${targetPrefix}go ${./go-wrapper.sh} $ccPath/${targetPrefix}go
581 '';
582
583 strictDeps = true;
584 propagatedBuildInputs = [
585 bintools
586 ]
587 ++ extraTools
588 ++ optionals cc.langD or cc.langJava or false [ zlib ];
589 depsTargetTargetPropagated = optional (libcxx != null) libcxx ++ extraPackages;
590
591 setupHooks = [
592 ../setup-hooks/role.bash
593 ]
594 ++ optional (cc.langC or true) ./setup-hook.sh
595 ++ optional (cc.langFortran or false) ./fortran-hook.sh
596 ++ optional (targetPlatform.isWindows) (
597 stdenvNoCC.mkDerivation {
598 name = "win-dll-hook.sh";
599 dontUnpack = true;
600 installPhase = ''
601 echo addToSearchPath "LINK_DLL_FOLDERS" "${cc_solib}/lib" > $out
602 echo addToSearchPath "LINK_DLL_FOLDERS" "${cc_solib}/lib64" >> $out
603 echo addToSearchPath "LINK_DLL_FOLDERS" "${cc_solib}/lib32" >> $out
604 '';
605 }
606 );
607
608 postFixup =
609 # Ensure flags files exists, as some other programs cat them. (That these
610 # are considered an exposed interface is a bit dubious, but fine for now.)
611 ''
612 touch "$out/nix-support/cc-cflags"
613 touch "$out/nix-support/cc-ldflags"
614 ''
615
616 # Backwards compatibility for packages expecting this file, e.g. with
617 # `$NIX_CC/nix-support/dynamic-linker`.
618 #
619 # TODO(@Ericson2314): Remove this after stable release and force
620 # everyone to refer to bintools-wrapper directly.
621 + optionalString (!isArocc) ''
622 if [[ -f "$bintools/nix-support/dynamic-linker" ]]; then
623 ln -s "$bintools/nix-support/dynamic-linker" "$out/nix-support"
624 fi
625 if [[ -f "$bintools/nix-support/dynamic-linker-m32" ]]; then
626 ln -s "$bintools/nix-support/dynamic-linker-m32" "$out/nix-support"
627 fi
628 ''
629
630 ##
631 ## GCC libs for non-GCC support
632 ##
633 + optionalString (useGccForLibs && isClang) ''
634
635 echo "-B${gccForLibs}/lib/gcc/${targetPlatform.config}/${gccForLibs.version}" >> $out/nix-support/cc-cflags
636 ''
637 + optionalString (useGccForLibs && !isArocc) ''
638 echo "-L${gccForLibs}/lib/gcc/${targetPlatform.config}/${gccForLibs.version}" >> $out/nix-support/cc-ldflags
639 echo "-L${gccForLibs_solib}/lib" >> $out/nix-support/cc-ldflags
640 ''
641
642 # TODO We would like to connect this to `useGccForLibs`, but we cannot yet
643 # because `libcxxStdenv` on linux still needs this. Maybe someday we'll
644 # always set `useLLVM` on Darwin, and maybe also break down `useLLVM` into
645 # fine-grained use flags (libgcc vs compiler-rt, ld.lld vs legacy, libc++
646 # vs libstdc++, etc.) since Darwin isn't `useLLVM` on all counts. (See
647 # https://clang.llvm.org/docs/Toolchain.html for all the axes one might
648 # break `useLLVM` into.)
649 +
650 optionalString
651 (
652 isClang
653 && targetPlatform.isLinux
654 && !(targetPlatform.useAndroidPrebuilt or false)
655 && !(targetPlatform.useLLVM or false)
656 && gccForLibs != null
657 )
658 (
659 ''
660 echo "--gcc-toolchain=${gccForLibs}" >> $out/nix-support/cc-cflags
661
662 # Pull in 'cc.out' target to get 'libstdc++fs.a'. It should be in
663 # 'cc.lib'. But it's a gcc package bug.
664 # TODO(trofi): remove once gcc is fixed to move libraries to .lib output.
665 echo "-L${gccForLibs}/${
666 optionalString (targetPlatform != hostPlatform) "/${targetPlatform.config}"
667 }/lib" >> $out/nix-support/cc-ldflags
668 ''
669 # this ensures that when clang passes -lgcc_s to lld (as it does
670 # when building e.g. firefox), lld is able to find libgcc_s.so
671 + optionals (!isArocc) (
672 concatMapStrings (libgcc: ''
673 echo "-L${libgcc}/lib" >> $out/nix-support/cc-ldflags
674 '') (toList (gccForLibs.libgcc or [ ]))
675 )
676 )
677
678 ##
679 ## General libc support
680 ##
681
682 # The "-B${libc_lib}/lib/" flag is a quick hack to force gcc to link
683 # against the crt1.o from our own glibc, rather than the one in
684 # /usr/lib. (This is only an issue when using an `impure'
685 # compiler/linker, i.e., one that searches /usr/lib and so on.)
686 #
687 # Unfortunately, setting -B appears to override the default search
688 # path. Thus, the gcc-specific "../includes-fixed" directory is
689 # now longer searched and glibc's <limits.h> header fails to
690 # compile, because it uses "#include_next <limits.h>" to find the
691 # limits.h file in ../includes-fixed. To remedy the problem,
692 # another -idirafter is necessary to add that directory again.
693 + optionalString (libc != null) (
694 ''
695 touch "$out/nix-support/libc-cflags"
696 touch "$out/nix-support/libc-ldflags"
697 ''
698 + optionalString (!isArocc) ''
699 echo "-B${libc_lib}${libc.libdir or "/lib/"}" >> $out/nix-support/libc-crt1-cflags
700 ''
701 + optionalString (!(cc.langD or false)) ''
702 include "-${
703 if isArocc then "I" else "idirafter"
704 }" "${libc_dev}${libc.incdir or "/include"}" >> $out/nix-support/libc-cflags
705 ''
706 + optionalString (isGNU && (!(cc.langD or false))) ''
707 for dir in "${cc}"/lib/gcc/*/*/include-fixed; do
708 include '-idirafter' ''${dir} >> $out/nix-support/libc-cflags
709 done
710 ''
711 + ''
712
713 echo "${libc_lib}" > $out/nix-support/orig-libc
714 echo "${libc_dev}" > $out/nix-support/orig-libc-dev
715 ''
716 # fortify-headers is a set of wrapper headers that augment libc
717 # and use #include_next to pass through to libc's true
718 # implementations, so must appear before them in search order.
719 # in theory a correctly placed -idirafter could be used, but in
720 # practice the compiler may have been built with a --with-headers
721 # like option that forces the libc headers before all -idirafter,
722 # hence -isystem here.
723 + optionalString includeFortifyHeaders' ''
724 include -isystem "${fortify-headers}/include" >> $out/nix-support/libc-cflags
725 ''
726 )
727
728 ##
729 ## General libc++ support
730 ##
731
732 # We have a libc++ directly, we have one via "smuggled" GCC, or we have one
733 # bundled with the C compiler because it is GCC
734 +
735 optionalString
736 (libcxx != null || (useGccForLibs && gccForLibs.langCC or false) || (isGNU && cc.langCC or false))
737 ''
738 touch "$out/nix-support/libcxx-cxxflags"
739 touch "$out/nix-support/libcxx-ldflags"
740 ''
741 # Adding -isystem flags should be done only for clang; gcc
742 # already knows how to find its own libstdc++, and adding
743 # additional -isystem flags will confuse gfortran (see
744 # https://github.com/NixOS/nixpkgs/pull/209870#issuecomment-1500550903)
745 + optionalString (libcxx == null && isClang && (useGccForLibs && gccForLibs.langCC or false)) ''
746 for dir in ${gccForLibs}/include/c++/*; do
747 include -isystem "$dir" >> $out/nix-support/libcxx-cxxflags
748 done
749 for dir in ${gccForLibs}/include/c++/*/${targetPlatform.config}; do
750 include -isystem "$dir" >> $out/nix-support/libcxx-cxxflags
751 done
752 ''
753 + optionalString (libcxx.isLLVM or false) ''
754 include -isystem "${getDev libcxx}/include/c++/v1" >> $out/nix-support/libcxx-cxxflags
755 echo "-stdlib=libc++" >> $out/nix-support/libcxx-ldflags
756 ''
757 # GCC NG friendly libc++
758 + optionalString (libcxx != null && libcxx.isGNU or false) ''
759 include -isystem "${getDev libcxx}/include" >> $out/nix-support/libcxx-cxxflags
760 ''
761
762 ##
763 ## Initial CFLAGS
764 ##
765
766 # GCC shows ${cc_solib}/lib in `gcc -print-search-dirs', but not
767 # ${cc_solib}/lib64 (even though it does actually search there...)..
768 # This confuses libtool. So add it to the compiler tool search
769 # path explicitly.
770 + optionalString (!nativeTools && !isArocc) ''
771 if [ -e "${cc_solib}/lib64" -a ! -L "${cc_solib}/lib64" ]; then
772 ccLDFlags+=" -L${cc_solib}/lib64"
773 ccCFlags+=" -B${cc_solib}/lib64"
774 fi
775 ccLDFlags+=" -L${cc_solib}/lib"
776 ccCFlags+=" -B${cc_solib}/lib"
777
778 ''
779 + optionalString (cc.langAda or false && !isArocc) ''
780 touch "$out/nix-support/gnat-cflags"
781 touch "$out/nix-support/gnat-ldflags"
782 basePath=$(echo $cc/lib/*/*/*)
783 ccCFlags+=" -B$basePath -I$basePath/adainclude"
784 gnatCFlags="-I$basePath/adainclude -I$basePath/adalib"
785
786 echo "$gnatCFlags" >> $out/nix-support/gnat-cflags
787 ''
788 + ''
789 echo "$ccLDFlags" >> $out/nix-support/cc-ldflags
790 echo "$ccCFlags" >> $out/nix-support/cc-cflags
791 ''
792 + optionalString (targetPlatform.isDarwin && (libcxx != null) && (cc.isClang or false)) ''
793 echo " -L${libcxx_solib}" >> $out/nix-support/cc-ldflags
794 ''
795
796 ## Prevent clang from seeing /usr/include. There is a desire to achieve this
797 ## through alternate means because it breaks -sysroot and related functionality.
798 #
799 # This flag prevents global system header directories from
800 # leaking through on non‐NixOS Linux. However, on macOS, the
801 # SDK path is used as the sysroot, and forcing `-nostdlibinc`
802 # breaks `-isysroot` with an unwrapped compiler. As macOS has
803 # no `/usr/include`, there’s essentially no risk to dropping
804 # the flag there. See discussion in NixOS/nixpkgs#191152.
805 #
806 +
807 optionalString
808 (
809 (cc.isClang or false)
810 && !(cc.isROCm or false)
811 && !targetPlatform.isDarwin
812 && !targetPlatform.isAndroid
813 )
814 ''
815 echo " -nostdlibinc" >> $out/nix-support/cc-cflags
816 ''
817
818 ##
819 ## Man page and info support
820 ##
821 + optionalString propagateDoc ''
822 ln -s ${cc.man} $man
823 ln -s ${cc.info} $info
824 ''
825 + optionalString (cc.langD or cc.langJava or false && !isArocc) ''
826 echo "-B${zlib}${zlib.libdir or "/lib/"}" >> $out/nix-support/libc-cflags
827 ''
828
829 ##
830 ## Hardening support
831 ##
832 + ''
833 export hardening_unsupported_flags="${concatStringsSep " " ccHardeningUnsupportedFlags}"
834 ''
835
836 # Do not prevent omission of framepointers on x86 32bit due to the small
837 # number of general purpose registers. Keeping EBP available provides
838 # non-trivial performance benefits.
839 # Also skip s390/s390x as it fails to build glibc and causes
840 # performance regressions:
841 # https://bugs.launchpad.net/ubuntu-z-systems/+bug/2064538
842 # https://github.com/NixOS/nixpkgs/issues/428260
843 + (
844 let
845 enable_fp = !targetPlatform.isx86_32 && !targetPlatform.isS390;
846 enable_leaf_fp =
847 enable_fp
848 && (
849 targetPlatform.isx86_64
850 || targetPlatform.isAarch64
851 || (targetPlatform.isRiscV && (!isGNU || versionAtLeast ccVersion "15.1"))
852 );
853 in
854 optionalString enable_fp ''
855 echo " -fno-omit-frame-pointer ${optionalString enable_leaf_fp "-mno-omit-leaf-frame-pointer "}" >> $out/nix-support/cc-cflags-before
856 ''
857 )
858
859 # For clang, this is handled in add-clang-cc-cflags-before.sh
860 + optionalString (!isClang && machineFlags != [ ]) ''
861 printf "%s\n" ${lib.escapeShellArgs machineFlags} >> $out/nix-support/cc-cflags-before
862 ''
863
864 # TODO: categorize these and figure out a better place for them
865 + optionalString targetPlatform.isWindows ''
866 hardening_unsupported_flags+=" pic"
867 ''
868 + optionalString targetPlatform.isMinGW ''
869 hardening_unsupported_flags+=" stackprotector fortify"
870 ''
871 + optionalString targetPlatform.isAvr ''
872 hardening_unsupported_flags+=" stackprotector pic"
873 ''
874 + optionalString (targetPlatform.libc == "newlib" || targetPlatform.libc == "newlib-nano") ''
875 hardening_unsupported_flags+=" stackprotector fortify pie pic"
876 ''
877 + optionalString (targetPlatform.libc == "musl" && targetPlatform.isx86_32) ''
878 hardening_unsupported_flags+=" stackprotector"
879 ''
880 + optionalString targetPlatform.isNetBSD ''
881 hardening_unsupported_flags+=" stackprotector fortify"
882 ''
883 + optionalString cc.langAda or false ''
884 hardening_unsupported_flags+=" format stackprotector strictoverflow"
885 ''
886 + optionalString cc.langD or false ''
887 hardening_unsupported_flags+=" format"
888 ''
889 + optionalString cc.langFortran or false ''
890 hardening_unsupported_flags+=" format"
891 ''
892 + optionalString targetPlatform.isWasm ''
893 hardening_unsupported_flags+=" stackprotector fortify pie pic"
894 ''
895 + optionalString targetPlatform.isMicroBlaze ''
896 hardening_unsupported_flags+=" stackprotector"
897 ''
898
899 + optionalString (libc != null && targetPlatform.isAvr && !isArocc) ''
900 for isa in avr5 avr3 avr4 avr6 avr25 avr31 avr35 avr51 avrxmega2 avrxmega4 avrxmega5 avrxmega6 avrxmega7 tiny-stack; do
901 echo "-B${getLib libc}/avr/lib/$isa" >> $out/nix-support/libc-crt1-cflags
902 done
903 ''
904
905 + optionalString targetPlatform.isAndroid ''
906 echo "-D__ANDROID_API__=${targetPlatform.androidSdkVersion}" >> $out/nix-support/cc-cflags
907 ''
908
909 # There are a few tools (to name one libstdcxx5) which do not work
910 # well with multi line flags, so make the flags single line again
911 + ''
912 for flags in "$out/nix-support"/*flags*; do
913 substituteInPlace "$flags" --replace $'\n' ' '
914 done
915
916 substituteAll ${./add-flags.sh} $out/nix-support/add-flags.sh
917 substituteAll ${./add-hardening.sh} $out/nix-support/add-hardening.sh
918 substituteAll ${../wrapper-common/utils.bash} $out/nix-support/utils.bash
919 substituteAll ${../wrapper-common/darwin-sdk-setup.bash} $out/nix-support/darwin-sdk-setup.bash
920 ''
921
922 + optionalString cc.langAda or false ''
923 substituteAll ${./add-gnat-extra-flags.sh} $out/nix-support/add-gnat-extra-flags.sh
924 ''
925
926 ##
927 ## General Clang support
928 ## Needs to go after ^ because the for loop eats \n and makes this file an invalid script
929 ##
930 + optionalString isClang (
931 let
932 hasUnsupportedGnuSuffix = hasPrefix "gnuabielfv" targetPlatform.parsed.abi.name;
933 clangCompatibleConfig =
934 if hasUnsupportedGnuSuffix then
935 removeSuffix (removePrefix "gnu" targetPlatform.parsed.abi.name) targetPlatform.config
936 else
937 targetPlatform.config;
938 explicitAbiValue = if hasUnsupportedGnuSuffix then targetPlatform.parsed.abi.abi else "";
939 in
940 ''
941 # Escape twice: once for this script, once for the one it gets substituted into.
942 export machineFlags=${escapeShellArg (escapeShellArgs machineFlags)}
943 export defaultTarget=${clangCompatibleConfig}
944 export explicitAbiValue=${explicitAbiValue}
945 substituteAll ${./add-clang-cc-cflags-before.sh} $out/nix-support/add-local-cc-cflags-before.sh
946 ''
947 )
948
949 ##
950 ## Extra custom steps
951 ##
952 + extraBuildCommands
953 + concatStringsSep "; " (
954 mapAttrsToList (name: value: "echo ${toString value} >> $out/nix-support/${name}") nixSupport
955 );
956
957 env = {
958 inherit isClang;
959
960 # for substitution in utils.bash
961 # TODO(@sternenseemann): invent something cleaner than passing in "" in case of absence
962 expandResponseParams = lib.optionalString (expand-response-params != "") (
963 lib.getExe expand-response-params
964 );
965 # TODO(@sternenseemann): rename env var via stdenv rebuild
966 shell = getBin runtimeShell + runtimeShell.shellPath or "";
967 gnugrep_bin = optionalString (!nativeTools) gnugrep;
968 rm = if nativeTools then "rm" else lib.getExe' coreutils "rm";
969 mktemp = if nativeTools then "mktemp" else lib.getExe' coreutils "mktemp";
970 # stdenv.cc.cc should not be null and we have nothing better for now.
971 # if the native impure bootstrap is gotten rid of this can become `inherit cc;` again.
972 cc = optionalString (!nativeTools) cc;
973 wrapperName = "CC_WRAPPER";
974 inherit suffixSalt coreutils_bin bintools;
975 inherit libc_bin libc_dev libc_lib;
976 inherit darwinPlatformForCC;
977 default_hardening_flags_str = builtins.toString defaultHardeningFlags;
978 inherit useMacroPrefixMap;
979 }
980 // lib.mapAttrs (_: lib.optionalString targetPlatform.isDarwin) {
981 # These will become empty strings when not targeting Darwin.
982 inherit (targetPlatform) darwinMinVersion darwinMinVersionVariable;
983 }
984 // lib.optionalAttrs (stdenvNoCC.targetPlatform.isDarwin && apple-sdk != null) {
985 # Wrapped compilers should do something useful even when no SDK is provided at `DEVELOPER_DIR`.
986 fallback_sdk = apple-sdk.__spliced.buildTarget or apple-sdk;
987 };
988
989 meta =
990 let
991 cc_ = optionalAttrs (cc != null) cc;
992 in
993 (optionalAttrs (cc_ ? meta) (removeAttrs cc.meta [ "priority" ]))
994 // {
995 description = attrByPath [ "meta" "description" ] "System C compiler" cc_ + " (wrapper script)";
996 priority = 10;
997 mainProgram = if name != "" then name else "${targetPrefix}${ccName}";
998 };
999}