Skip to content

Commit 190b8d0

Browse files
authored
[clang][Darwin] Prefer the toolchain-provided libc++.dylib if there is one (#170303)
When libc++ is bootstrapped with clang, the resulting clang uses the just-built libc++ headers from <install>/bin/../include/c++/v1. However, before this patch, clang would still use the system-provided libc++.dylib (usually in the Apple SDK) because it would fail to add the corresponding linker flag to find the just-built libc++. After this patch, Clang will instead link against the toolchain provided `libc++.dylib` in `<install>/lib` if it exists, which will result in programs being linked against corresponding libc++ headers and dylib. Fixes #77653 rdar://107060541
1 parent a5b02e1 commit 190b8d0

File tree

11 files changed

+144
-8
lines changed

11 files changed

+144
-8
lines changed

clang/lib/Driver/ToolChains/Darwin.cpp

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2846,11 +2846,49 @@ void AppleMachO::AddCXXStdlibLibArgs(const ArgList &Args,
28462846
CXXStdlibType Type = GetCXXStdlibType(Args);
28472847

28482848
switch (Type) {
2849-
case ToolChain::CST_Libcxx:
2850-
CmdArgs.push_back("-lc++");
2849+
case ToolChain::CST_Libcxx: {
2850+
// On Darwin, we prioritize a libc++ located in the toolchain to a libc++
2851+
// located in the sysroot. Unlike the driver for most other platforms, on
2852+
// Darwin we do that by explicitly passing the library path to the linker
2853+
// to avoid having to add the toolchain's `lib/` directory to the linker
2854+
// search path, which would make other libraries findable as well.
2855+
//
2856+
// Prefering the toolchain library over the sysroot library matches the
2857+
// behavior we have for headers, where we prefer headers in the toolchain
2858+
// over headers in the sysroot if there are any. Note that it's important
2859+
// for the header search path behavior to match the link-time search path
2860+
// behavior to ensure that we link the program against a library that
2861+
// matches the headers that were used to compile it.
2862+
//
2863+
// Otherwise, we end up compiling against some set of headers and then
2864+
// linking against a different library (which, confusingly, shares the same
2865+
// name) which may have been configured with different options, be at a
2866+
// different version, etc.
2867+
SmallString<128> InstallLib = llvm::sys::path::parent_path(getDriver().Dir);
2868+
llvm::sys::path::append(InstallLib, "lib"); // <install>/lib
2869+
auto Link = [&](StringRef Library) {
2870+
SmallString<128> Shared(InstallLib);
2871+
llvm::sys::path::append(Shared,
2872+
SmallString<4>("lib") + Library + ".dylib");
2873+
SmallString<128> Static(InstallLib);
2874+
llvm::sys::path::append(Static, SmallString<4>("lib") + Library + ".a");
2875+
SmallString<32> Relative("-l");
2876+
Relative += Library;
2877+
2878+
if (getVFS().exists(Shared)) {
2879+
CmdArgs.push_back(Args.MakeArgString(Shared));
2880+
} else if (getVFS().exists(Static)) {
2881+
CmdArgs.push_back(Args.MakeArgString(Static));
2882+
} else {
2883+
CmdArgs.push_back(Args.MakeArgString(Relative));
2884+
}
2885+
};
2886+
2887+
Link("c++");
28512888
if (Args.hasArg(options::OPT_fexperimental_library))
2852-
CmdArgs.push_back("-lc++experimental");
2889+
Link("c++experimental");
28532890
break;
2891+
}
28542892

28552893
case ToolChain::CST_Libstdcxx:
28562894
// Unfortunately, -lstdc++ doesn't always exist in the standard search path;

clang/test/Driver/Inputs/basic_darwin_toolchain/usr/lib/libc++.dylib

Whitespace-only changes.

clang/test/Driver/Inputs/basic_darwin_toolchain/usr/lib/libc++experimental.a

Whitespace-only changes.

clang/test/Driver/Inputs/basic_darwin_toolchain_static/usr/bin/.keep

Whitespace-only changes.

clang/test/Driver/Inputs/basic_darwin_toolchain_static/usr/include/c++/v1/.keep

Whitespace-only changes.

clang/test/Driver/Inputs/basic_darwin_toolchain_static/usr/lib/libc++.a

Whitespace-only changes.

clang/test/Driver/Inputs/basic_darwin_toolchain_static/usr/lib/libc++experimental.a

Whitespace-only changes.

clang/test/Driver/darwin-header-search-libcxx.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@
9292
// CHECK-LIBCXX-SYSROOT_AND_TOOLCHAIN-1: "-internal-isystem" "[[TOOLCHAIN]]/usr/bin/../include/c++/v1"
9393
// CHECK-LIBCXX-SYSROOT_AND_TOOLCHAIN-1-NOT: "-internal-isystem" "[[SYSROOT]]/usr/include/c++/v1"
9494

95-
// Make sure that using -nostdinc, -nostdinc++ or -nostdlib will drop both the toolchain
95+
// Make sure that using -nostdinc, -nostdinc++ or -nostdlibinc will drop both the toolchain
9696
// C++ include path and the sysroot one.
9797
//
9898
// RUN: %clang -### %s -fsyntax-only 2>&1 \
@@ -116,7 +116,7 @@
116116
// RUN: -isysroot %S/Inputs/basic_darwin_sdk_usr_cxx_v1 \
117117
// RUN: -stdlib=platform \
118118
// RUN: -nostdinc++ \
119-
// RUN: | FileCheck -DSYSROOT=%S/Inputs/basic_darwin_sdk_usr \
119+
// RUN: | FileCheck -DSYSROOT=%S/Inputs/basic_darwin_sdk_usr_cxx_v1 \
120120
// RUN: -DTOOLCHAIN=%S/Inputs/basic_darwin_toolchain \
121121
// RUN: --check-prefix=CHECK-LIBCXX-NOSTDINCXX %s
122122
// CHECK-LIBCXX-NOSTDINCXX: "-cc1"
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// UNSUPPORTED: system-windows
2+
3+
// Tests to check that we link against the toolchain-provided libc++ built library when it is provided.
4+
// This is required to prefer the toolchain's libc++ over the system's libc++, which matches the behavior
5+
// we have for header search paths.
6+
7+
// When libc++.dylib is NOT in the toolchain, we should use -lc++ and fall back to the libc++
8+
// in the sysroot.
9+
//
10+
// (1) Without -fexperimental-library.
11+
// RUN: %clangxx -### %s 2>&1 \
12+
// RUN: --target=x86_64-apple-darwin \
13+
// RUN: -ccc-install-dir %S/Inputs/basic_darwin_toolchain_no_libcxx/usr/bin \
14+
// RUN: | FileCheck -DTOOLCHAIN=%S/Inputs/basic_darwin_toolchain_no_libcxx \
15+
// RUN: --check-prefix=CHECK-1 %s
16+
// CHECK-1: "/usr/bin/ld"
17+
// CHECK-1: "-lc++"
18+
// CHECK-1-NOT: "[[TOOLCHAIN]]/usr/lib"
19+
//
20+
// (2) With -fexperimental-library.
21+
// RUN: %clangxx -### %s 2>&1 \
22+
// RUN: --target=x86_64-apple-darwin \
23+
// RUN: -ccc-install-dir %S/Inputs/basic_darwin_toolchain_no_libcxx/usr/bin \
24+
// RUN: -fexperimental-library \
25+
// RUN: | FileCheck -DTOOLCHAIN=%S/Inputs/basic_darwin_toolchain_no_libcxx \
26+
// RUN: --check-prefix=CHECK-2 %s
27+
// CHECK-2: "/usr/bin/ld"
28+
// CHECK-2: "-lc++" "-lc++experimental"
29+
// CHECK-2-NOT: "[[TOOLCHAIN]]/usr/lib"
30+
31+
// When we have libc++.dylib in the toolchain, it should be used over the one in the sysroot.
32+
// There are a few cases worth testing.
33+
//
34+
// (1) Without -fexperimental-library.
35+
// RUN: %clangxx -### %s 2>&1 \
36+
// RUN: --target=x86_64-apple-darwin \
37+
// RUN: -ccc-install-dir %S/Inputs/basic_darwin_toolchain/usr/bin \
38+
// RUN: | FileCheck -DTOOLCHAIN=%S/Inputs/basic_darwin_toolchain \
39+
// RUN: --check-prefix=CHECK-3 %s
40+
// CHECK-3: "/usr/bin/ld"
41+
// CHECK-3: "[[TOOLCHAIN]]/usr/lib/libc++.dylib"
42+
// CHECK-3-NOT: "-lc++"
43+
//
44+
// (2) With -fexperimental-library.
45+
// RUN: %clangxx -### %s 2>&1 \
46+
// RUN: --target=x86_64-apple-darwin \
47+
// RUN: -ccc-install-dir %S/Inputs/basic_darwin_toolchain/usr/bin \
48+
// RUN: -fexperimental-library \
49+
// RUN: | FileCheck -DTOOLCHAIN=%S/Inputs/basic_darwin_toolchain \
50+
// RUN: --check-prefix=CHECK-4 %s
51+
// CHECK-4: "/usr/bin/ld"
52+
// CHECK-4: "[[TOOLCHAIN]]/usr/lib/libc++.dylib"
53+
// CHECK-4: "[[TOOLCHAIN]]/usr/lib/libc++experimental.a"
54+
// CHECK-4-NOT: "-lc++"
55+
// CHECK-4-NOT: "-lc++experimental"
56+
57+
// When we have libc++.a in the toolchain instead of libc++.dylib, it should be
58+
// used over the one in the sysroot.
59+
//
60+
// (1) Without -fexperimental-library.
61+
// RUN: %clangxx -### %s 2>&1 \
62+
// RUN: --target=x86_64-apple-darwin \
63+
// RUN: -ccc-install-dir %S/Inputs/basic_darwin_toolchain_static/usr/bin \
64+
// RUN: | FileCheck -DTOOLCHAIN=%S/Inputs/basic_darwin_toolchain_static \
65+
// RUN: --check-prefix=CHECK-5 %s
66+
// CHECK-5: "/usr/bin/ld"
67+
// CHECK-5: "[[TOOLCHAIN]]/usr/lib/libc++.a"
68+
// CHECK-5-NOT: "-lc++"
69+
//
70+
// (2) With -fexperimental-library.
71+
// RUN: %clangxx -### %s 2>&1 \
72+
// RUN: --target=x86_64-apple-darwin \
73+
// RUN: -ccc-install-dir %S/Inputs/basic_darwin_toolchain_static/usr/bin \
74+
// RUN: -fexperimental-library \
75+
// RUN: | FileCheck -DTOOLCHAIN=%S/Inputs/basic_darwin_toolchain_static \
76+
// RUN: --check-prefix=CHECK-6 %s
77+
// CHECK-6: "/usr/bin/ld"
78+
// CHECK-6: "[[TOOLCHAIN]]/usr/lib/libc++.a"
79+
// CHECK-6: "[[TOOLCHAIN]]/usr/lib/libc++experimental.a"
80+
// CHECK-6-NOT: "-lc++"
81+
// CHECK-6-NOT: "-lc++experimental"

clang/test/Driver/experimental-library-flag.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
// CHECK: -fexperimental-library
1919

2020
// Depending on the stdlib in use, we should (or not) pass -lc++experimental.
21-
// CHECK-LIBCXX: -lc++experimental
22-
// CHECK-LIBSTDCXX-NOT: -lc++experimental
23-
// CHECK-NOSTDLIB-NOT: -lc++experimental
21+
// Note that we don't check for `-lc++experimental` specifically, since some targets
22+
// like Darwin pass the path to the library explicitly instead of using `-lx`.
23+
// CHECK-LIBCXX: c++experimental
24+
// CHECK-LIBSTDCXX-NOT: c++experimental
25+
// CHECK-NOSTDLIB-NOT: c++experimental

0 commit comments

Comments
 (0)