vuls: init at 0.27.0 (#348530)
[NixPkgs.git] / pkgs / build-support / cc-wrapper / default.nix
blob01f91b408e382f36692ff8f668f699b1ebe5cd07
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".
8 { name ? ""
9 , lib
10 , stdenvNoCC
11 , runtimeShell
12 , cc ? null, libc ? null, bintools, coreutils ? null
13 , zlib ? null
14 , nativeTools, noLibc ? false, nativeLibc, nativePrefix ? ""
15 , propagateDoc ? cc != null && cc ? man
16 , extraTools ? [], extraPackages ? [], extraBuildCommands ? ""
17 , nixSupport ? {}
18 , isGNU ? false, isClang ? cc.isClang or false, isZig ? cc.isZig or false
19 , isArocc ? cc.isArocc or false, isCcache ? cc.isCcache or false
20 , gnugrep ? null
21 , expand-response-params
22 , libcxx ? null
24 # Whether or not to add `-B` and `-L` to `nix-support/cc-{c,ld}flags`
25 , useCcForLibs ?
27   # Always add these flags for Clang, because in order to compile (most
28   # software) it needs libraries that are shipped and compiled with gcc.
29   if isClang then true
31   # Never add these flags for a build!=host cross-compiler or a host!=target
32   # ("cross-built-native") compiler; currently nixpkgs has a special build
33   # path for these (`crossStageStatic`).  Hopefully at some point that build
34   # path will be merged with this one and this conditional will be removed.
35   else if (with stdenvNoCC; buildPlatform != hostPlatform || hostPlatform != targetPlatform) then false
37   # Never add these flags when wrapping the bootstrapFiles' compiler; it has a
38   # /usr/-like layout with everything smashed into a single outpath, so it has
39   # no trouble finding its own libraries.
40   else if (cc.passthru.isFromBootstrapFiles or false) then false
42   # Add these flags when wrapping `xgcc` (the first compiler that nixpkgs builds)
43   else if (cc.passthru.isXgcc or false) then true
45   # Add these flags when wrapping `stdenv.cc`
46   else if (cc.stdenv.cc.cc.passthru.isXgcc or false) then true
48   # Do not add these flags in any other situation.  This is `false` mainly to
49   # prevent these flags from being added when wrapping *old* versions of gcc
50   # (e.g. `gcc6Stdenv`), since they will cause the old gcc to get `-B` and
51   # `-L` flags pointing at the new gcc's libstdc++ headers.  Example failure:
52   # https://hydra.nixos.org/build/213125495
53   else false
55 # the derivation at which the `-B` and `-L` flags added by `useCcForLibs` will point
56 , gccForLibs ? if useCcForLibs then cc else null
57 , fortify-headers ? null
58 , includeFortifyHeaders ? null
61 assert nativeTools -> !propagateDoc && nativePrefix != "";
62 assert !nativeTools -> cc != null && coreutils != null && gnugrep != null;
63 assert !(nativeLibc && noLibc);
64 assert (noLibc || nativeLibc) == (libc == null);
66 let
67   inherit (lib)
68     attrByPath
69     concatMapStrings
70     concatStringsSep
71     escapeShellArg
72     escapeShellArgs
73     getBin
74     getDev
75     getLib
76     getName
77     getVersion
78     mapAttrsToList
79     optional
80     optionalAttrs
81     optionals
82     optionalString
83     removePrefix
84     replaceStrings
85     toList
86     versionAtLeast
87     ;
89   inherit (stdenvNoCC) hostPlatform targetPlatform;
91   includeFortifyHeaders' = if includeFortifyHeaders != null
92     then includeFortifyHeaders
93     else (targetPlatform.libc == "musl" && isGNU);
95   # Prefix for binaries. Customarily ends with a dash separator.
96   #
97   # TODO(@Ericson2314) Make unconditional, or optional but always true by default.
98   targetPrefix = optionalString (targetPlatform != hostPlatform) (targetPlatform.config + "-");
100   ccVersion = getVersion cc;
101   ccName = removePrefix targetPrefix (getName cc);
103   libc_bin = optionalString (libc != null) (getBin libc);
104   libc_dev = optionalString (libc != null) (getDev libc);
105   libc_lib = optionalString (libc != null) (getLib libc);
106   cc_solib = getLib cc
107     + optionalString (targetPlatform != hostPlatform) "/${targetPlatform.config}";
109   # The wrapper scripts use 'cat' and 'grep', so we may need coreutils.
110   coreutils_bin = optionalString (!nativeTools) (getBin coreutils);
112   # The "suffix salt" is a arbitrary string added in the end of env vars
113   # defined by cc-wrapper's hooks so that multiple cc-wrappers can be used
114   # without interfering. For the moment, it is defined as the target triple,
115   # adjusted to be a valid bash identifier. This should be considered an
116   # unstable implementation detail, however.
117   suffixSalt = replaceStrings ["-" "."] ["_" "_"] targetPlatform.config;
119   useGccForLibs = useCcForLibs
120     && libcxx == null
121     && !targetPlatform.isDarwin
122     && !(targetPlatform.useLLVM or false)
123     && !(targetPlatform.useAndroidPrebuilt or false)
124     && !(targetPlatform.isiOS or false)
125     && gccForLibs != null;
126   gccForLibs_solib = getLib gccForLibs
127     + optionalString (targetPlatform != hostPlatform) "/${targetPlatform.config}";
129   # Analogously to cc_solib and gccForLibs_solib
130   libcxx_solib = "${getLib libcxx}/lib";
132   # The following two functions, `isGccArchSupported` and
133   # `isGccTuneSupported`, only handle those situations where a flag
134   # (`-march` or `-mtune`) is accepted by one compiler but rejected
135   # by another, and both compilers are relevant to nixpkgs.  We are
136   # not trying to maintain a complete list of all flags accepted by
137   # all versions of all compilers ever in nixpkgs.
138   #
139   # The two main cases of interest are:
140   #
141   # - One compiler is gcc and the other is clang
142   # - One compiler is pkgs.gcc and the other is bootstrap-files.gcc
143   #   -- older compilers (for example bootstrap's GCC 5) fail with
144   #   -march=too-modern-cpu
146   isGccArchSupported = arch:
147     if targetPlatform.isPower then false else # powerpc does not allow -march=
148     if isGNU then
149       { # Generic
150         x86-64-v2 = versionAtLeast ccVersion "11.0";
151         x86-64-v3 = versionAtLeast ccVersion "11.0";
152         x86-64-v4 = versionAtLeast ccVersion "11.0";
154         # Intel
155         skylake        = true;
156         skylake-avx512 = true;
157         cannonlake     = versionAtLeast ccVersion "8.0";
158         icelake-client = versionAtLeast ccVersion "8.0";
159         icelake-server = versionAtLeast ccVersion "8.0";
160         cascadelake    = versionAtLeast ccVersion "9.0";
161         cooperlake     = versionAtLeast ccVersion "10.0";
162         tigerlake      = versionAtLeast ccVersion "10.0";
163         knm            = versionAtLeast ccVersion "8.0";
164         alderlake      = versionAtLeast ccVersion "12.0";
165         sapphirerapids = versionAtLeast ccVersion "11.0";
166         emeraldrapids  = versionAtLeast ccVersion "13.0";
168         # AMD
169         znver1         = true;
170         znver2         = versionAtLeast ccVersion "9.0";
171         znver3         = versionAtLeast ccVersion "11.0";
172         znver4         = versionAtLeast ccVersion "13.0";
173       }.${arch} or true
174     else if isClang then
175       { #Generic
176         x86-64-v2 = versionAtLeast ccVersion "12.0";
177         x86-64-v3 = versionAtLeast ccVersion "12.0";
178         x86-64-v4 = versionAtLeast ccVersion "12.0";
180         # Intel
181         cannonlake     = versionAtLeast ccVersion "5.0";
182         icelake-client = versionAtLeast ccVersion "7.0";
183         icelake-server = versionAtLeast ccVersion "7.0";
184         knm            = versionAtLeast ccVersion "7.0";
185         alderlake      = versionAtLeast ccVersion "16.0";
186         sapphirerapids = versionAtLeast ccVersion "12.0";
187         emeraldrapids  = versionAtLeast ccVersion "16.0";
189         # AMD
190         znver1         = versionAtLeast ccVersion "4.0";
191         znver2         = versionAtLeast ccVersion "9.0";
192         znver3         = versionAtLeast ccVersion "12.0";
193         znver4         = versionAtLeast ccVersion "16.0";
194       }.${arch} or true
195     else
196       false;
198   isGccTuneSupported = tune:
199     # for x86 -mtune= takes the same values as -march, plus two more:
200     if targetPlatform.isx86 then
201       {
202         generic = true;
203         intel = true;
204       }.${tune} or (isGccArchSupported tune)
205     # on arm64, the -mtune= values are specific processors
206     else if targetPlatform.isAarch64 then
207       (if isGNU then
208         {
209           cortex-a53              = true;
210           cortex-a72              = true;
211           "cortex-a72.cortex-a53" = true;
212         }.${tune} or false
213        else if isClang then
214          {
215            cortex-a53             = versionAtLeast ccVersion "3.9"; # llvm dfc5d1
216          }.${tune} or false
217        else false)
218     else if targetPlatform.isPower then
219       # powerpc does not support -march
220       true
221     else if targetPlatform.isMips then
222       # for mips -mtune= takes the same values as -march
223       isGccArchSupported tune
224     else
225       false;
227   # Clang does not support as many `-mtune=` values as gcc does;
228   # this function will return the best possible approximation of the
229   # provided `-mtune=` value, or `null` if none exists.
230   #
231   # Note: this function can make use of ccVersion; for example, `if
232   # versionOlder ccVersion "12" then ...`
233   findBestTuneApproximation = tune:
234     let guess = if isClang
235                 then {
236                   # clang does not tune for big.LITTLE chips
237                   "cortex-a72.cortex-a53" = "cortex-a72";
238                 }.${tune} or tune
239                 else tune;
240     in if isGccTuneSupported guess
241        then guess
242        else null;
244   thumb = if targetPlatform.gcc.thumb then "thumb" else "arm";
245   tune = if targetPlatform ? gcc.tune
246          then findBestTuneApproximation targetPlatform.gcc.tune
247          else null;
249   # Machine flags. These are necessary to support
251   # TODO: We should make a way to support miscellaneous machine
252   # flags and other gcc flags as well.
254   machineFlags =
255     # Always add -march based on cpu in triple. Sometimes there is a
256     # discrepency (x86_64 vs. x86-64), so we provide an "arch" arg in
257     # that case.
258     optional (targetPlatform ? gcc.arch && !(targetPlatform.isDarwin && targetPlatform.isAarch64) && isGccArchSupported targetPlatform.gcc.arch) "-march=${targetPlatform.gcc.arch}" ++
259     # TODO: aarch64-darwin has mcpu incompatible with gcc
260     optional (targetPlatform ? gcc.cpu && !(targetPlatform.isDarwin && targetPlatform.isAarch64)) "-mcpu=${targetPlatform.gcc.cpu}" ++
261     # -mfloat-abi only matters on arm32 but we set it here
262     # unconditionally just in case. If the abi specifically sets hard
263     # vs. soft floats we use it here.
264     optional (targetPlatform ? gcc.float-abi) "-mfloat-abi=${targetPlatform.gcc.float-abi}" ++
265     optional (targetPlatform ? gcc.fpu) "-mfpu=${targetPlatform.gcc.fpu}" ++
266     optional (targetPlatform ? gcc.mode) "-mmode=${targetPlatform.gcc.mode}" ++
267     optional (targetPlatform ? gcc.thumb) "-m${thumb}" ++
268     optional (tune != null) "-mtune=${tune}";
270   defaultHardeningFlags = bintools.defaultHardeningFlags or [];
272   # if cc.hardeningUnsupportedFlagsByTargetPlatform exists, this is
273   # called with the targetPlatform as an argument and
274   # cc.hardeningUnsupportedFlags is completely ignored - the function
275   # is responsible for including the constant hardeningUnsupportedFlags
276   # list however it sees fit.
277   ccHardeningUnsupportedFlags = if cc ? hardeningUnsupportedFlagsByTargetPlatform
278     then cc.hardeningUnsupportedFlagsByTargetPlatform targetPlatform
279     else (cc.hardeningUnsupportedFlags or []);
281   darwinPlatformForCC = optionalString targetPlatform.isDarwin (
282     if (targetPlatform.darwinPlatform == "macos" && isGNU) then "macosx"
283     else targetPlatform.darwinPlatform
284   );
286   darwinMinVersion = optionalString targetPlatform.isDarwin (
287     targetPlatform.darwinMinVersion
288   );
290   darwinMinVersionVariable = optionalString targetPlatform.isDarwin
291     targetPlatform.darwinMinVersionVariable;
294 assert includeFortifyHeaders' -> fortify-headers != null;
296 # Ensure bintools matches
297 assert libc_bin == bintools.libc_bin;
298 assert libc_dev == bintools.libc_dev;
299 assert libc_lib == bintools.libc_lib;
300 assert nativeTools == bintools.nativeTools;
301 assert nativeLibc == bintools.nativeLibc;
302 assert nativePrefix == bintools.nativePrefix;
304 stdenvNoCC.mkDerivation {
305   pname = targetPrefix
306     + (if name != "" then name else "${ccName}-wrapper");
307   version = optionalString (cc != null) ccVersion;
309   preferLocalBuild = true;
311   outputs = [ "out" ] ++ optionals propagateDoc [ "man" "info" ];
313   # Cannot be in "passthru" due to "substituteAll"
314   inherit isArocc;
316   passthru = {
317     inherit targetPrefix suffixSalt;
318     # "cc" is the generic name for a C compiler, but there is no one for package
319     # providing the linker and related tools. The two we use now are GNU
320     # Binutils, and Apple's "cctools"; "bintools" as an attempt to find an
321     # unused middle-ground name that evokes both.
322     inherit bintools;
323     inherit cc libc libcxx nativeTools nativeLibc nativePrefix isGNU isClang isZig;
325     emacsBufferSetup = pkgs: ''
326       ; We should handle propagation here too
327       (mapc
328         (lambda (arg)
329           (when (file-directory-p (concat arg "/include"))
330             (setenv "NIX_CFLAGS_COMPILE_${suffixSalt}" (concat (getenv "NIX_CFLAGS_COMPILE_${suffixSalt}") " -isystem " arg "/include"))))
331         '(${concatStringsSep " " (map (pkg: "\"${pkg}\"") pkgs)}))
332     '';
334     # Expose expand-response-params we are /actually/ using. In stdenv
335     # bootstrapping, expand-response-params usually comes from an earlier stage,
336     # so it is important to expose this for reference checking.
337     inherit expand-response-params;
339     inherit nixSupport;
341     inherit defaultHardeningFlags;
342   };
344   dontBuild = true;
345   dontConfigure = true;
346   enableParallelBuilding = true;
348   unpackPhase = ''
349     src=$PWD
350   '';
352   wrapper = ./cc-wrapper.sh;
354   installPhase =
355     ''
356       mkdir -p $out/bin $out/nix-support
358       wrap() {
359         local dst="$1"
360         local wrapper="$2"
361         export prog="$3"
362         export use_response_file_by_default=${if isClang && !isCcache then "1" else "0"}
363         substituteAll "$wrapper" "$out/bin/$dst"
364         chmod +x "$out/bin/$dst"
365       }
366     ''
368     + (if nativeTools then ''
369       echo ${if targetPlatform.isDarwin then cc else nativePrefix} > $out/nix-support/orig-cc
371       ccPath="${if targetPlatform.isDarwin then cc else nativePrefix}/bin"
372     '' else ''
373       echo $cc > $out/nix-support/orig-cc
375       ccPath="${cc}/bin"
376     '')
378     # Create symlinks to everything in the bintools wrapper.
379     + ''
380       for bbin in $bintools/bin/*; do
381         mkdir -p "$out/bin"
382         ln -s "$bbin" "$out/bin/$(basename $bbin)"
383       done
384     ''
386     # We export environment variables pointing to the wrapped nonstandard
387     # cmds, lest some lousy configure script use those to guess compiler
388     # version.
389     + ''
390       export named_cc=${targetPrefix}cc
391       export named_cxx=${targetPrefix}c++
393       if [ -e $ccPath/${targetPrefix}gcc ]; then
394         wrap ${targetPrefix}gcc $wrapper $ccPath/${targetPrefix}gcc
395         ln -s ${targetPrefix}gcc $out/bin/${targetPrefix}cc
396         export named_cc=${targetPrefix}gcc
397         export named_cxx=${targetPrefix}g++
398       elif [ -e $ccPath/clang ]; then
399         wrap ${targetPrefix}clang $wrapper $ccPath/clang
400         ln -s ${targetPrefix}clang $out/bin/${targetPrefix}cc
401         export named_cc=${targetPrefix}clang
402         export named_cxx=${targetPrefix}clang++
403       elif [ -e $ccPath/arocc ]; then
404         wrap ${targetPrefix}arocc $wrapper $ccPath/arocc
405         ln -s ${targetPrefix}arocc $out/bin/${targetPrefix}cc
406         export named_cc=${targetPrefix}arocc
407       fi
409       if [ -e $ccPath/${targetPrefix}g++ ]; then
410         wrap ${targetPrefix}g++ $wrapper $ccPath/${targetPrefix}g++
411         ln -s ${targetPrefix}g++ $out/bin/${targetPrefix}c++
412       elif [ -e $ccPath/clang++ ]; then
413         wrap ${targetPrefix}clang++ $wrapper $ccPath/clang++
414         ln -s ${targetPrefix}clang++ $out/bin/${targetPrefix}c++
415       fi
417       if [ -e $ccPath/${targetPrefix}cpp ]; then
418         wrap ${targetPrefix}cpp $wrapper $ccPath/${targetPrefix}cpp
419       elif [ -e $ccPath/cpp ]; then
420         wrap ${targetPrefix}cpp $wrapper $ccPath/cpp
421       fi
422     ''
424     # No need to wrap gnat, gnatkr, gnatname or gnatprep; we can just symlink them in
425     + optionalString cc.langAda or false ''
426       for cmd in gnatbind gnatchop gnatclean gnatlink gnatls gnatmake; do
427         wrap ${targetPrefix}$cmd ${./gnat-wrapper.sh} $ccPath/${targetPrefix}$cmd
428       done
430       for cmd in gnat gnatkr gnatname gnatprep; do
431         ln -s $ccPath/${targetPrefix}$cmd $out/bin/${targetPrefix}$cmd
432       done
434       # this symlink points to the unwrapped gnat's output "out". It is used by
435       # our custom gprconfig compiler description to find GNAT's ada runtime. See
436       # ../../development/ada-modules/gprbuild/{boot.nix, nixpkgs-gnat.xml}
437       ln -sf ${cc} $out/nix-support/gprconfig-gnat-unwrapped
438     ''
440     + optionalString cc.langD or false ''
441       wrap ${targetPrefix}gdc $wrapper $ccPath/${targetPrefix}gdc
442     ''
444     + optionalString cc.langFortran or false ''
445       wrap ${targetPrefix}gfortran $wrapper $ccPath/${targetPrefix}gfortran
446       ln -sv ${targetPrefix}gfortran $out/bin/${targetPrefix}g77
447       ln -sv ${targetPrefix}gfortran $out/bin/${targetPrefix}f77
448       export named_fc=${targetPrefix}gfortran
449     ''
451     + optionalString cc.langJava or false ''
452       wrap ${targetPrefix}gcj $wrapper $ccPath/${targetPrefix}gcj
453     ''
455     + optionalString cc.langGo or false ''
456       wrap ${targetPrefix}gccgo $wrapper $ccPath/${targetPrefix}gccgo
457       wrap ${targetPrefix}go ${./go-wrapper.sh} $ccPath/${targetPrefix}go
458     '';
460   strictDeps = true;
461   propagatedBuildInputs = [ bintools ] ++ extraTools ++ optionals cc.langD or cc.langJava or false [ zlib ];
462   depsTargetTargetPropagated = optional (libcxx != null) libcxx ++ extraPackages;
464   setupHooks = [
465     ../setup-hooks/role.bash
466   ] ++ optional (cc.langC or true) ./setup-hook.sh
467     ++ optional (cc.langFortran or false) ./fortran-hook.sh
468     ++ optional (targetPlatform.isWindows) (stdenvNoCC.mkDerivation {
469       name = "win-dll-hook.sh";
470       dontUnpack = true;
471       installPhase = ''
472         echo addToSearchPath "LINK_DLL_FOLDERS" "${cc_solib}/lib" > $out
473         echo addToSearchPath "LINK_DLL_FOLDERS" "${cc_solib}/lib64" >> $out
474         echo addToSearchPath "LINK_DLL_FOLDERS" "${cc_solib}/lib32" >> $out
475       '';
476     });
478   postFixup =
479     # Ensure flags files exists, as some other programs cat them. (That these
480     # are considered an exposed interface is a bit dubious, but fine for now.)
481     ''
482       touch "$out/nix-support/cc-cflags"
483       touch "$out/nix-support/cc-ldflags"
484     ''
486     # Backwards compatibility for packages expecting this file, e.g. with
487     # `$NIX_CC/nix-support/dynamic-linker`.
488     #
489     # TODO(@Ericson2314): Remove this after stable release and force
490     # everyone to refer to bintools-wrapper directly.
491     + optionalString (!isArocc) ''
492       if [[ -f "$bintools/nix-support/dynamic-linker" ]]; then
493         ln -s "$bintools/nix-support/dynamic-linker" "$out/nix-support"
494       fi
495       if [[ -f "$bintools/nix-support/dynamic-linker-m32" ]]; then
496         ln -s "$bintools/nix-support/dynamic-linker-m32" "$out/nix-support"
497       fi
498     ''
500     ##
501     ## GCC libs for non-GCC support
502     ##
503     + optionalString (useGccForLibs && isClang) ''
505       echo "-B${gccForLibs}/lib/gcc/${targetPlatform.config}/${gccForLibs.version}" >> $out/nix-support/cc-cflags
506     ''
507     + optionalString (useGccForLibs && !isArocc) ''
508       echo "-L${gccForLibs}/lib/gcc/${targetPlatform.config}/${gccForLibs.version}" >> $out/nix-support/cc-ldflags
509       echo "-L${gccForLibs_solib}/lib" >> $out/nix-support/cc-ldflags
510     ''
512     # TODO We would like to connect this to `useGccForLibs`, but we cannot yet
513     # because `libcxxStdenv` on linux still needs this. Maybe someday we'll
514     # always set `useLLVM` on Darwin, and maybe also break down `useLLVM` into
515     # fine-grained use flags (libgcc vs compiler-rt, ld.lld vs legacy, libc++
516     # vs libstdc++, etc.) since Darwin isn't `useLLVM` on all counts. (See
517     # https://clang.llvm.org/docs/Toolchain.html for all the axes one might
518     # break `useLLVM` into.)
519     + optionalString (isClang
520                       && targetPlatform.isLinux
521                       && !(targetPlatform.useAndroidPrebuilt or false)
522                       && !(targetPlatform.useLLVM or false)
523                       && gccForLibs != null) (''
524       echo "--gcc-toolchain=${gccForLibs}" >> $out/nix-support/cc-cflags
526       # Pull in 'cc.out' target to get 'libstdc++fs.a'. It should be in
527       # 'cc.lib'. But it's a gcc package bug.
528       # TODO(trofi): remove once gcc is fixed to move libraries to .lib output.
529       echo "-L${gccForLibs}/${optionalString (targetPlatform != hostPlatform) "/${targetPlatform.config}"}/lib" >> $out/nix-support/cc-ldflags
530     ''
531     # this ensures that when clang passes -lgcc_s to lld (as it does
532     # when building e.g. firefox), lld is able to find libgcc_s.so
533     + optionals (!isArocc) (concatMapStrings (libgcc: ''
534       echo "-L${libgcc}/lib" >> $out/nix-support/cc-ldflags
535     '') (toList (gccForLibs.libgcc or []))))
537     ##
538     ## General libc support
539     ##
541     # The "-B${libc_lib}/lib/" flag is a quick hack to force gcc to link
542     # against the crt1.o from our own glibc, rather than the one in
543     # /usr/lib.  (This is only an issue when using an `impure'
544     # compiler/linker, i.e., one that searches /usr/lib and so on.)
545     #
546     # Unfortunately, setting -B appears to override the default search
547     # path. Thus, the gcc-specific "../includes-fixed" directory is
548     # now longer searched and glibc's <limits.h> header fails to
549     # compile, because it uses "#include_next <limits.h>" to find the
550     # limits.h file in ../includes-fixed. To remedy the problem,
551     # another -idirafter is necessary to add that directory again.
552     + optionalString (libc != null) (''
553       touch "$out/nix-support/libc-cflags"
554       touch "$out/nix-support/libc-ldflags"
555     '' + optionalString (!isArocc) ''
556       echo "-B${libc_lib}${libc.libdir or "/lib/"}" >> $out/nix-support/libc-crt1-cflags
557     '' + optionalString (!(cc.langD or false)) ''
558       echo "-${if isArocc then "I" else "idirafter"} ${libc_dev}${libc.incdir or "/include"}" >> $out/nix-support/libc-cflags
559     '' + optionalString (isGNU && (!(cc.langD or false))) ''
560       for dir in "${cc}"/lib/gcc/*/*/include-fixed; do
561         echo '-idirafter' ''${dir} >> $out/nix-support/libc-cflags
562       done
563     '' + ''
565       echo "${libc_lib}" > $out/nix-support/orig-libc
566       echo "${libc_dev}" > $out/nix-support/orig-libc-dev
567     ''
568     # fortify-headers is a set of wrapper headers that augment libc
569     # and use #include_next to pass through to libc's true
570     # implementations, so must appear before them in search order.
571     # in theory a correctly placed -idirafter could be used, but in
572     # practice the compiler may have been built with a --with-headers
573     # like option that forces the libc headers before all -idirafter,
574     # hence -isystem here.
575     + optionalString includeFortifyHeaders' ''
576       echo "-isystem ${fortify-headers}/include" >> $out/nix-support/libc-cflags
577     '')
579     ##
580     ## General libc++ support
581     ##
583     # We have a libc++ directly, we have one via "smuggled" GCC, or we have one
584     # bundled with the C compiler because it is GCC
585     + optionalString (libcxx != null || (useGccForLibs && gccForLibs.langCC or false) || (isGNU && cc.langCC or false)) ''
586       touch "$out/nix-support/libcxx-cxxflags"
587       touch "$out/nix-support/libcxx-ldflags"
588     ''
589     # Adding -isystem flags should be done only for clang; gcc
590     # already knows how to find its own libstdc++, and adding
591     # additional -isystem flags will confuse gfortran (see
592     # https://github.com/NixOS/nixpkgs/pull/209870#issuecomment-1500550903)
593     + optionalString (libcxx == null && isClang && (useGccForLibs && gccForLibs.langCC or false)) ''
594       for dir in ${gccForLibs}/include/c++/*; do
595         echo "-isystem $dir" >> $out/nix-support/libcxx-cxxflags
596       done
597       for dir in ${gccForLibs}/include/c++/*/${targetPlatform.config}; do
598         echo "-isystem $dir" >> $out/nix-support/libcxx-cxxflags
599       done
600     ''
601     + optionalString (libcxx.isLLVM or false) ''
602       echo "-isystem ${getDev libcxx}/include/c++/v1" >> $out/nix-support/libcxx-cxxflags
603       echo "-stdlib=libc++" >> $out/nix-support/libcxx-ldflags
604     ''
606     ##
607     ## Initial CFLAGS
608     ##
610     # GCC shows ${cc_solib}/lib in `gcc -print-search-dirs', but not
611     # ${cc_solib}/lib64 (even though it does actually search there...)..
612     # This confuses libtool.  So add it to the compiler tool search
613     # path explicitly.
614     + optionalString (!nativeTools && !isArocc) ''
615       if [ -e "${cc_solib}/lib64" -a ! -L "${cc_solib}/lib64" ]; then
616         ccLDFlags+=" -L${cc_solib}/lib64"
617         ccCFlags+=" -B${cc_solib}/lib64"
618       fi
619       ccLDFlags+=" -L${cc_solib}/lib"
620       ccCFlags+=" -B${cc_solib}/lib"
622     '' + optionalString (cc.langAda or false && !isArocc) ''
623       touch "$out/nix-support/gnat-cflags"
624       touch "$out/nix-support/gnat-ldflags"
625       basePath=$(echo $cc/lib/*/*/*)
626       ccCFlags+=" -B$basePath -I$basePath/adainclude"
627       gnatCFlags="-I$basePath/adainclude -I$basePath/adalib"
629       echo "$gnatCFlags" >> $out/nix-support/gnat-cflags
630     '' + ''
631       echo "$ccLDFlags" >> $out/nix-support/cc-ldflags
632       echo "$ccCFlags" >> $out/nix-support/cc-cflags
633     '' + optionalString (targetPlatform.isDarwin && (libcxx != null) && (cc.isClang or false)) ''
634       echo " -L${libcxx_solib}" >> $out/nix-support/cc-ldflags
635     ''
637     ##
638     ## Man page and info support
639     ##
640     + optionalString propagateDoc ''
641       ln -s ${cc.man} $man
642       ln -s ${cc.info} $info
643     '' + optionalString (cc.langD or cc.langJava or false && !isArocc) ''
644       echo "-B${zlib}${zlib.libdir or "/lib/"}" >> $out/nix-support/libc-cflags
645     ''
647     ##
648     ## Hardening support
649     ##
650     + ''
651       export hardening_unsupported_flags="${concatStringsSep " " ccHardeningUnsupportedFlags}"
652     ''
654     # For clang, this is handled in add-clang-cc-cflags-before.sh
655     + lib.optionalString (!isClang && machineFlags != []) ''
656       printf "%s\n" ${lib.escapeShellArgs machineFlags} >> $out/nix-support/cc-cflags-before
657     ''
659     # TODO: categorize these and figure out a better place for them
660     + optionalString targetPlatform.isWindows ''
661       hardening_unsupported_flags+=" pic"
662     '' + optionalString targetPlatform.isMinGW ''
663       hardening_unsupported_flags+=" stackprotector fortify"
664     '' + optionalString targetPlatform.isAvr ''
665       hardening_unsupported_flags+=" stackprotector pic"
666     '' + optionalString (targetPlatform.libc == "newlib" || targetPlatform.libc == "newlib-nano") ''
667       hardening_unsupported_flags+=" stackprotector fortify pie pic"
668     '' + optionalString (targetPlatform.libc == "musl" && targetPlatform.isx86_32) ''
669       hardening_unsupported_flags+=" stackprotector"
670     '' + optionalString targetPlatform.isNetBSD ''
671       hardening_unsupported_flags+=" stackprotector fortify"
672     '' + optionalString cc.langAda or false ''
673       hardening_unsupported_flags+=" format stackprotector strictoverflow"
674     '' + optionalString cc.langD or false ''
675       hardening_unsupported_flags+=" format"
676     '' + optionalString cc.langFortran or false ''
677       hardening_unsupported_flags+=" format"
678     '' + optionalString targetPlatform.isWasm ''
679       hardening_unsupported_flags+=" stackprotector fortify pie pic"
680     '' + optionalString targetPlatform.isMicroBlaze ''
681       hardening_unsupported_flags+=" stackprotector"
682     ''
684     + optionalString (libc != null && targetPlatform.isAvr && !isArocc) ''
685       for isa in avr5 avr3 avr4 avr6 avr25 avr31 avr35 avr51 avrxmega2 avrxmega4 avrxmega5 avrxmega6 avrxmega7 tiny-stack; do
686         echo "-B${getLib libc}/avr/lib/$isa" >> $out/nix-support/libc-crt1-cflags
687       done
688     ''
690     + optionalString targetPlatform.isDarwin ''
691         echo "-arch ${targetPlatform.darwinArch}" >> $out/nix-support/cc-cflags
692     ''
694     + optionalString targetPlatform.isAndroid ''
695       echo "-D__ANDROID_API__=${targetPlatform.androidSdkVersion}" >> $out/nix-support/cc-cflags
696     ''
698     # There are a few tools (to name one libstdcxx5) which do not work
699     # well with multi line flags, so make the flags single line again
700     + ''
701       for flags in "$out/nix-support"/*flags*; do
702         substituteInPlace "$flags" --replace $'\n' ' '
703       done
705       substituteAll ${./add-flags.sh} $out/nix-support/add-flags.sh
706       substituteAll ${./add-hardening.sh} $out/nix-support/add-hardening.sh
707       substituteAll ${../wrapper-common/utils.bash} $out/nix-support/utils.bash
708     ''
710     + optionalString cc.langAda or false ''
711       substituteAll ${./add-gnat-extra-flags.sh} $out/nix-support/add-gnat-extra-flags.sh
712     ''
714     ##
715     ## General Clang support
716     ## Needs to go after ^ because the for loop eats \n and makes this file an invalid script
717     ##
718     + optionalString isClang ''
719       # Escape twice: once for this script, once for the one it gets substituted into.
720       export machineFlags=${escapeShellArg (escapeShellArgs machineFlags)}
721       export defaultTarget=${targetPlatform.config}
722       substituteAll ${./add-clang-cc-cflags-before.sh} $out/nix-support/add-local-cc-cflags-before.sh
723     ''
725     ##
726     ## Extra custom steps
727     ##
728     + extraBuildCommands
729     + concatStringsSep "; "
730       (mapAttrsToList
731         (name: value: "echo ${toString value} >> $out/nix-support/${name}")
732         nixSupport);
735   env = {
736     inherit isClang;
738     # for substitution in utils.bash
739     # TODO(@sternenseemann): invent something cleaner than passing in "" in case of absence
740     expandResponseParams = "${expand-response-params}/bin/expand-response-params";
741     # TODO(@sternenseemann): rename env var via stdenv rebuild
742     shell = getBin runtimeShell + runtimeShell.shellPath or "";
743     gnugrep_bin = optionalString (!nativeTools) gnugrep;
744     # stdenv.cc.cc should not be null and we have nothing better for now.
745     # if the native impure bootstrap is gotten rid of this can become `inherit cc;` again.
746     cc = optionalString (!nativeTools) cc;
747     wrapperName = "CC_WRAPPER";
748     inherit suffixSalt coreutils_bin bintools;
749     inherit libc_bin libc_dev libc_lib;
750     inherit darwinPlatformForCC darwinMinVersion darwinMinVersionVariable;
751     default_hardening_flags_str = builtins.toString defaultHardeningFlags;
752   };
754   meta =
755     let cc_ = optionalAttrs (cc != null) cc; in
756     (optionalAttrs (cc_ ? meta) (removeAttrs cc.meta ["priority"])) //
757     { description = attrByPath ["meta" "description"] "System C compiler" cc_ + " (wrapper script)";
758       priority = 10;
759       mainProgram = if name != "" then name else "${targetPrefix}${ccName}";
760   };