Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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