Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 41 additions & 3 deletions clang/lib/Driver/ToolChains/Darwin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2846,11 +2846,49 @@ void AppleMachO::AddCXXStdlibLibArgs(const ArgList &Args,
CXXStdlibType Type = GetCXXStdlibType(Args);

switch (Type) {
case ToolChain::CST_Libcxx:
CmdArgs.push_back("-lc++");
case ToolChain::CST_Libcxx: {
// On Darwin, we prioritize a libc++ located in the toolchain to a libc++
// located in the sysroot. Unlike the driver for most other platforms, on
// Darwin we do that by explicitly passing the library path to the linker
// to avoid having to add the toolchain's `lib/` directory to the linker
// search path, which would make other libraries findable as well.
//
// Prefering the toolchain library over the sysroot library matches the
// behavior we have for headers, where we prefer headers in the toolchain
// over headers in the sysroot if there are any. Note that it's important
// for the header search path behavior to match the link-time search path
// behavior to ensure that we link the program against a library that
// matches the headers that were used to compile it.
//
// Otherwise, we end up compiling against some set of headers and then
// linking against a different library (which, confusingly, shares the same
// name) which may have been configured with different options, be at a
// different version, etc.
SmallString<128> InstallLib = llvm::sys::path::parent_path(getDriver().Dir);
llvm::sys::path::append(InstallLib, "lib"); // <install>/lib
auto Link = [&](StringRef Library) {
SmallString<128> Shared(InstallLib);
llvm::sys::path::append(Shared,
SmallString<4>("lib") + Library + ".dylib");
SmallString<128> Static(InstallLib);
llvm::sys::path::append(Static, SmallString<4>("lib") + Library + ".a");
SmallString<32> Relative("-l");
Relative += Library;

if (getVFS().exists(Shared)) {
CmdArgs.push_back(Args.MakeArgString(Shared));
} else if (getVFS().exists(Static)) {
CmdArgs.push_back(Args.MakeArgString(Static));
} else {
CmdArgs.push_back(Args.MakeArgString(Relative));
}
};

Link("c++");
if (Args.hasArg(options::OPT_fexperimental_library))
CmdArgs.push_back("-lc++experimental");
Link("c++experimental");
break;
}

case ToolChain::CST_Libstdcxx:
// Unfortunately, -lstdc++ doesn't always exist in the standard search path;
Expand Down
Empty file.
Empty file.
Empty file.
4 changes: 2 additions & 2 deletions clang/test/Driver/darwin-header-search-libcxx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@
// CHECK-LIBCXX-SYSROOT_AND_TOOLCHAIN-1: "-internal-isystem" "[[TOOLCHAIN]]/usr/bin/../include/c++/v1"
// CHECK-LIBCXX-SYSROOT_AND_TOOLCHAIN-1-NOT: "-internal-isystem" "[[SYSROOT]]/usr/include/c++/v1"

// Make sure that using -nostdinc, -nostdinc++ or -nostdlib will drop both the toolchain
// Make sure that using -nostdinc, -nostdinc++ or -nostdlibinc will drop both the toolchain
// C++ include path and the sysroot one.
//
// RUN: %clang -### %s -fsyntax-only 2>&1 \
Expand All @@ -116,7 +116,7 @@
// RUN: -isysroot %S/Inputs/basic_darwin_sdk_usr_cxx_v1 \
// RUN: -stdlib=platform \
// RUN: -nostdinc++ \
// RUN: | FileCheck -DSYSROOT=%S/Inputs/basic_darwin_sdk_usr \
// RUN: | FileCheck -DSYSROOT=%S/Inputs/basic_darwin_sdk_usr_cxx_v1 \
// RUN: -DTOOLCHAIN=%S/Inputs/basic_darwin_toolchain \
// RUN: --check-prefix=CHECK-LIBCXX-NOSTDINCXX %s
// CHECK-LIBCXX-NOSTDINCXX: "-cc1"
Expand Down
81 changes: 81 additions & 0 deletions clang/test/Driver/darwin-link-libcxx.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// UNSUPPORTED: system-windows

// Tests to check that we link against the toolchain-provided libc++ built library when it is provided.
// This is required to prefer the toolchain's libc++ over the system's libc++, which matches the behavior
// we have for header search paths.

// When libc++.dylib is NOT in the toolchain, we should use -lc++ and fall back to the libc++
// in the sysroot.
//
// (1) Without -fexperimental-library.
// RUN: %clangxx -### %s 2>&1 \
// RUN: --target=x86_64-apple-darwin \
// RUN: -ccc-install-dir %S/Inputs/basic_darwin_toolchain_no_libcxx/usr/bin \
// RUN: | FileCheck -DTOOLCHAIN=%S/Inputs/basic_darwin_toolchain_no_libcxx \
// RUN: --check-prefix=CHECK-1 %s
// CHECK-1: "/usr/bin/ld"
// CHECK-1: "-lc++"
// CHECK-1-NOT: "[[TOOLCHAIN]]/usr/lib"
//
// (2) With -fexperimental-library.
// RUN: %clangxx -### %s 2>&1 \
// RUN: --target=x86_64-apple-darwin \
// RUN: -ccc-install-dir %S/Inputs/basic_darwin_toolchain_no_libcxx/usr/bin \
// RUN: -fexperimental-library \
// RUN: | FileCheck -DTOOLCHAIN=%S/Inputs/basic_darwin_toolchain_no_libcxx \
// RUN: --check-prefix=CHECK-2 %s
// CHECK-2: "/usr/bin/ld"
// CHECK-2: "-lc++" "-lc++experimental"
// CHECK-2-NOT: "[[TOOLCHAIN]]/usr/lib"

// When we have libc++.dylib in the toolchain, it should be used over the one in the sysroot.
// There are a few cases worth testing.
//
// (1) Without -fexperimental-library.
// RUN: %clangxx -### %s 2>&1 \
// RUN: --target=x86_64-apple-darwin \
// RUN: -ccc-install-dir %S/Inputs/basic_darwin_toolchain/usr/bin \
// RUN: | FileCheck -DTOOLCHAIN=%S/Inputs/basic_darwin_toolchain \
// RUN: --check-prefix=CHECK-3 %s
// CHECK-3: "/usr/bin/ld"
// CHECK-3: "[[TOOLCHAIN]]/usr/lib/libc++.dylib"
// CHECK-3-NOT: "-lc++"
//
// (2) With -fexperimental-library.
// RUN: %clangxx -### %s 2>&1 \
// RUN: --target=x86_64-apple-darwin \
// RUN: -ccc-install-dir %S/Inputs/basic_darwin_toolchain/usr/bin \
// RUN: -fexperimental-library \
// RUN: | FileCheck -DTOOLCHAIN=%S/Inputs/basic_darwin_toolchain \
// RUN: --check-prefix=CHECK-4 %s
// CHECK-4: "/usr/bin/ld"
// CHECK-4: "[[TOOLCHAIN]]/usr/lib/libc++.dylib"
// CHECK-4: "[[TOOLCHAIN]]/usr/lib/libc++experimental.a"
// CHECK-4-NOT: "-lc++"
// CHECK-4-NOT: "-lc++experimental"

// When we have libc++.a in the toolchain instead of libc++.dylib, it should be
// used over the one in the sysroot.
//
// (1) Without -fexperimental-library.
// RUN: %clangxx -### %s 2>&1 \
// RUN: --target=x86_64-apple-darwin \
// RUN: -ccc-install-dir %S/Inputs/basic_darwin_toolchain_static/usr/bin \
// RUN: | FileCheck -DTOOLCHAIN=%S/Inputs/basic_darwin_toolchain_static \
// RUN: --check-prefix=CHECK-5 %s
// CHECK-5: "/usr/bin/ld"
// CHECK-5: "[[TOOLCHAIN]]/usr/lib/libc++.a"
// CHECK-5-NOT: "-lc++"
//
// (2) With -fexperimental-library.
// RUN: %clangxx -### %s 2>&1 \
// RUN: --target=x86_64-apple-darwin \
// RUN: -ccc-install-dir %S/Inputs/basic_darwin_toolchain_static/usr/bin \
// RUN: -fexperimental-library \
// RUN: | FileCheck -DTOOLCHAIN=%S/Inputs/basic_darwin_toolchain_static \
// RUN: --check-prefix=CHECK-6 %s
// CHECK-6: "/usr/bin/ld"
// CHECK-6: "[[TOOLCHAIN]]/usr/lib/libc++.a"
// CHECK-6: "[[TOOLCHAIN]]/usr/lib/libc++experimental.a"
// CHECK-6-NOT: "-lc++"
// CHECK-6-NOT: "-lc++experimental"
8 changes: 5 additions & 3 deletions clang/test/Driver/experimental-library-flag.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
// CHECK: -fexperimental-library

// Depending on the stdlib in use, we should (or not) pass -lc++experimental.
// CHECK-LIBCXX: -lc++experimental
// CHECK-LIBSTDCXX-NOT: -lc++experimental
// CHECK-NOSTDLIB-NOT: -lc++experimental
// Note that we don't check for `-lc++experimental` specifically, since some targets
// like Darwin pass the path to the library explicitly instead of using `-lx`.
// CHECK-LIBCXX: c++experimental
// CHECK-LIBSTDCXX-NOT: c++experimental
// CHECK-NOSTDLIB-NOT: c++experimental
15 changes: 15 additions & 0 deletions compiler-rt/cmake/config-ix.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -507,8 +507,23 @@ if(APPLE)
set(CMAKE_OSX_DEPLOYMENT_TARGET "")

set(DARWIN_COMMON_CFLAGS -stdlib=libc++)

# This is tricky: We want to link against libc++, however the libc++ for the
# architecture we're currently building may not have been built yet, since
# compiler-rt on Darwin builds for all targets at once while libc++ builds for
# a single target. Hence, we pass -nostdlib++ to disable the default mechanism
# for finding libc++, and we pass -lc++ which will end up finding libc++ in the
# SDK currently in use. That libc++ is the wrong libc++ to use if we're using
# headers from a just-built libc++, but at least it contains all the architectures
# we should be interested in.
#
# Fixing this properly would require removing the impedence mismatch between
# the compiler-rt build on Darwin (which wants to build all architectures at
# once) and the libc++ build, which produces a single architecture per CMake
# invocation.
set(DARWIN_COMMON_LINK_FLAGS
-stdlib=libc++
-nostdlib++
-lc++
-lc++abi)

Expand Down
Loading