about summary refs log tree commit diff
path: root/pkgs/test/cc-wrapper/default.nix
blob: 6a0b11a6cc9742864a1fab897c91d97e9bf2890e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
{ lib, stdenv, glibc, buildPackages }:

let
  # Sanitizers are not supported on Darwin.
  # Sanitizer headers aren't available in older libc++ stdenvs due to a bug
  sanitizersWorking = (stdenv.buildPlatform == stdenv.hostPlatform) && !stdenv.isDarwin && !stdenv.hostPlatform.isMusl && (
    (stdenv.cc.isClang && lib.versionAtLeast (lib.getVersion stdenv.cc.name) "5.0.0")
    || (stdenv.cc.isGNU && stdenv.isLinux)
  );
  staticLibc = lib.optionalString (stdenv.hostPlatform.libc == "glibc") "-L ${glibc.static}/lib";
  emulator = stdenv.hostPlatform.emulator buildPackages;
  isCxx = stdenv.cc.libcxx != null;
  libcxxStdenvSuffix = lib.optionalString isCxx "-libcxx";
in stdenv.mkDerivation {
  pname = "cc-wrapper-test-${stdenv.cc.cc.pname}${libcxxStdenvSuffix}";
  version = stdenv.cc.version;

  buildCommand = ''
    echo "Testing: ${stdenv.cc.name}" >&2
    echo "With libc: ${stdenv.cc.libc.name}" >&2
    set -o pipefail

    NIX_DEBUG=1 $CC -v
    NIX_DEBUG=1 $CXX -v

    echo "checking whether compiler builds valid C binaries... " >&2
    $CC -o cc-check ${./cc-main.c}
    ${emulator} ./cc-check

    echo "checking whether compiler builds valid C++ binaries... " >&2
    $CXX -o cxx-check ${./cxx-main.cc}
    ${emulator} ./cxx-check

    # test for https://github.com/NixOS/nixpkgs/issues/214524#issuecomment-1431745905
    # .../include/cxxabi.h:20:10: fatal error: '__cxxabi_config.h' file not found
    # in libcxxStdenv
    echo "checking whether cxxabi.h can be included... " >&2
    $CXX -o include-cxxabi ${./include-cxxabi.cc}
    ${emulator} ./include-cxxabi

    # cxx doesn't have libatomic.so
    ${lib.optionalString (!isCxx) ''
      # https://github.com/NixOS/nixpkgs/issues/91285
      echo "checking whether libatomic.so can be linked... " >&2
      $CXX -shared -o atomics.so ${./atomics.cc} -latomic ${lib.optionalString (stdenv.cc.isClang && lib.versionOlder stdenv.cc.version "6.0.0" ) "-std=c++17"}
      $READELF -d ./atomics.so | grep libatomic.so && echo "ok" >&2 || echo "failed" >&2
    ''}

    # Test that linking libc++ works, and statically.
    ${lib.optionalString isCxx ''
      echo "checking whether can link with libc++... " >&2
      NIX_DEBUG=1 $CXX ${./cxx-main.cc} -c -o cxx-main.o
      NIX_DEBUG=1 $CC cxx-main.o -lc++ -o cxx-main
      NIX_DEBUG=1 $CC cxx-main.o ${lib.getLib stdenv.cc.libcxx}/lib/libc++.a -o cxx-main-static
      ${emulator} ./cxx-main
      ${emulator} ./cxx-main-static
      rm cxx-main{,-static,.o}
    ''}

    ${lib.optionalString (stdenv.isDarwin && stdenv.cc.isClang) ''
      echo "checking whether compiler can build with CoreFoundation.framework... " >&2
      mkdir -p foo/lib
      $CC -framework CoreFoundation -o core-foundation-check ${./core-foundation-main.c}
      ${emulator} ./core-foundation-check
    ''}


    ${lib.optionalString (!stdenv.isDarwin) ''
      echo "checking whether compiler builds valid static C binaries... " >&2
      $CC ${staticLibc} -static -o cc-static ${./cc-main.c}
      ${emulator} ./cc-static
      ${lib.optionalString (stdenv.cc.isGNU && lib.versionAtLeast (lib.getVersion stdenv.cc.name) "8.0.0") ''
        echo "checking whether compiler builds valid static pie C binaries... " >&2
        $CC ${staticLibc} -static-pie -o cc-static-pie ${./cc-main.c}
        ${emulator} ./cc-static-pie
      ''}
    ''}

    ${# See: https://github.com/llvm/llvm-project/commit/ed1d07282cc9d8e4c25d585e03e5c8a1b6f63a74
      # `gcc` does not support this so we gate the test on `clang`
      lib.optionalString stdenv.cc.isClang ''
        echo "checking whether cc-wrapper accepts -- followed by positional (file) args..." >&2
        mkdir -p positional

        # Make sure `--` is not parsed as a "non flag arg"; we should get an
        # input file error here and *not* a linker error.
        { ! $CC --; } |& grep -q "no input files"

        # And that positional file args _must_ be files (this is just testing
        # that we remembered to put the `--` back in the args to the compiler):
        { ! $CC -c -- -o foo ${./foo.c}; } \
          |& grep -q "no such file or directory: '-o'"

        # Now check that we accept single and multiple positional file args:
        $CC -c -DVALUE=42 -o positional/foo.o -- ${./foo.c}
        $CC -o positional/main -- positional/foo.o ${./ldflags-main.c}
        ${emulator} ./positional/main
    ''}

    echo "checking whether compiler uses NIX_CFLAGS_COMPILE... " >&2
    mkdir -p foo/include
    cp ${./foo.c} foo/include/foo.h
    NIX_CFLAGS_COMPILE="-Ifoo/include -DVALUE=42" $CC -o cflags-check ${./cflags-main.c}
    ${emulator} ./cflags-check

    echo "checking whether compiler uses NIX_LDFLAGS... " >&2
    mkdir -p foo/lib
    $CC -shared \
      ${lib.optionalString stdenv.isDarwin "-Wl,-install_name,@rpath/libfoo.dylib"} \
      -DVALUE=42 \
      -o foo/lib/libfoo${stdenv.hostPlatform.extensions.sharedLibrary} \
      ${./foo.c}

    NIX_LDFLAGS="-L$NIX_BUILD_TOP/foo/lib -rpath $NIX_BUILD_TOP/foo/lib" $CC -lfoo -o ldflags-check ${./ldflags-main.c}
    ${emulator} ./ldflags-check

    echo "Check whether -nostdinc and -nostdinc++ is handled correctly" >&2
    mkdir -p std-include
    cp ${./stdio.h} std-include/stdio.h
    NIX_DEBUG=1 $CC -I std-include -nostdinc -o nostdinc-main ${./nostdinc-main.c}
    ${emulator} ./nostdinc-main
    $CXX -I std-include -nostdinc++ -o nostdinc-main++ ${./nostdinc-main.c}
    ${emulator} ./nostdinc-main++

    ${lib.optionalString sanitizersWorking ''
      echo "checking whether sanitizers are fully functional... ">&2
      $CC -o sanitizers -fsanitize=address,undefined ${./sanitizers.c}
      ASAN_OPTIONS=use_sigaltstack=0 ${emulator} ./sanitizers
    ''}

    touch $out
  '';

  meta.platforms = lib.platforms.all;
}