Skip to content

Link against the C++ standard library when C++ interop is enabled #30914

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Jul 9, 2020
Merged
Show file tree
Hide file tree
Changes from 2 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
2 changes: 2 additions & 0 deletions include/swift/Driver/ToolChain.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ class ToolChain {

const char *computeFrontendModeForCompile() const;

bool cxxInteropEnabled() const;

void addFrontendInputAndOutputArguments(
llvm::opt::ArgStringList &Arguments,
std::vector<FilelistInfo> &FilelistInfos) const;
Expand Down
5 changes: 5 additions & 0 deletions lib/Driver/DarwinToolChains.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -712,6 +712,11 @@ toolchains::Darwin::constructInvocation(const DynamicLinkJobAction &job,
Arguments.push_back("-arch");
Arguments.push_back(context.Args.MakeArgString(getTriple().getArchName()));

if (context.cxxInteropEnabled()) {
// On Darwin, we only support libc++.
Arguments.push_back("-lc++");
}

addArgsToLinkStdlib(Arguments, job, context);

addProfileGenerationArgs(Arguments, context);
Expand Down
13 changes: 13 additions & 0 deletions lib/Driver/ToolChain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,19 @@ ToolChain::JobContext::getTemporaryFilePath(const llvm::Twine &name,
return C.getArgs().MakeArgString(buffer.str());
}

bool
ToolChain::JobContext::cxxInteropEnabled() const {
// TODO: Eventually, we'll want to have a driver flag to control C++ interop,
// but for the time being, we just query the frontend flag.
for (const Arg *A : Args.filtered(options::OPT_Xfrontend)) {
if (A->containsValue("-enable-cxx-interop")) {
return true;
}
}

return false;
}

Optional<Job::ResponseFileInfo>
ToolChain::getResponseFileInfo(const Compilation &C, const char *executablePath,
const ToolChain::InvocationInfo &invocationInfo,
Expand Down
35 changes: 18 additions & 17 deletions lib/Driver/UnixToolChains.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -182,27 +182,28 @@ toolchains::GenericUnix::constructInvocation(const DynamicLinkJobAction &job,

// Configure the toolchain.
//
// By default use the system `clang` to perform the link. We use `clang` for
// the driver here because we do not wish to select a particular C++ runtime.
// Furthermore, until C++ interop is enabled, we cannot have a dependency on
// C++ code from pure Swift code. If linked libraries are C++ based, they
// should properly link C++. In the case of static linking, the user can
// explicitly specify the C++ runtime to link against. This is particularly
// important for platforms like android where as it is a Linux platform, the
// default C++ runtime is `libstdc++` which is unsupported on the target but
// as the builds are usually cross-compiled from Linux, libstdc++ is going to
// be present. This results in linking the wrong version of libstdc++
// generating invalid binaries. It is also possible to use different C++
// runtimes than the default C++ runtime for the platform (e.g. libc++ on
// Windows rather than msvcprt). When C++ interop is enabled, we will need to
// surface this via a driver flag. For now, opt for the simpler approach of
// just using `clang` and avoid a dependency on the C++ runtime.
const char *Clang = "clang";
// We don't use `clang++` unconditionally because we want to avoid pulling in
// a C++ standard library if it's not needed, in particular because the
// standard library that `clang++` selects by default may not be the one that
// is desired.
//
// TODO: In principle, it should be possible to use a different C++ standard
// library than the one configured by default by passing a `-stdlib` option
// to `-Xcc` and `-Xclang-linker`, e.g.
// `-Xcc -stdlib=libc++ -Xclang-linker -stdlib=libc++`.
// Once there is a driver flag for C++ interop, we will probably also want to
// add a driver flag for selecting the C++ standard library.
//
// In practice, using libc++ on Linux, for example, does not work because the
// SwiftGlibc module definition is incompatible with libc++'s header layout.
// We probably need to ensure that we use libc++'s own module map instead of
// the SwiftGlibc module map.
const char *Clang = context.cxxInteropEnabled()? "clang++" : "clang";
if (const Arg *A = context.Args.getLastArg(options::OPT_tools_directory)) {
StringRef toolchainPath(A->getValue());

// If there is a clang in the toolchain folder, use that instead.
if (auto tool = llvm::sys::findProgramByName("clang", {toolchainPath})) {
if (auto tool = llvm::sys::findProgramByName(Clang, {toolchainPath})) {
Clang = context.Args.MakeArgString(tool.get());
}

Expand Down
20 changes: 2 additions & 18 deletions lib/Driver/WindowsToolChains.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,28 +85,12 @@ toolchains::Windows::constructInvocation(const DynamicLinkJobAction &job,
}

// Configure the toolchain.
//
// By default use the system `clang` to perform the link. We use `clang` for
// the driver here because we do not wish to select a particular C++ runtime.
// Furthermore, until C++ interop is enabled, we cannot have a dependency on
// C++ code from pure Swift code. If linked libraries are C++ based, they
// should properly link C++. In the case of static linking, the user can
// explicitly specify the C++ runtime to link against. This is particularly
// important for platforms like android where as it is a Linux platform, the
// default C++ runtime is `libstdc++` which is unsupported on the target but
// as the builds are usually cross-compiled from Linux, libstdc++ is going to
// be present. This results in linking the wrong version of libstdc++
// generating invalid binaries. It is also possible to use different C++
// runtimes than the default C++ runtime for the platform (e.g. libc++ on
// Windows rather than msvcprt). When C++ interop is enabled, we will need to
// surface this via a driver flag. For now, opt for the simpler approach of
// just using `clang` and avoid a dependency on the C++ runtime.
const char *Clang = "clang";
const char *Clang = context.cxxInteropEnabled()? "clang++" : "clang";
if (const Arg *A = context.Args.getLastArg(options::OPT_tools_directory)) {
StringRef toolchainPath(A->getValue());

// If there is a clang in the toolchain folder, use that instead.
if (auto tool = llvm::sys::findProgramByName("clang", {toolchainPath}))
if (auto tool = llvm::sys::findProgramByName(Clang, {toolchainPath}))
Clang = context.Args.MakeArgString(tool.get());
}

Expand Down
31 changes: 31 additions & 0 deletions test/Driver/linker.swift
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,12 @@
// INFERRED_NAMED_DARWIN tests above: 'libLINKER.dylib'.
// RUN: %swiftc_driver -driver-print-jobs -target x86_64-apple-macosx10.9 -emit-library %s -o libLINKER.dylib | %FileCheck -check-prefix INFERRED_NAME_DARWIN %s

// RUN: %swiftc_driver -driver-print-jobs -target x86_64-apple-ios7.1 -Xfrontend -enable-cxx-interop %s 2>&1 | %FileCheck -check-prefix IOS-cxx-interop %s

// RUN: %swiftc_driver -driver-print-jobs -target x86_64-unknown-linux-gnu -Xfrontend -enable-cxx-interop %s 2>&1 | %FileCheck -check-prefix LINUX-cxx-interop %s

// RUN: %swiftc_driver -driver-print-jobs -target x86_64-unknown-windows-msvc -Xfrontend -enable-cxx-interop %s 2>&1 | %FileCheck -check-prefix WINDOWS-cxx-interop %s

// Check reading the SDKSettings.json from an SDK
// RUN: %swiftc_driver -driver-print-jobs -target x86_64-apple-macosx10.9 -sdk %S/Inputs/MacOSX10.15.versioned.sdk %s 2>&1 | %FileCheck -check-prefix MACOS_10_15 %s
// RUN: %swiftc_driver -driver-print-jobs -target x86_64-apple-macosx10.9 -sdk %S/Inputs/MacOSX10.15.4.versioned.sdk %s 2>&1 | %FileCheck -check-prefix MACOS_10_15_4 %s
Expand Down Expand Up @@ -408,6 +414,31 @@
// INFERRED_NAME_WINDOWS: -o LINKER.dll
// INFERRED_NAME_WASI: -o libLINKER.so

// IOS-cxx-interop: swift
// IOS-cxx-interop-DAG: -enable-cxx-interop
// IOS-cxx-interop-DAG: -o [[OBJECTFILE:.*]]

// IOS-cxx-interop: {{(bin/)?}}ld{{"? }}
// IOS-cxx-interop-DAG: [[OBJECTFILE]]
// IOS-cxx-interop-DAG: -lc++
// IOS-cxx-interop: -o linker

// LINUX-cxx-interop: swift
// LINUX-cxx-interop-DAG: -enable-cxx-interop
// LINUX-cxx-interop-DAG: -o [[OBJECTFILE:.*]]

// LINUX-cxx-interop: clang++{{(\.exe)?"? }}
// LINUX-cxx-interop: [[OBJECTFILE]]
// LINUX-cxx-interop: -o linker

// WINDOWS-cxx-interop: swift
// WINDOWS-cxx-interop-DAG: -enable-cxx-interop
// WINDOWS-cxx-interop-DAG: -o [[OBJECTFILE:.*]]

// WINDOWS-cxx-interop: clang++{{(\.exe)?"? }}
// WINDOWS-cxx-interop: [[OBJECTFILE]]
// WINDOWS-cxx-interop: -o linker

// Test ld detection. We use hard links to make sure
// the Swift driver really thinks it's been moved.

Expand Down