blob: 95accad29f2bd9f58aaf270d722849bce3eb6585 [file] [log] [blame]
//===------ 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/AST/DiagnosticsDriver.h"
#include "swift/AST/PlatformKind.h"
#include "swift/Basic/Dwarf.h"
#include "swift/Basic/LLVM.h"
#include "swift/Basic/Platform.h"
#include "swift/Basic/Range.h"
#include "swift/Basic/STLExtras.h"
#include "swift/Basic/TaskQueue.h"
#include "swift/Config.h"
#include "swift/Driver/Compilation.h"
#include "clang/Driver/DarwinSDKInfo.h"
#include "swift/Driver/Driver.h"
#include "swift/Driver/Job.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"
#include "llvm/Support/VersionTuple.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);
SmallVector<std::string, 4> runtimeLibraryPaths;
getRuntimeLibraryPaths(runtimeLibraryPaths, context.Args, context.OI.SDKPath,
/*Shared=*/true);
addPathEnvironmentVariableIfNeeded(II.ExtraEnvironment, "DYLD_LIBRARY_PATH",
":", options::OPT_L, context.Args,
runtimeLibraryPaths);
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) {
const DarwinPlatformKind kind = getDarwinPlatformKind(triple);
switch (kind) {
case DarwinPlatformKind::MacOS:
return "osx";
case DarwinPlatformKind::IPhoneOS:
// Here we return "osx" under the assumption that all the
// darwin runtime libraries are zippered and so the "osx" variants
// should be used for macCatalyst targets.
if (tripleIsMacCatalystEnvironment(triple))
return "osx";
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()) {
// Explicitly ask for the default toolchain so that we don't find a Clang
// included with an open-source toolchain.
const char *args[] = {"-toolchain", "default", "-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 bool findXcodeClangLibPath(const Twine &libName,
llvm::SmallVectorImpl<char> &path) {
assert(path.empty());
if (!findXcodeClangPath(path)) {
return false;
}
llvm::sys::path::remove_filename(path); // 'clang'
llvm::sys::path::remove_filename(path); // 'bin'
llvm::sys::path::append(path, "lib", libName);
return true;
}
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()));
}
/// Returns true if the compiler depends on features provided by the ObjC
/// runtime that are not present on the deployment target indicated by
/// \p triple.
static bool wantsObjCRuntime(const llvm::Triple &triple) {
assert((!triple.isTvOS() || triple.isiOS()) &&
"tvOS is considered a kind of iOS");
// When updating the versions listed here, please record the most recent
// feature being depended on and when it was introduced:
//
// - Make assigning 'nil' to an NSMutableDictionary subscript delete the
// entry, like it does for Swift.Dictionary, rather than trap.
if (triple.isiOS())
return triple.isOSVersionLT(9);
if (triple.isMacOSX())
return triple.isMacOSXVersionLT(10, 11);
if (triple.isWatchOS())
return false;
llvm_unreachable("unknown Darwin OS");
}
void
toolchains::Darwin::addLinkerInputArgs(InvocationInfo &II,
const JobContext &context) const {
ArgStringList &Arguments = II.Arguments;
if (context.shouldUseInputFileList()) {
Arguments.push_back("-filelist");
Arguments.push_back(context.getTemporaryFilePath("inputs", "LinkFileList"));
II.FilelistInfos.push_back(
{Arguments.back(), context.OI.CompilerOutputType,
FilelistInfo::WhichFiles::InputJobsAndSourceInputActions});
} else {
addPrimaryInputsOfType(Arguments, context.Inputs, context.Args,
file_types::TY_Object);
addPrimaryInputsOfType(Arguments, context.Inputs, context.Args,
file_types::TY_LLVM_BC);
addInputsOfType(Arguments, context.InputActions, file_types::TY_Object);
addInputsOfType(Arguments, context.InputActions, file_types::TY_LLVM_BC);
}
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");
}
static void findARCLiteLibPath(const toolchains::Darwin &TC,
llvm::SmallVectorImpl<char> &ARCLiteLib) {
auto& D = TC.getDriver();
llvm::sys::path::append(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();
findXcodeClangLibPath("arc", ARCLiteLib);
}
}
void
toolchains::Darwin::addArgsToLinkARCLite(ArgStringList &Arguments,
const JobContext &context) const {
if (!context.Args.hasFlag(options::OPT_link_objc_runtime,
options::OPT_no_link_objc_runtime,
/*Default=*/wantsObjCRuntime(getTriple())))
return;
llvm::SmallString<128> ARCLiteLib;
findARCLiteLibPath(*this, ARCLiteLib);
if (!ARCLiteLib.empty()) {
llvm::sys::path::append(ARCLiteLib, "libarclite_");
ARCLiteLib += getPlatformNameForTriple(getTriple());
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");
}
}
void toolchains::Darwin::addLTOLibArgs(ArgStringList &Arguments,
const JobContext &context) const {
llvm::SmallString<128> LTOLibPath;
if (findXcodeClangLibPath("libLTO.dylib", LTOLibPath)) {
Arguments.push_back("-lto_library");
Arguments.push_back(context.Args.MakeArgString(LTOLibPath));
}
}
void
toolchains::Darwin::addSanitizerArgs(ArgStringList &Arguments,
const DynamicLinkJobAction &job,
const JobContext &context) const {
// 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);
if (context.OI.SelectedSanitizers & SanitizerKind::Undefined)
addLinkSanitizerLibArgsForDarwin(context.Args, Arguments, "ubsan", *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);
}
namespace {
enum class BackDeployLibFilter {
executable,
all
};
// Whether the given job matches the backward-deployment library filter.
bool jobMatchesFilter(LinkKind jobKind, BackDeployLibFilter filter) {
switch (filter) {
case BackDeployLibFilter::executable:
return jobKind == LinkKind::Executable;
case BackDeployLibFilter::all:
return true;
}
llvm_unreachable("unhandled back deploy lib filter!");
}
}
void
toolchains::Darwin::addArgsToLinkStdlib(ArgStringList &Arguments,
const DynamicLinkJobAction &job,
const JobContext &context) const {
// Link compatibility libraries, if we're deploying back to OSes that
// have an older Swift runtime.
SmallString<128> SharedResourceDirPath;
getResourceDirPath(SharedResourceDirPath, context.Args, /*Shared=*/true);
Optional<llvm::VersionTuple> runtimeCompatibilityVersion;
if (context.Args.hasArg(options::OPT_runtime_compatibility_version)) {
auto value = context.Args.getLastArgValue(
options::OPT_runtime_compatibility_version);
if (value.equals("5.0")) {
runtimeCompatibilityVersion = llvm::VersionTuple(5, 0);
} else if (value.equals("5.1")) {
runtimeCompatibilityVersion = llvm::VersionTuple(5, 1);
} else if (value.equals("none")) {
runtimeCompatibilityVersion = None;
} else {
// TODO: diagnose unknown runtime compatibility version?
}
} else if (job.getKind() == LinkKind::Executable) {
runtimeCompatibilityVersion
= getSwiftRuntimeCompatibilityVersionForTarget(getTriple());
}
if (runtimeCompatibilityVersion) {
auto addBackDeployLib = [&](llvm::VersionTuple version,
BackDeployLibFilter filter,
StringRef libraryName) {
if (*runtimeCompatibilityVersion > version)
return;
if (!jobMatchesFilter(job.getKind(), filter))
return;
SmallString<128> BackDeployLib;
BackDeployLib.append(SharedResourceDirPath);
llvm::sys::path::append(BackDeployLib, "lib" + libraryName + ".a");
if (llvm::sys::fs::exists(BackDeployLib)) {
Arguments.push_back("-force_load");
Arguments.push_back(context.Args.MakeArgString(BackDeployLib));
}
};
#define BACK_DEPLOYMENT_LIB(Version, Filter, LibraryName) \
addBackDeployLib( \
llvm::VersionTuple Version, BackDeployLibFilter::Filter, LibraryName);
#include "swift/Frontend/BackDeploymentLibs.def"
}
// Add the runtime library link path, which is platform-specific and found
// relative to the compiler.
SmallVector<std::string, 4> RuntimeLibPaths;
getRuntimeLibraryPaths(RuntimeLibPaths, context.Args,
context.OI.SDKPath, /*Shared=*/true);
for (auto path : RuntimeLibPaths) {
Arguments.push_back("-L");
Arguments.push_back(context.Args.MakeArgString(path));
}
if (context.Args.hasFlag(options::OPT_toolchain_stdlib_rpath,
options::OPT_no_toolchain_stdlib_rpath, false)) {
// If the user has explicitly asked for a toolchain stdlib, we should
// provide one using -rpath. This used to be the default behaviour but it
// was considered annoying in at least the SwiftPM scenario (see
// https://bugs.swift.org/browse/SR-1967) and is obsolete in all scenarios
// of deploying for Swift-in-the-OS. We keep it here as an optional
// behaviour so that people downloading snapshot toolchains for testing new
// stdlibs will be able to link to the stdlib bundled in that toolchain.
for (auto path : RuntimeLibPaths) {
Arguments.push_back("-rpath");
Arguments.push_back(context.Args.MakeArgString(path));
}
} else if (!tripleRequiresRPathForSwiftInOS(getTriple()) ||
context.Args.hasArg(options::OPT_no_stdlib_rpath)) {
// If targeting an OS with Swift in /usr/lib/swift, the LC_ID_DYLIB
// install_name the stdlib will be an absolute path like
// /usr/lib/swift/libswiftCore.dylib, and we do not need to provide an rpath
// at all.
//
// Also, if the user explicitly asks for no rpath entry, we assume they know
// what they're doing and do not add one here.
} else {
// The remaining cases are back-deploying (to OSs predating
// Swift-in-the-OS). In these cases, the stdlib will be giving us (via
// stdlib/linker-support/magic-symbols-for-install-name.c) an LC_ID_DYLIB
// install_name that is rpath-relative, like @rpath/libswiftCore.dylib.
//
// If we're linking an app bundle, it's possible there's an embedded stdlib
// in there, in which case we'd want to put @executable_path/../Frameworks
// in the rpath to find and prefer it, but (a) we don't know when we're
// linking an app bundle and (b) we probably _never_ will be because Xcode
// links using clang, not the swift driver.
//
// So that leaves us with the case of linking a command-line app. These are
// only supported by installing a secondary package that puts some frozen
// Swift-in-OS libraries in the /usr/lib/swift location. That's the best we
// can give for rpath, though it might fail at runtime if the support
// package isn't installed.
Arguments.push_back("-rpath");
Arguments.push_back(context.Args.MakeArgString("/usr/lib/swift"));
// We don't need an rpath for /System/iOSSupport/usr/lib/swift because...
assert(!tripleIsMacCatalystEnvironment(getTriple())
&& "macCatalyst not supported without Swift-in-the-OS");
}
}
void
toolchains::Darwin::addProfileGenerationArgs(ArgStringList &Arguments,
const JobContext &context) const {
const llvm::Triple &Triple = getTriple();
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 (Triple.isSimulatorEnvironment()) {
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));
}
}
Optional<llvm::VersionTuple>
toolchains::Darwin::getTargetSDKVersion(const llvm::Triple &triple) const {
if (!SDKInfo)
return None;
return swift::getTargetSDKVersion(*SDKInfo, triple);
}
void
toolchains::Darwin::addDeploymentTargetArgs(ArgStringList &Arguments,
const JobContext &context) const {
auto addPlatformVersionArg = [&](const llvm::Triple &triple) {
// Compute the name of the platform for the linker.
const char *platformName;
if (tripleIsMacCatalystEnvironment(triple)) {
platformName = "mac-catalyst";
} else {
switch (getDarwinPlatformKind(triple)) {
case DarwinPlatformKind::MacOS:
platformName = "macos";
break;
case DarwinPlatformKind::IPhoneOS:
platformName = "ios";
break;
case DarwinPlatformKind::IPhoneOSSimulator:
platformName = "ios-simulator";
break;
case DarwinPlatformKind::TvOS:
platformName = "tvos";
break;
case DarwinPlatformKind::TvOSSimulator:
platformName = "tvos-simulator";
break;
case DarwinPlatformKind::WatchOS:
platformName = "watchos";
break;
case DarwinPlatformKind::WatchOSSimulator:
platformName = "watchos-simulator";
break;
}
}
// Compute the platform version.
unsigned major, minor, micro;
if (tripleIsMacCatalystEnvironment(triple)) {
triple.getiOSVersion(major, minor, micro);
// Mac Catalyst on arm was introduced with an iOS deployment target of
// 14.0; the linker doesn't want to see a deployment target before that.
if (major < 14 && triple.isAArch64()) {
major = 14;
minor = 0;
micro = 0;
}
// Mac Catalyst was introduced with an iOS deployment target of 13.0;
// the linker doesn't want to see a deployment target before that.
if (major < 13) {
major = 13;
minor = 0;
micro = 0;
}
} else {
switch (getDarwinPlatformKind((triple))) {
case DarwinPlatformKind::MacOS:
triple.getMacOSXVersion(major, minor, micro);
// The first deployment of arm64 for macOS is version 10.16;
if (triple.isAArch64() && major <= 10 && minor < 16) {
llvm::VersionTuple firstMacARM64e(10, 16, 0);
firstMacARM64e = canonicalizePlatformVersion(PlatformKind::macOS,
firstMacARM64e);
major = firstMacARM64e.getMajor();
minor = firstMacARM64e.getMinor().getValueOr(0);
micro = firstMacARM64e.getSubminor().getValueOr(0);
}
break;
case DarwinPlatformKind::IPhoneOS:
case DarwinPlatformKind::IPhoneOSSimulator:
case DarwinPlatformKind::TvOS:
case DarwinPlatformKind::TvOSSimulator:
triple.getiOSVersion(major, minor, micro);
// The first deployment of arm64 simulators is iOS/tvOS 14.0;
// the linker doesn't want to see a deployment target before that.
if (triple.isSimulatorEnvironment() && triple.isAArch64() &&
major < 14) {
major = 14;
minor = 0;
micro = 0;
}
break;
case DarwinPlatformKind::WatchOS:
case DarwinPlatformKind::WatchOSSimulator:
triple.getOSVersion(major, minor, micro);
break;
}
}
// Compute the SDK version.
unsigned sdkMajor = 0, sdkMinor = 0, sdkMicro = 0;
if (auto sdkVersion = getTargetSDKVersion(triple)) {
sdkMajor = sdkVersion->getMajor();
sdkMinor = sdkVersion->getMinor().getValueOr(0);
sdkMicro = sdkVersion->getSubminor().getValueOr(0);
}
Arguments.push_back("-platform_version");
Arguments.push_back(platformName);
addVersionString(context.Args, Arguments, major, minor, micro);
addVersionString(context.Args, Arguments, sdkMajor, sdkMinor, sdkMicro);
};
addPlatformVersionArg(getTriple());
if (auto targetVariant = getTargetVariant()) {
assert(triplesAreValidForZippering(getTriple(), *targetVariant));
addPlatformVersionArg(*targetVariant);
}
}
void toolchains::Darwin::addCommonFrontendArgs(
const OutputInfo &OI, const CommandOutput &output,
const llvm::opt::ArgList &inputArgs,
llvm::opt::ArgStringList &arguments) const {
ToolChain::addCommonFrontendArgs(OI, output, inputArgs, arguments);
if (auto sdkVersion = getTargetSDKVersion(getTriple())) {
arguments.push_back("-target-sdk-version");
arguments.push_back(inputArgs.MakeArgString(sdkVersion->getAsString()));
}
if (auto targetVariant = getTargetVariant()) {
if (auto variantSDKVersion = getTargetSDKVersion(*targetVariant)) {
arguments.push_back("-target-variant-sdk-version");
arguments.push_back(
inputArgs.MakeArgString(variantSDKVersion->getAsString()));
}
}
}
ToolChain::InvocationInfo
toolchains::Darwin::constructInvocation(const DynamicLinkJobAction &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 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;
addLinkerInputArgs(II, context);
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;
case LinkKind::StaticLibrary:
llvm_unreachable("the dynamic linker cannot build static libraries");
}
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) +
".a");
if (llvm::sys::fs::exists(CompilerRTPath))
Arguments.push_back(context.Args.MakeArgString(CompilerRTPath));
addArgsToLinkARCLite(Arguments, context);
if (job.shouldPerformLTO()) {
addLTOLibArgs(Arguments, context);
}
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");
}
addSanitizerArgs(Arguments, job, context);
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()));
// On Darwin, we only support libc++.
if (context.Args.hasArg(options::OPT_enable_experimental_cxx_interop)) {
Arguments.push_back("-lc++");
}
addArgsToLinkStdlib(Arguments, job, context);
addProfileGenerationArgs(Arguments, context);
addDeploymentTargetArgs(Arguments, context);
Arguments.push_back("-no_objc_category_merging");
// These custom arguments should be right before the object file at the end.
context.Args.AddAllArgs(Arguments, options::OPT_linker_option_Group);
context.Args.AddAllArgValues(Arguments, options::OPT_Xlinker);
// 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;
}
ToolChain::InvocationInfo
toolchains::Darwin::constructInvocation(const StaticLinkJobAction &job,
const JobContext &context) const {
assert(context.Output.getPrimaryOutputType() == file_types::TY_Image &&
"Invalid linker output type.");
// Configure the toolchain.
const char *LibTool = "libtool";
InvocationInfo II = {LibTool};
ArgStringList &Arguments = II.Arguments;
Arguments.push_back("-static");
if (context.shouldUseInputFileList()) {
Arguments.push_back("-filelist");
Arguments.push_back(context.getTemporaryFilePath("inputs", "LinkFileList"));
II.FilelistInfos.push_back({Arguments.back(), context.OI.CompilerOutputType,
FilelistInfo::WhichFiles::InputJobs});
} else {
addPrimaryInputsOfType(Arguments, context.Inputs, context.Args,
file_types::TY_Object);
addPrimaryInputsOfType(Arguments, context.Inputs, context.Args,
file_types::TY_LLVM_BC);
}
addInputsOfType(Arguments, context.InputActions, file_types::TY_Object);
addInputsOfType(Arguments, context.InputActions, file_types::TY_LLVM_BC);
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;
}
static void validateLinkObjcRuntimeARCLiteLib(const toolchains::Darwin &TC,
DiagnosticEngine &diags,
const llvm::opt::ArgList &args) {
auto Triple = TC.getTriple();
if (args.hasFlag(options::OPT_link_objc_runtime,
options::OPT_no_link_objc_runtime,
/*Default=*/wantsObjCRuntime(Triple))) {
llvm::SmallString<128> ARCLiteLib;
findARCLiteLibPath(TC, ARCLiteLib);
if (ARCLiteLib.empty()) {
diags.diagnose(SourceLoc(),
diag::warn_arclite_not_found_when_link_objc_runtime);
}
}
}
static void validateDeploymentTarget(const toolchains::Darwin &TC,
DiagnosticEngine &diags,
const llvm::opt::ArgList &args) {
// Check minimum supported OS versions.
auto triple = TC.getTriple();
if (triple.isMacOSX()) {
if (triple.isMacOSXVersionLT(10, 9))
diags.diagnose(SourceLoc(), diag::error_os_minimum_deployment,
"OS X 10.9");
} else if (triple.isiOS()) {
if (triple.isTvOS()) {
if (triple.isOSVersionLT(9, 0)) {
diags.diagnose(SourceLoc(), diag::error_os_minimum_deployment,
"tvOS 9.0");
return;
}
}
if (triple.isOSVersionLT(7))
diags.diagnose(SourceLoc(), diag::error_os_minimum_deployment,
"iOS 7");
if (triple.isArch32Bit() && !triple.isOSVersionLT(11)) {
diags.diagnose(SourceLoc(), diag::error_ios_maximum_deployment_32,
triple.getOSMajorVersion());
}
} else if (triple.isWatchOS()) {
if (triple.isOSVersionLT(2, 0)) {
diags.diagnose(SourceLoc(), diag::error_os_minimum_deployment,
"watchOS 2.0");
return;
}
}
}
static void validateTargetVariant(const toolchains::Darwin &TC,
DiagnosticEngine &diags,
const llvm::opt::ArgList &args,
StringRef defaultTarget) {
if (TC.getTargetVariant().hasValue()) {
auto target = TC.getTriple();
auto variant = *TC.getTargetVariant();
if (!triplesAreValidForZippering(target, variant)) {
diags.diagnose(SourceLoc(), diag::error_unsupported_target_variant,
variant.str(),
variant.isiOS());
}
}
}
void
toolchains::Darwin::validateArguments(DiagnosticEngine &diags,
const llvm::opt::ArgList &args,
StringRef defaultTarget) const {
// Validating arclite library path when link-objc-runtime.
validateLinkObjcRuntimeARCLiteLib(*this, diags, args);
// Validating apple platforms deployment targets.
validateDeploymentTarget(*this, diags, args);
validateTargetVariant(*this, diags, args, defaultTarget);
// Validating darwin unsupported -static-stdlib argument.
if (args.hasArg(options::OPT_static_stdlib)) {
diags.diagnose(SourceLoc(), diag::error_darwin_static_stdlib_not_supported);
}
// If a C++ standard library is specified, it has to be libc++.
if (auto arg = args.getLastArg(options::OPT_experimental_cxx_stdlib)) {
if (StringRef(arg->getValue()) != "libc++") {
diags.diagnose(SourceLoc(), diag::error_darwin_only_supports_libcxx);
}
}
}
void
toolchains::Darwin::validateOutputInfo(DiagnosticEngine &diags,
const OutputInfo &outputInfo) const {
// If we have been provided with an SDK, go read the SDK information.
if (!outputInfo.SDKPath.empty()) {
auto SDKInfoOrErr = clang::driver::parseDarwinSDKInfo(
*llvm::vfs::getRealFileSystem(), outputInfo.SDKPath);
if (SDKInfoOrErr) {
SDKInfo = *SDKInfoOrErr;
} else {
llvm::consumeError(SDKInfoOrErr.takeError());
diags.diagnose(SourceLoc(), diag::warn_drv_darwin_sdk_invalid_settings);
}
}
}