mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
And use it in getDarwinLibraryNameSuffixForTriple, making the logic there clearer. Suggested by David U!
480 lines
18 KiB
C++
480 lines
18 KiB
C++
//===------ DarwinToolChains.cpp - Job invocations (Darwin-specific) ------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "ToolChains.h"
|
|
|
|
#include "swift/Basic/Dwarf.h"
|
|
#include "swift/Basic/LLVM.h"
|
|
#include "swift/Basic/Platform.h"
|
|
#include "swift/Basic/Range.h"
|
|
#include "swift/Basic/TaskQueue.h"
|
|
#include "swift/Config.h"
|
|
#include "swift/Driver/Compilation.h"
|
|
#include "swift/Driver/Driver.h"
|
|
#include "swift/Driver/Job.h"
|
|
#include "swift/Frontend/Frontend.h"
|
|
#include "swift/Option/Options.h"
|
|
#include "clang/Basic/Version.h"
|
|
#include "clang/Driver/Util.h"
|
|
#include "llvm/ADT/StringSwitch.h"
|
|
#include "llvm/Option/Arg.h"
|
|
#include "llvm/Option/ArgList.h"
|
|
#include "llvm/ProfileData/InstrProf.h"
|
|
#include "llvm/Support/FileSystem.h"
|
|
#include "llvm/Support/Path.h"
|
|
#include "llvm/Support/Process.h"
|
|
#include "llvm/Support/Program.h"
|
|
|
|
using namespace swift;
|
|
using namespace swift::driver;
|
|
using namespace llvm::opt;
|
|
|
|
std::string
|
|
toolchains::Darwin::findProgramRelativeToSwiftImpl(StringRef name) const {
|
|
StringRef swiftPath = getDriver().getSwiftProgramPath();
|
|
StringRef swiftBinDir = llvm::sys::path::parent_path(swiftPath);
|
|
|
|
// See if we're in an Xcode toolchain.
|
|
bool hasToolchain = false;
|
|
llvm::SmallString<128> path{swiftBinDir};
|
|
llvm::sys::path::remove_filename(path); // bin
|
|
llvm::sys::path::remove_filename(path); // usr
|
|
if (llvm::sys::path::extension(path) == ".xctoolchain") {
|
|
hasToolchain = true;
|
|
llvm::sys::path::remove_filename(path); // *.xctoolchain
|
|
llvm::sys::path::remove_filename(path); // Toolchains
|
|
llvm::sys::path::append(path, "usr", "bin");
|
|
}
|
|
|
|
StringRef paths[] = {swiftBinDir, path};
|
|
auto pathsRef = llvm::makeArrayRef(paths);
|
|
if (!hasToolchain)
|
|
pathsRef = pathsRef.drop_back();
|
|
|
|
auto result = llvm::sys::findProgramByName(name, pathsRef);
|
|
if (result)
|
|
return result.get();
|
|
return {};
|
|
}
|
|
|
|
ToolChain::InvocationInfo
|
|
toolchains::Darwin::constructInvocation(const InterpretJobAction &job,
|
|
const JobContext &context) const {
|
|
InvocationInfo II = ToolChain::constructInvocation(job, context);
|
|
|
|
SmallString<128> runtimeLibraryPath;
|
|
getRuntimeLibraryPath(runtimeLibraryPath, context.Args, /*Shared=*/true);
|
|
|
|
addPathEnvironmentVariableIfNeeded(II.ExtraEnvironment, "DYLD_LIBRARY_PATH",
|
|
":", options::OPT_L, context.Args,
|
|
runtimeLibraryPath);
|
|
addPathEnvironmentVariableIfNeeded(II.ExtraEnvironment, "DYLD_FRAMEWORK_PATH",
|
|
":", options::OPT_F, context.Args);
|
|
// FIXME: Add options::OPT_Fsystem paths to DYLD_FRAMEWORK_PATH as well.
|
|
return II;
|
|
}
|
|
|
|
static StringRef
|
|
getDarwinLibraryNameSuffixForTriple(const llvm::Triple &triple,
|
|
bool distinguishSimulator = true) {
|
|
const DarwinPlatformKind kind = getDarwinPlatformKind(triple);
|
|
const DarwinPlatformKind effectiveKind =
|
|
distinguishSimulator ? kind : getNonSimulatorPlatform(kind);
|
|
switch (effectiveKind) {
|
|
case DarwinPlatformKind::MacOS:
|
|
return "osx";
|
|
case DarwinPlatformKind::IPhoneOS:
|
|
return "ios";
|
|
case DarwinPlatformKind::IPhoneOSSimulator:
|
|
return "iossim";
|
|
case DarwinPlatformKind::TvOS:
|
|
return "tvos";
|
|
case DarwinPlatformKind::TvOSSimulator:
|
|
return "tvossim";
|
|
case DarwinPlatformKind::WatchOS:
|
|
return "watchos";
|
|
case DarwinPlatformKind::WatchOSSimulator:
|
|
return "watchossim";
|
|
}
|
|
llvm_unreachable("Unsupported Darwin platform");
|
|
}
|
|
|
|
std::string toolchains::Darwin::sanitizerRuntimeLibName(StringRef Sanitizer,
|
|
bool shared) const {
|
|
return (Twine("libclang_rt.") + Sanitizer + "_" +
|
|
getDarwinLibraryNameSuffixForTriple(this->getTriple()) +
|
|
(shared ? "_dynamic.dylib" : ".a"))
|
|
.str();
|
|
}
|
|
|
|
static void addLinkRuntimeLibRPath(const ArgList &Args,
|
|
ArgStringList &Arguments,
|
|
StringRef DarwinLibName,
|
|
const ToolChain &TC) {
|
|
// Adding the rpaths might negatively interact when other rpaths are involved,
|
|
// so we should make sure we add the rpaths last, after all user-specified
|
|
// rpaths. This is currently true from this place, but we need to be
|
|
// careful if this function is ever called before user's rpaths are emitted.
|
|
assert(DarwinLibName.endswith(".dylib") && "must be a dynamic library");
|
|
|
|
// Add @executable_path to rpath to support having the dylib copied with
|
|
// the executable.
|
|
Arguments.push_back("-rpath");
|
|
Arguments.push_back("@executable_path");
|
|
|
|
// Add the path to the resource dir to rpath to support using the dylib
|
|
// from the default location without copying.
|
|
|
|
SmallString<128> ClangLibraryPath;
|
|
TC.getClangLibraryPath(Args, ClangLibraryPath);
|
|
|
|
Arguments.push_back("-rpath");
|
|
Arguments.push_back(Args.MakeArgString(ClangLibraryPath));
|
|
}
|
|
|
|
static void addLinkSanitizerLibArgsForDarwin(const ArgList &Args,
|
|
ArgStringList &Arguments,
|
|
StringRef Sanitizer,
|
|
const ToolChain &TC,
|
|
bool shared = true) {
|
|
// Sanitizer runtime libraries requires C++.
|
|
Arguments.push_back("-lc++");
|
|
// Add explicit dependency on -lc++abi, as -lc++ doesn't re-export
|
|
// all RTTI-related symbols that are used.
|
|
Arguments.push_back("-lc++abi");
|
|
|
|
auto LibName = TC.sanitizerRuntimeLibName(Sanitizer, shared);
|
|
TC.addLinkRuntimeLib(Args, Arguments, LibName);
|
|
|
|
if (shared)
|
|
addLinkRuntimeLibRPath(Args, Arguments, LibName, TC);
|
|
}
|
|
|
|
/// Runs <code>xcrun -f clang</code> in order to find the location of Clang for
|
|
/// the currently active Xcode.
|
|
///
|
|
/// We get the "currently active" part by passing through the DEVELOPER_DIR
|
|
/// environment variable (along with the rest of the environment).
|
|
static bool findXcodeClangPath(llvm::SmallVectorImpl<char> &path) {
|
|
assert(path.empty());
|
|
|
|
auto xcrunPath = llvm::sys::findProgramByName("xcrun");
|
|
if (!xcrunPath.getError()) {
|
|
const char *args[] = {"-f", "clang", nullptr};
|
|
sys::TaskQueue queue;
|
|
queue.addTask(xcrunPath->c_str(), args, /*Env=*/llvm::None,
|
|
/*Context=*/nullptr,
|
|
/*SeparateErrors=*/true);
|
|
queue.execute(nullptr,
|
|
[&path](sys::ProcessId PID, int returnCode, StringRef output,
|
|
StringRef errors,
|
|
sys::TaskProcessInformation ProcInfo,
|
|
void *unused) -> sys::TaskFinishedResponse {
|
|
if (returnCode == 0) {
|
|
output = output.rtrim();
|
|
path.append(output.begin(), output.end());
|
|
}
|
|
return sys::TaskFinishedResponse::ContinueExecution;
|
|
});
|
|
}
|
|
|
|
return !path.empty();
|
|
}
|
|
|
|
static void addVersionString(const ArgList &inputArgs, ArgStringList &arguments,
|
|
unsigned major, unsigned minor, unsigned micro) {
|
|
llvm::SmallString<8> buf;
|
|
llvm::raw_svector_ostream os{buf};
|
|
os << major << '.' << minor << '.' << micro;
|
|
arguments.push_back(inputArgs.MakeArgString(os.str()));
|
|
}
|
|
|
|
ToolChain::InvocationInfo
|
|
toolchains::Darwin::constructInvocation(const LinkJobAction &job,
|
|
const JobContext &context) const {
|
|
assert(context.Output.getPrimaryOutputType() == file_types::TY_Image &&
|
|
"Invalid linker output type.");
|
|
|
|
if (context.Args.hasFlag(options::OPT_static_executable,
|
|
options::OPT_no_static_executable, false)) {
|
|
llvm::report_fatal_error("-static-executable is not supported on Darwin");
|
|
}
|
|
|
|
const Driver &D = getDriver();
|
|
const llvm::Triple &Triple = getTriple();
|
|
|
|
// Configure the toolchain.
|
|
// By default, use the system `ld` to link.
|
|
const char *LD = "ld";
|
|
if (const Arg *A = context.Args.getLastArg(options::OPT_tools_directory)) {
|
|
StringRef toolchainPath(A->getValue());
|
|
|
|
// If there is a 'ld' in the toolchain folder, use that instead.
|
|
if (auto toolchainLD =
|
|
llvm::sys::findProgramByName("ld", {toolchainPath})) {
|
|
LD = context.Args.MakeArgString(toolchainLD.get());
|
|
}
|
|
}
|
|
|
|
InvocationInfo II = {LD};
|
|
ArgStringList &Arguments = II.Arguments;
|
|
|
|
if (context.shouldUseInputFileList()) {
|
|
Arguments.push_back("-filelist");
|
|
Arguments.push_back(context.getTemporaryFilePath("inputs", "LinkFileList"));
|
|
II.FilelistInfos.push_back({Arguments.back(), file_types::TY_Object,
|
|
FilelistInfo::WhichFiles::Input});
|
|
} else {
|
|
addPrimaryInputsOfType(Arguments, context.Inputs, context.Args,
|
|
file_types::TY_Object);
|
|
}
|
|
|
|
addInputsOfType(Arguments, context.InputActions, file_types::TY_Object);
|
|
|
|
if (context.OI.CompilerMode == OutputInfo::Mode::SingleCompile)
|
|
addInputsOfType(Arguments, context.Inputs, context.Args,
|
|
file_types::TY_SwiftModuleFile, "-add_ast_path");
|
|
else
|
|
addPrimaryInputsOfType(Arguments, context.Inputs, context.Args,
|
|
file_types::TY_SwiftModuleFile, "-add_ast_path");
|
|
|
|
// Add all .swiftmodule file inputs as arguments, preceded by the
|
|
// "-add_ast_path" linker option.
|
|
addInputsOfType(Arguments, context.InputActions,
|
|
file_types::TY_SwiftModuleFile, "-add_ast_path");
|
|
|
|
switch (job.getKind()) {
|
|
case LinkKind::None:
|
|
llvm_unreachable("invalid link kind");
|
|
case LinkKind::Executable:
|
|
// The default for ld; no extra flags necessary.
|
|
break;
|
|
case LinkKind::DynamicLibrary:
|
|
Arguments.push_back("-dylib");
|
|
break;
|
|
}
|
|
|
|
assert(Triple.isOSDarwin());
|
|
|
|
// FIXME: If we used Clang as a linker instead of going straight to ld,
|
|
// we wouldn't have to replicate a bunch of Clang's logic here.
|
|
|
|
// Always link the regular compiler_rt if it's present.
|
|
//
|
|
// Note: Normally we'd just add this unconditionally, but it's valid to build
|
|
// Swift and use it as a linker without building compiler_rt.
|
|
SmallString<128> CompilerRTPath;
|
|
getClangLibraryPath(context.Args, CompilerRTPath);
|
|
llvm::sys::path::append(
|
|
CompilerRTPath,
|
|
Twine("libclang_rt.") +
|
|
getDarwinLibraryNameSuffixForTriple(Triple, /*simulator*/false) +
|
|
".a");
|
|
if (llvm::sys::fs::exists(CompilerRTPath))
|
|
Arguments.push_back(context.Args.MakeArgString(CompilerRTPath));
|
|
|
|
bool wantsObjCRuntime = false;
|
|
if (Triple.isiOS())
|
|
wantsObjCRuntime = Triple.isOSVersionLT(9);
|
|
else if (Triple.isMacOSX())
|
|
wantsObjCRuntime = Triple.isMacOSXVersionLT(10, 11);
|
|
|
|
if (context.Args.hasFlag(options::OPT_link_objc_runtime,
|
|
options::OPT_no_link_objc_runtime,
|
|
/*Default=*/wantsObjCRuntime)) {
|
|
llvm::SmallString<128> ARCLiteLib(D.getSwiftProgramPath());
|
|
llvm::sys::path::remove_filename(ARCLiteLib); // 'swift'
|
|
llvm::sys::path::remove_filename(ARCLiteLib); // 'bin'
|
|
llvm::sys::path::append(ARCLiteLib, "lib", "arc");
|
|
|
|
if (!llvm::sys::fs::is_directory(ARCLiteLib)) {
|
|
// If we don't have a 'lib/arc/' directory, find the "arclite" library
|
|
// relative to the Clang in the active Xcode.
|
|
ARCLiteLib.clear();
|
|
if (findXcodeClangPath(ARCLiteLib)) {
|
|
llvm::sys::path::remove_filename(ARCLiteLib); // 'clang'
|
|
llvm::sys::path::remove_filename(ARCLiteLib); // 'bin'
|
|
llvm::sys::path::append(ARCLiteLib, "lib", "arc");
|
|
}
|
|
}
|
|
|
|
if (!ARCLiteLib.empty()) {
|
|
llvm::sys::path::append(ARCLiteLib, "libarclite_");
|
|
ARCLiteLib += getPlatformNameForTriple(Triple);
|
|
ARCLiteLib += ".a";
|
|
|
|
Arguments.push_back("-force_load");
|
|
Arguments.push_back(context.Args.MakeArgString(ARCLiteLib));
|
|
|
|
// Arclite depends on CoreFoundation.
|
|
Arguments.push_back("-framework");
|
|
Arguments.push_back("CoreFoundation");
|
|
} else {
|
|
// FIXME: We should probably diagnose this, but this is not a place where
|
|
// we can emit diagnostics. Silently ignore it for now.
|
|
}
|
|
}
|
|
|
|
context.Args.AddAllArgValues(Arguments, options::OPT_Xlinker);
|
|
context.Args.AddAllArgs(Arguments, options::OPT_linker_option_Group);
|
|
for (const Arg *arg :
|
|
context.Args.filtered(options::OPT_F, options::OPT_Fsystem)) {
|
|
Arguments.push_back("-F");
|
|
Arguments.push_back(arg->getValue());
|
|
}
|
|
|
|
if (context.Args.hasArg(options::OPT_enable_app_extension)) {
|
|
// Keep this string fixed in case the option used by the
|
|
// compiler itself changes.
|
|
Arguments.push_back("-application_extension");
|
|
}
|
|
|
|
// Linking sanitizers will add rpaths, which might negatively interact when
|
|
// other rpaths are involved, so we should make sure we add the rpaths after
|
|
// all user-specified rpaths.
|
|
if (context.OI.SelectedSanitizers & SanitizerKind::Address)
|
|
addLinkSanitizerLibArgsForDarwin(context.Args, Arguments, "asan", *this);
|
|
|
|
if (context.OI.SelectedSanitizers & SanitizerKind::Thread)
|
|
addLinkSanitizerLibArgsForDarwin(context.Args, Arguments, "tsan", *this);
|
|
|
|
// Only link in libFuzzer for executables.
|
|
if (job.getKind() == LinkKind::Executable &&
|
|
(context.OI.SelectedSanitizers & SanitizerKind::Fuzzer))
|
|
addLinkSanitizerLibArgsForDarwin(context.Args, Arguments, "fuzzer", *this,
|
|
/*shared=*/false);
|
|
|
|
if (context.Args.hasArg(options::OPT_embed_bitcode,
|
|
options::OPT_embed_bitcode_marker)) {
|
|
Arguments.push_back("-bitcode_bundle");
|
|
}
|
|
|
|
if (!context.OI.SDKPath.empty()) {
|
|
Arguments.push_back("-syslibroot");
|
|
Arguments.push_back(context.Args.MakeArgString(context.OI.SDKPath));
|
|
}
|
|
|
|
Arguments.push_back("-lobjc");
|
|
Arguments.push_back("-lSystem");
|
|
|
|
Arguments.push_back("-arch");
|
|
Arguments.push_back(context.Args.MakeArgString(getTriple().getArchName()));
|
|
|
|
// Add the runtime library link path, which is platform-specific and found
|
|
// relative to the compiler.
|
|
SmallString<128> RuntimeLibPath;
|
|
getRuntimeLibraryPath(RuntimeLibPath, context.Args, /*Shared=*/true);
|
|
|
|
// Link the standard library.
|
|
Arguments.push_back("-L");
|
|
if (context.Args.hasFlag(options::OPT_static_stdlib,
|
|
options::OPT_no_static_stdlib, false)) {
|
|
SmallString<128> StaticRuntimeLibPath;
|
|
getRuntimeLibraryPath(StaticRuntimeLibPath, context.Args, /*Shared=*/false);
|
|
Arguments.push_back(context.Args.MakeArgString(StaticRuntimeLibPath));
|
|
Arguments.push_back("-lc++");
|
|
Arguments.push_back("-framework");
|
|
Arguments.push_back("Foundation");
|
|
Arguments.push_back("-force_load_swift_libs");
|
|
} else {
|
|
Arguments.push_back(context.Args.MakeArgString(RuntimeLibPath));
|
|
// FIXME: We probably shouldn't be adding an rpath here unless we know ahead
|
|
// of time the standard library won't be copied. SR-1967
|
|
Arguments.push_back("-rpath");
|
|
Arguments.push_back(context.Args.MakeArgString(RuntimeLibPath));
|
|
}
|
|
|
|
if (context.Args.hasArg(options::OPT_profile_generate)) {
|
|
SmallString<128> LibProfile;
|
|
getClangLibraryPath(context.Args, LibProfile);
|
|
|
|
StringRef RT;
|
|
if (Triple.isiOS()) {
|
|
if (Triple.isTvOS())
|
|
RT = "tvos";
|
|
else
|
|
RT = "ios";
|
|
} else if (Triple.isWatchOS()) {
|
|
RT = "watchos";
|
|
} else {
|
|
assert(Triple.isMacOSX());
|
|
RT = "osx";
|
|
}
|
|
|
|
StringRef Sim;
|
|
if (tripleIsAnySimulator(Triple)) {
|
|
Sim = "sim";
|
|
}
|
|
|
|
llvm::sys::path::append(LibProfile,
|
|
"libclang_rt.profile_" + RT + Sim + ".a");
|
|
|
|
// FIXME: Continue accepting the old path for simulator libraries for now.
|
|
if (!Sim.empty() && !llvm::sys::fs::exists(LibProfile)) {
|
|
llvm::sys::path::remove_filename(LibProfile);
|
|
llvm::sys::path::append(LibProfile, "libclang_rt.profile_" + RT + ".a");
|
|
}
|
|
|
|
Arguments.push_back(context.Args.MakeArgString(LibProfile));
|
|
}
|
|
|
|
// FIXME: Properly handle deployment targets.
|
|
assert(Triple.isiOS() || Triple.isWatchOS() || Triple.isMacOSX());
|
|
if (Triple.isiOS()) {
|
|
bool isiOSSimulator = tripleIsiOSSimulator(Triple);
|
|
if (Triple.isTvOS()) {
|
|
if (isiOSSimulator)
|
|
Arguments.push_back("-tvos_simulator_version_min");
|
|
else
|
|
Arguments.push_back("-tvos_version_min");
|
|
} else {
|
|
if (isiOSSimulator)
|
|
Arguments.push_back("-ios_simulator_version_min");
|
|
else
|
|
Arguments.push_back("-iphoneos_version_min");
|
|
}
|
|
unsigned major, minor, micro;
|
|
Triple.getiOSVersion(major, minor, micro);
|
|
addVersionString(context.Args, Arguments, major, minor, micro);
|
|
} else if (Triple.isWatchOS()) {
|
|
if (tripleIsWatchSimulator(Triple))
|
|
Arguments.push_back("-watchos_simulator_version_min");
|
|
else
|
|
Arguments.push_back("-watchos_version_min");
|
|
unsigned major, minor, micro;
|
|
Triple.getOSVersion(major, minor, micro);
|
|
addVersionString(context.Args, Arguments, major, minor, micro);
|
|
} else {
|
|
Arguments.push_back("-macosx_version_min");
|
|
unsigned major, minor, micro;
|
|
Triple.getMacOSXVersion(major, minor, micro);
|
|
addVersionString(context.Args, Arguments, major, minor, micro);
|
|
}
|
|
|
|
Arguments.push_back("-no_objc_category_merging");
|
|
|
|
// This should be the last option, for convenience in checking output.
|
|
Arguments.push_back("-o");
|
|
Arguments.push_back(
|
|
context.Args.MakeArgString(context.Output.getPrimaryOutputFilename()));
|
|
|
|
return II;
|
|
}
|
|
|
|
bool toolchains::Darwin::shouldStoreInvocationInDebugInfo() const {
|
|
// This matches the behavior in Clang (see
|
|
// clang/lib/driver/ToolChains/Darwin.cpp).
|
|
if (const char *S = ::getenv("RC_DEBUG_OPTIONS"))
|
|
return S[0] != '\0';
|
|
return false;
|
|
}
|