Files
swift-mirror/lib/Driver/ToolChains.cpp
Drew Crawford 7ae91eae3c Implement -static-stdlib for Linux (#1891)
[Driver] implement -static-stdlib for Linux

Implement the -static-stdlib driver flag for Linux, allowing the static
linking of the standard library.

This implementation largely follows the Darwin implementation in #1817,
although some pecularities warrant extended discussion.

The original "link with stdlib" implementation had some redundancies
with getRuntimeLibraryPath; these redundancies are resolved, and the
implementation alternates between getRuntimeLibraryPath and
getStaticRuntimeLibraryPath cleanly as appropriate.

A variety of libraries are required to link statically on Linux.  The
implementation currently dynamically links with them.  We should
probably support static linking of those as well, but I think that is
beyond the scope of a -static-stdlib flag.

The test coverage uses ldd here, as otool is not available on Linux.  As
a result, we currently have separate tests for Linux vs the other
platforms; that isn't ideal, but it seems necessary.

Perhaps the oddest part, and the one worth the most discussion, is the
use of --dynamic-list.  Inside
stdlib/public/runtime/ProtocolConformances.cpp appears the following
code:

    #elif defined(__ELF__)
    static int _addImageProtocolConformances(struct dl_phdr_info *info,
                                              size_t size, void *data) {
      // inspectArgs contains addImage*Block function and the section name
      InspectArgs *inspectArgs = reinterpret_cast<InspectArgs *>(data);

      void *handle;
      if (!info->dlpi_name || info->dlpi_name[0] == '\0') {
        handle = dlopen(nullptr, RTLD_LAZY);
      } else
        handle = dlopen(info->dlpi_name, RTLD_LAZY | RTLD_NOLOAD);
      auto conformances = reinterpret_cast<const uint8_t*>(
          dlsym(handle, inspectArgs->sectionName));

The effect of this is to search for protocol_conformances_start inside
the images.  However, dlsym only finds symbols that exist in the dynamic
table.  Failure to find the protocol conformances can be diagnosed by a
"hello world" program printing

    String(_core: Swift._StringCore(_baseAddress: Swift.OpaquePointer(_rawValue: (Opaque Value)), _countAndFlags: Swift.UInt(_value: (Opaque Value)), _owner: Swift.Optional<Swift.AnyObject>.none))

instead of "hello world".  (And also by the test coverage in this commit.)

Surprisingly, this behavior can still occur on ELF platforms even if
`objdump -t` reports a valid `.protocol_conformances_start`.  This is
because `objdump -t` searches the global table, not the dynamic table,
while dlsym only searches the dynamic table.  To configure objdump to
search only the dynamic table, use `-T`.

Inquiring minds may wonder whether dynamically-linked programs (e.g. all
Linux binaries up until now) also have a broken protocol conformance
table on ELF.  The answer is, surprisingly, no; I checked, and ordinary
ELF programs are fine.  The distinction is probably this, from the ld
manpage:

> the dynamic symbol table will normally contain only those
symbols which are referenced by some dynamic object mentioned in the
link.

I think the linker sees `.protocol_conformances_start` inside
libswiftCore.so and erroneously concludes the one in *the executable* is
"referenced by some dynamic object" (e.g. the standard library).  This
behavior seems to save the dyanmically-linked executable from a broken
protocol conformance table.  I wonder if it would be wise to apply a
similar fix to dynamically-linked programs to avoid relying on the
linker "helping" us here, but that's out of scope of this commit.

The linker manpage reflects that many people have been bitten by dlsym
"surprise", and encourages the use of `--export-dynamic`:

> If you use "dlopen" to load a dynamic object which needs to refer back
> to the symbols defined by the program, rather than some other dynamic
> object, then you will probably need to use [--export-dynamic] when
> linking the program itself.

However in this situation, the use of `--export-dynamic` causes the
entire stdlib to be exported, which is not ideal.  However, by combining
with the `--exclude-libs ALL` argument, we avoid exporting the entire stdlib.
2016-05-04 09:22:33 -07:00

1428 lines
51 KiB
C++

//===--- ToolChains.cpp - Job invocations (general and per-platform) ------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#include "ToolChains.h"
#include "swift/Basic/Dwarf.h"
#include "swift/Basic/Fallthrough.h"
#include "swift/Basic/LLVM.h"
#include "swift/Basic/Platform.h"
#include "swift/Basic/Range.h"
#include "swift/Basic/TaskQueue.h"
#include "swift/Driver/Driver.h"
#include "swift/Driver/Job.h"
#include "swift/Frontend/Frontend.h"
#include "swift/Option/Options.h"
#include "swift/Config.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/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;
/// The name of the Swift migrator binary.
static const char * const SWIFT_UPDATE_NAME = "swift-update";
/// The limit for passing a list of files on the command line.
static const size_t TOO_MANY_FILES = 128;
static void addInputsOfType(ArgStringList &Arguments,
ArrayRef<const Action *> Inputs,
types::ID InputType) {
for (auto &Input : Inputs) {
if (Input->getType() != InputType)
continue;
Arguments.push_back(cast<InputAction>(Input)->getInputArg().getValue());
}
}
static void addInputsOfType(ArgStringList &Arguments,
ArrayRef<const Job *> Jobs,
types::ID InputType) {
for (const Job *Cmd : Jobs) {
auto &output = Cmd->getOutput().getAnyOutputForType(InputType);
if (!output.empty())
Arguments.push_back(output.c_str());
}
}
static void addPrimaryInputsOfType(ArgStringList &Arguments,
ArrayRef<const Job *> Jobs,
types::ID InputType) {
for (const Job *Cmd : Jobs) {
auto &outputInfo = Cmd->getOutput();
if (outputInfo.getPrimaryOutputType() == InputType) {
for (const std::string &Output : outputInfo.getPrimaryOutputFilenames()) {
Arguments.push_back(Output.c_str());
}
}
}
}
/// Handle arguments common to all invocations of the frontend (compilation,
/// module-merging, LLDB's REPL, etc).
static void addCommonFrontendArgs(const ToolChain &TC,
const OutputInfo &OI,
const CommandOutput &output,
const ArgList &inputArgs,
ArgStringList &arguments) {
arguments.push_back("-target");
arguments.push_back(inputArgs.MakeArgString(TC.getTriple().str()));
const llvm::Triple &Triple = TC.getTriple();
// Enable address top-byte ignored in the ARM64 backend.
if (Triple.getArch() == llvm::Triple::aarch64) {
arguments.push_back("-Xllvm");
arguments.push_back("-aarch64-use-tbi");
}
// Enable or disable ObjC interop appropriately for the platform
if (Triple.isOSDarwin()) {
arguments.push_back("-enable-objc-interop");
} else {
arguments.push_back("-disable-objc-interop");
}
// Handle the CPU and its preferences.
if (auto arg = inputArgs.getLastArg(options::OPT_target_cpu))
arg->render(inputArgs, arguments);
if (!OI.SDKPath.empty()) {
arguments.push_back("-sdk");
arguments.push_back(inputArgs.MakeArgString(OI.SDKPath));
}
inputArgs.AddAllArgs(arguments, options::OPT_I);
inputArgs.AddAllArgs(arguments, options::OPT_F);
inputArgs.AddLastArg(arguments, options::OPT_AssertConfig);
inputArgs.AddLastArg(arguments, options::OPT_autolink_force_load);
inputArgs.AddLastArg(arguments, options::OPT_color_diagnostics);
inputArgs.AddLastArg(arguments, options::OPT_fixit_all);
inputArgs.AddLastArg(arguments, options::OPT_enable_app_extension);
inputArgs.AddLastArg(arguments, options::OPT_enable_testing);
inputArgs.AddLastArg(arguments, options::OPT_g_Group);
inputArgs.AddLastArg(arguments, options::OPT_import_objc_header);
inputArgs.AddLastArg(arguments, options::OPT_import_underlying_module);
inputArgs.AddLastArg(arguments, options::OPT_module_cache_path);
inputArgs.AddLastArg(arguments, options::OPT_module_link_name);
inputArgs.AddLastArg(arguments, options::OPT_nostdimport);
inputArgs.AddLastArg(arguments, options::OPT_parse_stdlib);
inputArgs.AddLastArg(arguments, options::OPT_resource_dir);
inputArgs.AddLastArg(arguments, options::OPT_solver_memory_threshold);
inputArgs.AddLastArg(arguments, options::OPT_suppress_warnings);
inputArgs.AddLastArg(arguments, options::OPT_profile_generate);
inputArgs.AddLastArg(arguments, options::OPT_profile_coverage_mapping);
inputArgs.AddLastArg(arguments, options::OPT_warnings_as_errors);
inputArgs.AddLastArg(arguments, options::OPT_sanitize_EQ);
// Pass on any build config options
inputArgs.AddAllArgs(arguments, options::OPT_D);
// Pass through the values passed to -Xfrontend.
inputArgs.AddAllArgValues(arguments, options::OPT_Xfrontend);
// Pass through any subsystem flags.
inputArgs.AddAllArgs(arguments, options::OPT_Xllvm);
inputArgs.AddAllArgs(arguments, options::OPT_Xcc);
const std::string &moduleDocOutputPath =
output.getAdditionalOutputForType(types::TY_SwiftModuleDocFile);
if (!moduleDocOutputPath.empty()) {
arguments.push_back("-emit-module-doc-path");
arguments.push_back(moduleDocOutputPath.c_str());
}
if (llvm::sys::Process::StandardErrHasColors())
arguments.push_back("-color-diagnostics");
}
ToolChain::InvocationInfo
ToolChain::constructInvocation(const CompileJobAction &job,
const JobContext &context) const {
InvocationInfo II{SWIFT_EXECUTABLE_NAME};
ArgStringList &Arguments = II.Arguments;
if (context.OI.CompilerMode == OutputInfo::Mode::UpdateCode)
II.ExecutableName = SWIFT_UPDATE_NAME;
else
Arguments.push_back("-frontend");
// Determine the frontend mode option.
const char *FrontendModeOption = nullptr;
switch (context.OI.CompilerMode) {
case OutputInfo::Mode::StandardCompile:
case OutputInfo::Mode::SingleCompile: {
switch (context.Output.getPrimaryOutputType()) {
case types::TY_Object:
FrontendModeOption = "-c";
break;
case types::TY_RawSIL:
FrontendModeOption = "-emit-silgen";
break;
case types::TY_SIL:
FrontendModeOption = "-emit-sil";
break;
case types::TY_RawSIB:
FrontendModeOption = "-emit-sibgen";
break;
case types::TY_SIB:
FrontendModeOption = "-emit-sib";
break;
case types::TY_LLVM_IR:
FrontendModeOption = "-emit-ir";
break;
case types::TY_LLVM_BC:
FrontendModeOption = "-emit-bc";
break;
case types::TY_Assembly:
FrontendModeOption = "-S";
break;
case types::TY_SwiftModuleFile:
// Since this is our primary output, we need to specify the option here.
FrontendModeOption = "-emit-module";
break;
case types::TY_Nothing:
// We were told to output nothing, so get the last mode option and use that.
if (const Arg *A = context.Args.getLastArg(options::OPT_modes_Group))
FrontendModeOption = A->getSpelling().data();
else
llvm_unreachable("We were told to perform a standard compile, "
"but no mode option was passed to the driver.");
break;
case types::TY_Swift:
case types::TY_dSYM:
case types::TY_AutolinkFile:
case types::TY_Dependencies:
case types::TY_SwiftModuleDocFile:
case types::TY_ClangModuleFile:
case types::TY_SerializedDiagnostics:
case types::TY_ObjCHeader:
case types::TY_Image:
case types::TY_SwiftDeps:
case types::TY_Remapping:
llvm_unreachable("Output type can never be primary output.");
case types::TY_INVALID:
llvm_unreachable("Invalid type ID");
}
break;
}
case OutputInfo::Mode::Immediate:
case OutputInfo::Mode::REPL:
llvm_unreachable("REPL and immediate modes handled elsewhere");
case OutputInfo::Mode::UpdateCode:
// Make sure that adding '-update-code' will permit accepting all arguments
// '-c' accepts.
FrontendModeOption = "-c";
break;
}
assert(FrontendModeOption != nullptr && "No frontend mode option specified!");
Arguments.push_back(FrontendModeOption);
assert(context.Inputs.empty() &&
"The Swift frontend does not expect to be fed any input Jobs!");
// Add input arguments.
switch (context.OI.CompilerMode) {
case OutputInfo::Mode::StandardCompile:
case OutputInfo::Mode::UpdateCode: {
assert(context.InputActions.size() == 1 &&
"The Swift frontend expects exactly one input (the primary file)!");
auto *IA = cast<InputAction>(context.InputActions[0]);
const Arg &PrimaryInputArg = IA->getInputArg();
if (context.Args.hasArg(options::OPT_driver_use_filelists) ||
context.getTopLevelInputFiles().size() > TOO_MANY_FILES) {
Arguments.push_back("-filelist");
Arguments.push_back(context.getAllSourcesPath());
Arguments.push_back("-primary-file");
PrimaryInputArg.render(context.Args, Arguments);
} else {
bool FoundPrimaryInput = false;
for (auto inputPair : context.getTopLevelInputFiles()) {
if (!types::isPartOfSwiftCompilation(inputPair.first))
continue;
// See if this input should be passed with -primary-file.
if (!FoundPrimaryInput &&
PrimaryInputArg.getIndex() == inputPair.second->getIndex()) {
Arguments.push_back("-primary-file");
FoundPrimaryInput = true;
}
Arguments.push_back(inputPair.second->getValue());
}
}
break;
}
case OutputInfo::Mode::SingleCompile: {
if (context.Args.hasArg(options::OPT_driver_use_filelists) ||
context.InputActions.size() > TOO_MANY_FILES) {
Arguments.push_back("-filelist");
Arguments.push_back(context.getAllSourcesPath());
} else {
for (const Action *A : context.InputActions) {
cast<InputAction>(A)->getInputArg().render(context.Args, Arguments);
}
}
break;
}
case OutputInfo::Mode::Immediate:
case OutputInfo::Mode::REPL:
llvm_unreachable("REPL and immediate modes handled elsewhere");
}
if (context.Args.hasArg(options::OPT_parse_stdlib))
Arguments.push_back("-disable-objc-attr-requires-foundation-module");
addCommonFrontendArgs(*this, context.OI, context.Output, context.Args,
Arguments);
// Pass the optimization level down to the frontend.
context.Args.AddLastArg(Arguments, options::OPT_O_Group);
if (context.Args.hasArg(options::OPT_parse_as_library) ||
context.Args.hasArg(options::OPT_emit_library))
Arguments.push_back("-parse-as-library");
context.Args.AddLastArg(Arguments, options::OPT_parse_sil);
Arguments.push_back("-module-name");
Arguments.push_back(context.Args.MakeArgString(context.OI.ModuleName));
const std::string &ModuleOutputPath =
context.Output.getAdditionalOutputForType(types::ID::TY_SwiftModuleFile);
if (!ModuleOutputPath.empty()) {
Arguments.push_back("-emit-module-path");
Arguments.push_back(ModuleOutputPath.c_str());
}
const std::string &ObjCHeaderOutputPath =
context.Output.getAdditionalOutputForType(types::ID::TY_ObjCHeader);
if (!ObjCHeaderOutputPath.empty()) {
assert(context.OI.CompilerMode == OutputInfo::Mode::SingleCompile &&
"The Swift tool should only emit an Obj-C header in single compile"
"mode!");
Arguments.push_back("-emit-objc-header-path");
Arguments.push_back(ObjCHeaderOutputPath.c_str());
}
const std::string &SerializedDiagnosticsPath =
context.Output.getAdditionalOutputForType(types::TY_SerializedDiagnostics);
if (!SerializedDiagnosticsPath.empty()) {
Arguments.push_back("-serialize-diagnostics-path");
Arguments.push_back(SerializedDiagnosticsPath.c_str());
}
const std::string &DependenciesPath =
context.Output.getAdditionalOutputForType(types::TY_Dependencies);
if (!DependenciesPath.empty()) {
Arguments.push_back("-emit-dependencies-path");
Arguments.push_back(DependenciesPath.c_str());
}
const std::string &ReferenceDependenciesPath =
context.Output.getAdditionalOutputForType(types::TY_SwiftDeps);
if (!ReferenceDependenciesPath.empty()) {
Arguments.push_back("-emit-reference-dependencies-path");
Arguments.push_back(ReferenceDependenciesPath.c_str());
}
const std::string &FixitsPath =
context.Output.getAdditionalOutputForType(types::TY_Remapping);
if (!FixitsPath.empty()) {
Arguments.push_back("-emit-fixits-path");
Arguments.push_back(FixitsPath.c_str());
}
if (context.OI.numThreads > 0) {
Arguments.push_back("-num-threads");
Arguments.push_back(
context.Args.MakeArgString(Twine(context.OI.numThreads)));
}
// Add the output file argument if necessary.
if (context.Output.getPrimaryOutputType() != types::TY_Nothing) {
if (context.Args.hasArg(options::OPT_driver_use_filelists) ||
context.Output.getPrimaryOutputFilenames().size() > TOO_MANY_FILES) {
Arguments.push_back("-output-filelist");
Arguments.push_back(context.getTemporaryFilePath("outputs", ""));
II.FilelistInfo = {Arguments.back(),
context.Output.getPrimaryOutputType(),
FilelistInfo::Output};
} else {
for (auto &FileName : context.Output.getPrimaryOutputFilenames()) {
Arguments.push_back("-o");
Arguments.push_back(FileName.c_str());
}
}
}
if (context.Args.hasArg(options::OPT_embed_bitcode_marker))
Arguments.push_back("-embed-bitcode-marker");
return II;
}
ToolChain::InvocationInfo
ToolChain::constructInvocation(const InterpretJobAction &job,
const JobContext &context) const {
assert(context.OI.CompilerMode == OutputInfo::Mode::Immediate);
ArgStringList Arguments;
Arguments.push_back("-frontend");
Arguments.push_back("-interpret");
assert(context.Inputs.empty() &&
"The Swift frontend does not expect to be fed any input Jobs!");
for (const Action *A : context.InputActions) {
cast<InputAction>(A)->getInputArg().render(context.Args, Arguments);
}
if (context.Args.hasArg(options::OPT_parse_stdlib))
Arguments.push_back("-disable-objc-attr-requires-foundation-module");
addCommonFrontendArgs(*this, context.OI, context.Output, context.Args,
Arguments);
// Pass the optimization level down to the frontend.
context.Args.AddLastArg(Arguments, options::OPT_O_Group);
context.Args.AddLastArg(Arguments, options::OPT_parse_sil);
Arguments.push_back("-module-name");
Arguments.push_back(context.Args.MakeArgString(context.OI.ModuleName));
context.Args.AddAllArgs(Arguments, options::OPT_l, options::OPT_framework);
// The immediate arguments must be last.
context.Args.AddLastArg(Arguments, options::OPT__DASH_DASH);
return {SWIFT_EXECUTABLE_NAME, Arguments};
}
ToolChain::InvocationInfo
ToolChain::constructInvocation(const BackendJobAction &job,
const JobContext &context) const {
assert(context.Args.hasArg(options::OPT_embed_bitcode));
ArgStringList Arguments;
Arguments.push_back("-frontend");
// Determine the frontend mode option.
const char *FrontendModeOption = nullptr;
switch (context.OI.CompilerMode) {
case OutputInfo::Mode::StandardCompile:
case OutputInfo::Mode::SingleCompile: {
switch (context.Output.getPrimaryOutputType()) {
case types::TY_Object:
FrontendModeOption = "-c";
break;
case types::TY_LLVM_IR:
FrontendModeOption = "-emit-ir";
break;
case types::TY_LLVM_BC:
FrontendModeOption = "-emit-bc";
break;
case types::TY_Assembly:
FrontendModeOption = "-S";
break;
case types::TY_Nothing:
// We were told to output nothing, so get the last mode option and use that.
if (const Arg *A = context.Args.getLastArg(options::OPT_modes_Group))
FrontendModeOption = A->getSpelling().data();
else
llvm_unreachable("We were told to perform a standard compile, "
"but no mode option was passed to the driver.");
break;
case types::TY_SwiftModuleFile:
case types::TY_RawSIL:
case types::TY_RawSIB:
case types::TY_SIL:
case types::TY_SIB:
llvm_unreachable("Cannot be output from backend job");
case types::TY_Swift:
case types::TY_dSYM:
case types::TY_AutolinkFile:
case types::TY_Dependencies:
case types::TY_SwiftModuleDocFile:
case types::TY_ClangModuleFile:
case types::TY_SerializedDiagnostics:
case types::TY_ObjCHeader:
case types::TY_Image:
case types::TY_SwiftDeps:
case types::TY_Remapping:
llvm_unreachable("Output type can never be primary output.");
case types::TY_INVALID:
llvm_unreachable("Invalid type ID");
}
break;
}
case OutputInfo::Mode::Immediate:
case OutputInfo::Mode::REPL:
case OutputInfo::Mode::UpdateCode:
llvm_unreachable("invalid mode for backend job");
}
assert(FrontendModeOption != nullptr && "No frontend mode option specified!");
Arguments.push_back(FrontendModeOption);
// Add input arguments.
switch (context.OI.CompilerMode) {
case OutputInfo::Mode::StandardCompile: {
assert(context.Inputs.size() == 1 && "The backend expects one input!");
Arguments.push_back("-primary-file");
const Job *Cmd = context.Inputs.front();
Arguments.push_back(
Cmd->getOutput().getPrimaryOutputFilename().c_str());
break;
}
case OutputInfo::Mode::SingleCompile: {
assert(context.Inputs.size() == 1 && "The backend expects one input!");
Arguments.push_back("-primary-file");
const Job *Cmd = context.Inputs.front();
// In multi-threaded compilation, the backend job must select the correct
// output file of the compilation job.
auto OutNames = Cmd->getOutput().getPrimaryOutputFilenames();
Arguments.push_back(OutNames[job.getInputIndex()].c_str());
break;
}
case OutputInfo::Mode::Immediate:
case OutputInfo::Mode::REPL:
case OutputInfo::Mode::UpdateCode:
llvm_unreachable("invalid mode for backend job");
}
// Only white-listed flags below are allowed to be embedded.
Arguments.push_back("-target");
Arguments.push_back(context.Args.MakeArgString(getTriple().str()));
// Enable address top-byte ignored in the ARM64 backend.
if (getTriple().getArch() == llvm::Triple::aarch64) {
Arguments.push_back("-Xllvm");
Arguments.push_back("-aarch64-use-tbi");
}
// Handle the CPU and its preferences.
if (auto arg = context.Args.getLastArg(options::OPT_target_cpu))
arg->render(context.Args, Arguments);
context.Args.AddLastArg(Arguments, options::OPT_parse_stdlib);
// Pass the optimization level down to the frontend.
context.Args.AddLastArg(Arguments, options::OPT_O_Group);
Arguments.push_back("-module-name");
Arguments.push_back(context.Args.MakeArgString(context.OI.ModuleName));
// Add the output file argument if necessary.
if (context.Output.getPrimaryOutputType() != types::TY_Nothing) {
for (auto &FileName : context.Output.getPrimaryOutputFilenames()) {
Arguments.push_back("-o");
Arguments.push_back(FileName.c_str());
}
}
// Add flags implied by -embed-bitcode.
Arguments.push_back("-embed-bitcode");
// Disable all llvm IR level optimizations.
Arguments.push_back("-disable-llvm-optzns");
return {SWIFT_EXECUTABLE_NAME, Arguments};
}
ToolChain::InvocationInfo
ToolChain::constructInvocation(const MergeModuleJobAction &job,
const JobContext &context) const {
InvocationInfo II{SWIFT_EXECUTABLE_NAME};
ArgStringList &Arguments = II.Arguments;
if (context.OI.CompilerMode == OutputInfo::Mode::UpdateCode)
II.ExecutableName = SWIFT_UPDATE_NAME;
else
Arguments.push_back("-frontend");
// We just want to emit a module, so pass -emit-module without any other
// mode options.
Arguments.push_back("-emit-module");
if (context.Args.hasArg(options::OPT_driver_use_filelists) ||
context.Inputs.size() > TOO_MANY_FILES) {
Arguments.push_back("-filelist");
Arguments.push_back(context.getTemporaryFilePath("inputs", ""));
II.FilelistInfo = {Arguments.back(), types::TY_SwiftModuleFile,
FilelistInfo::Input};
addInputsOfType(Arguments, context.InputActions, types::TY_SwiftModuleFile);
} else {
size_t origLen = Arguments.size();
(void)origLen;
addInputsOfType(Arguments, context.Inputs, types::TY_SwiftModuleFile);
addInputsOfType(Arguments, context.InputActions, types::TY_SwiftModuleFile);
assert(Arguments.size() - origLen >=
context.Inputs.size() + context.InputActions.size());
assert((Arguments.size() - origLen == context.Inputs.size() ||
!context.InputActions.empty()) &&
"every input to MergeModule must generate a swiftmodule");
}
// Tell all files to parse as library, which is necessary to load them as
// serialized ASTs.
Arguments.push_back("-parse-as-library");
addCommonFrontendArgs(*this, context.OI, context.Output, context.Args,
Arguments);
Arguments.push_back("-module-name");
Arguments.push_back(context.Args.MakeArgString(context.OI.ModuleName));
assert(context.Output.getPrimaryOutputType() == types::TY_SwiftModuleFile &&
"The MergeModule tool only produces swiftmodule files!");
const std::string &ObjCHeaderOutputPath =
context.Output.getAdditionalOutputForType(types::TY_ObjCHeader);
if (!ObjCHeaderOutputPath.empty()) {
Arguments.push_back("-emit-objc-header-path");
Arguments.push_back(ObjCHeaderOutputPath.c_str());
}
Arguments.push_back("-o");
Arguments.push_back(
context.Args.MakeArgString(context.Output.getPrimaryOutputFilename()));
return II;
}
ToolChain::InvocationInfo
ToolChain::constructInvocation(const ModuleWrapJobAction &job,
const JobContext &context) const {
ArgStringList Arguments;
Arguments.push_back("-modulewrap");
addInputsOfType(Arguments, context.Inputs, types::TY_SwiftModuleFile);
addInputsOfType(Arguments, context.InputActions, types::TY_SwiftModuleFile);
assert(Arguments.size() == 2 &&
"ModuleWrap expects exactly one merged swiftmodule as input");
assert(context.Output.getPrimaryOutputType() == types::TY_Object &&
"The -modulewrap mode only produces object files");
Arguments.push_back("-o");
Arguments.push_back(
context.Args.MakeArgString(context.Output.getPrimaryOutputFilename()));
return {SWIFT_EXECUTABLE_NAME, Arguments};
}
ToolChain::InvocationInfo
ToolChain::constructInvocation(const REPLJobAction &job,
const JobContext &context) const {
assert(context.Inputs.empty());
assert(context.InputActions.empty());
bool useLLDB;
switch (job.getRequestedMode()) {
case REPLJobAction::Mode::Integrated:
useLLDB = false;
break;
case REPLJobAction::Mode::RequireLLDB:
useLLDB = true;
break;
case REPLJobAction::Mode::PreferLLDB:
useLLDB = !findProgramRelativeToSwift("lldb").empty();
break;
}
ArgStringList FrontendArgs;
addCommonFrontendArgs(*this, context.OI, context.Output, context.Args,
FrontendArgs);
context.Args.AddAllArgs(FrontendArgs, options::OPT_l, options::OPT_framework,
options::OPT_L);
if (!useLLDB) {
FrontendArgs.insert(FrontendArgs.begin(), {"-frontend", "-repl"});
FrontendArgs.push_back("-module-name");
FrontendArgs.push_back(context.Args.MakeArgString(context.OI.ModuleName));
return {SWIFT_EXECUTABLE_NAME, FrontendArgs};
}
// Squash important frontend options into a single argument for LLDB.
std::string SingleArg = "--repl=";
{
llvm::raw_string_ostream os(SingleArg);
Job::printArguments(os, FrontendArgs);
}
ArgStringList Arguments;
Arguments.push_back(context.Args.MakeArgString(std::move(SingleArg)));
return {"lldb", Arguments};
}
ToolChain::InvocationInfo
ToolChain::constructInvocation(const GenerateDSYMJobAction &job,
const JobContext &context) const {
assert(context.Inputs.size() == 1);
assert(context.InputActions.empty());
assert(context.Output.getPrimaryOutputType() == types::TY_dSYM);
ArgStringList Arguments;
StringRef inputPath =
context.Inputs.front()->getOutput().getPrimaryOutputFilename();
Arguments.push_back(context.Args.MakeArgString(inputPath));
Arguments.push_back("-o");
Arguments.push_back(
context.Args.MakeArgString(context.Output.getPrimaryOutputFilename()));
return {"dsymutil", Arguments};
}
ToolChain::InvocationInfo
ToolChain::constructInvocation(const AutolinkExtractJobAction &job,
const JobContext &context) const {
llvm_unreachable("autolink extraction not implemented for this toolchain");
}
ToolChain::InvocationInfo
ToolChain::constructInvocation(const LinkJobAction &job,
const JobContext &context) const {
llvm_unreachable("linking not implemented for this toolchain");
}
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 {};
}
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()));
}
/// 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);
queue.execute(nullptr,
[&path](sys::ProcessId PID,
int returnCode,
StringRef output,
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 addPathEnvironmentVariableIfNeeded(Job::EnvironmentVector &env,
const char *name,
const char *separator,
options::ID optionID,
const ArgList &args,
StringRef extraEntry = "") {
auto linkPathOptions = args.filtered(optionID);
if (linkPathOptions.begin() == linkPathOptions.end() && extraEntry.empty())
return;
std::string newPaths;
interleave(linkPathOptions,
[&](const Arg *arg) { newPaths.append(arg->getValue()); },
[&] { newPaths.append(separator); });
if (!extraEntry.empty()) {
if (!newPaths.empty())
newPaths.append(separator);
newPaths.append(extraEntry.data(), extraEntry.size());
}
if (auto currentPaths = llvm::sys::Process::GetEnv(name)) {
newPaths.append(separator);
newPaths.append(currentPaths.getValue());
}
env.emplace_back(name, args.MakeArgString(newPaths));
}
/// Get the runtime library link path, which is platform-specific and found
/// relative to the compiler.
static void getRuntimeLibraryPath(SmallVectorImpl<char> &runtimeLibPath,
const llvm::opt::ArgList &args,
const ToolChain &TC) {
// FIXME: Duplicated from CompilerInvocation, but in theory the runtime
// library link path and the standard library module import path don't
// need to be the same.
if (const Arg *A = args.getLastArg(options::OPT_resource_dir)) {
StringRef value = A->getValue();
runtimeLibPath.append(value.begin(), value.end());
} else {
auto programPath = TC.getDriver().getSwiftProgramPath();
runtimeLibPath.append(programPath.begin(), programPath.end());
llvm::sys::path::remove_filename(runtimeLibPath); // remove /swift
llvm::sys::path::remove_filename(runtimeLibPath); // remove /bin
llvm::sys::path::append(runtimeLibPath, "lib", "swift");
}
llvm::sys::path::append(runtimeLibPath,
getPlatformNameForTriple(TC.getTriple()));
}
/// Get the runtime library link path for static linking,
/// which is platform-specific and found relative to the compiler.
static void getRuntimeStaticLibraryPath(SmallVectorImpl<char> &runtimeLibPath,
const llvm::opt::ArgList &args,
const ToolChain &TC) {
// FIXME: Duplicated from CompilerInvocation, but in theory the runtime
// library link path and the standard library module import path don't
// need to be the same.
if (const Arg *A = args.getLastArg(options::OPT_resource_dir)) {
StringRef value = A->getValue();
runtimeLibPath.append(value.begin(), value.end());
} else {
auto programPath = TC.getDriver().getSwiftProgramPath();
runtimeLibPath.append(programPath.begin(), programPath.end());
llvm::sys::path::remove_filename(runtimeLibPath); // remove /swift
llvm::sys::path::remove_filename(runtimeLibPath); // remove /bin
llvm::sys::path::append(runtimeLibPath, "lib", "swift_static");
}
llvm::sys::path::append(runtimeLibPath,
getPlatformNameForTriple(TC.getTriple()));
}
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, *this);
addPathEnvironmentVariableIfNeeded(II.ExtraEnvironment, "DYLD_LIBRARY_PATH",
":", options::OPT_L, context.Args,
runtimeLibraryPath);
addPathEnvironmentVariableIfNeeded(II.ExtraEnvironment, "DYLD_FRAMEWORK_PATH",
":", options::OPT_F, context.Args);
return II;
}
static StringRef
getDarwinLibraryNameSuffixForTriple(const llvm::Triple &triple) {
switch (getDarwinPlatformKind(triple)) {
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");
}
static void
addLinkRuntimeLibForDarwin(const ArgList &Args, ArgStringList &Arguments,
StringRef DarwinLibName, bool AddRPath,
const ToolChain &TC) {
SmallString<128> Dir;
getRuntimeLibraryPath(Dir, Args, TC);
// Remove platform name.
llvm::sys::path::remove_filename(Dir);
llvm::sys::path::append(Dir, "clang", "lib", "darwin");
SmallString<128> P(Dir);
llvm::sys::path::append(P, DarwinLibName);
Arguments.push_back(Args.MakeArgString(P));
// 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.
if (AddRPath) {
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.
Arguments.push_back("-rpath");
Arguments.push_back(Args.MakeArgString(Dir));
}
}
static void
addLinkSanitizerLibArgsForDarwin(const ArgList &Args,
ArgStringList &Arguments,
StringRef Sanitizer, const ToolChain &TC) {
// 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");
addLinkRuntimeLibForDarwin(Args, Arguments,
(Twine("libclang_rt.") + Sanitizer + "_" +
getDarwinLibraryNameSuffixForTriple(TC.getTriple()) +
"_dynamic.dylib").str(),
/*AddRPath*/ true, TC);
}
ToolChain::InvocationInfo
toolchains::Darwin::constructInvocation(const LinkJobAction &job,
const JobContext &context) const {
assert(context.Output.getPrimaryOutputType() == types::TY_Image &&
"Invalid linker output type.");
const Driver &D = getDriver();
const llvm::Triple &Triple = getTriple();
InvocationInfo II{"ld"};
ArgStringList &Arguments = II.Arguments;
if (context.Args.hasArg(options::OPT_driver_use_filelists) ||
context.Inputs.size() > TOO_MANY_FILES) {
Arguments.push_back("-filelist");
Arguments.push_back(context.getTemporaryFilePath("inputs", "LinkFileList"));
II.FilelistInfo = {Arguments.back(), types::TY_Object, FilelistInfo::Input};
} else {
addPrimaryInputsOfType(Arguments, context.Inputs, types::TY_Object);
}
addInputsOfType(Arguments, context.InputActions, types::TY_Object);
if (context.OI.DebugInfoKind == IRGenDebugInfoKind::Normal) {
size_t argCount = Arguments.size();
if (context.OI.CompilerMode == OutputInfo::Mode::SingleCompile)
addInputsOfType(Arguments, context.Inputs, types::TY_SwiftModuleFile);
else
addPrimaryInputsOfType(Arguments, context.Inputs,
types::TY_SwiftModuleFile);
if (Arguments.size() > argCount) {
assert(argCount + 1 == Arguments.size() &&
"multiple swiftmodules found for -g");
Arguments.insert(Arguments.end() - 1, "-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 Clang's logic here.
bool wantsObjCRuntime = false;
if (Triple.isiOS())
wantsObjCRuntime = Triple.isOSVersionLT(8);
else if (Triple.isWatchOS())
wantsObjCRuntime = Triple.isOSVersionLT(2);
else if (Triple.isMacOSX())
wantsObjCRuntime = Triple.isMacOSXVersionLT(10, 10);
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);
context.Args.AddAllArgs(Arguments, options::OPT_F);
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");
}
// Liking in 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.SelectedSanitizer == SanitizerKind::Address)
addLinkSanitizerLibArgsForDarwin(context.Args, Arguments, "asan", *this);
if (context.OI.SelectedSanitizer == SanitizerKind::Thread)
addLinkSanitizerLibArgsForDarwin(context.Args, Arguments, "tsan", *this);
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, *this);
// 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;
getRuntimeStaticLibraryPath(StaticRuntimeLibPath, context.Args, *this);
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));
}
if (context.Args.hasArg(options::OPT_profile_generate)) {
SmallString<128> LibProfile(RuntimeLibPath);
llvm::sys::path::remove_filename(LibProfile); // remove platform name
llvm::sys::path::append(LibProfile, "clang", "lib", "darwin");
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: We probably shouldn't be adding an rpath here unless we know ahead
// of time the standard library won't be copied.
Arguments.push_back("-rpath");
Arguments.push_back(context.Args.MakeArgString(RuntimeLibPath));
// 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.Output.getPrimaryOutputFilename().c_str());
return II;
}
ToolChain::InvocationInfo
toolchains::GenericUnix::constructInvocation(const InterpretJobAction &job,
const JobContext &context) const {
InvocationInfo II = ToolChain::constructInvocation(job, context);
SmallString<128> runtimeLibraryPath;
getRuntimeLibraryPath(runtimeLibraryPath, context.Args, *this);
addPathEnvironmentVariableIfNeeded(II.ExtraEnvironment, "LD_LIBRARY_PATH",
":", options::OPT_L, context.Args,
runtimeLibraryPath);
return II;
}
ToolChain::InvocationInfo
toolchains::GenericUnix::constructInvocation(const AutolinkExtractJobAction &job,
const JobContext &context) const {
assert(context.Output.getPrimaryOutputType() == types::TY_AutolinkFile);
ArgStringList Arguments;
addPrimaryInputsOfType(Arguments, context.Inputs, types::TY_Object);
addInputsOfType(Arguments, context.InputActions, types::TY_Object);
Arguments.push_back("-o");
Arguments.push_back(
context.Args.MakeArgString(context.Output.getPrimaryOutputFilename()));
return {"swift-autolink-extract", Arguments};
}
std::string toolchains::GenericUnix::getDefaultLinker() const {
switch(getTriple().getArch()) {
case llvm::Triple::arm:
case llvm::Triple::armeb:
case llvm::Triple::thumb:
case llvm::Triple::thumbeb:
// BFD linker has issues wrt relocation of the protocol conformance
// section on these targets, it also generates COPY relocations for
// final executables, as such, unless specified, we default to gold
// linker.
return "gold";
default:
// Otherwise, use the default BFD linker.
return "";
}
}
std::string toolchains::GenericUnix::getTargetForLinker() const {
return getTriple().str();
}
bool toolchains::GenericUnix::shouldProvideRPathToLinker() const {
return true;
}
std::string toolchains::GenericUnix::getPreInputObjectPath(
StringRef RuntimeLibraryPath) const {
// On Linux and FreeBSD (really, ELF binaries) we need to add objects
// to provide markers and size for the metadata sections.
SmallString<128> PreInputObjectPath = RuntimeLibraryPath;
llvm::sys::path::append(PreInputObjectPath,
swift::getMajorArchitectureName(getTriple()));
llvm::sys::path::append(PreInputObjectPath, "swift_begin.o");
return PreInputObjectPath.str();
}
std::string toolchains::GenericUnix::getPostInputObjectPath(
StringRef RuntimeLibraryPath) const {
SmallString<128> PostInputObjectPath = RuntimeLibraryPath;
llvm::sys::path::append(PostInputObjectPath,
swift::getMajorArchitectureName(getTriple()));
llvm::sys::path::append(PostInputObjectPath, "swift_end.o");
return PostInputObjectPath.str();
}
ToolChain::InvocationInfo
toolchains::GenericUnix::constructInvocation(const LinkJobAction &job,
const JobContext &context) const {
assert(context.Output.getPrimaryOutputType() == types::TY_Image &&
"Invalid linker output type.");
ArgStringList Arguments;
switch (job.getKind()) {
case LinkKind::None:
llvm_unreachable("invalid link kind");
case LinkKind::Executable:
// Default case, nothing extra needed
break;
case LinkKind::DynamicLibrary:
Arguments.push_back("-shared");
break;
}
// Select the linker to use
std::string Linker;
if (const Arg *A = context.Args.getLastArg(options::OPT_use_ld)) {
Linker = A->getValue();
} else {
Linker = getDefaultLinker();
}
if (!Linker.empty()) {
Arguments.push_back(context.Args.MakeArgString("-fuse-ld=" + Linker));
}
std::string Target = getTargetForLinker();
if (!Target.empty()) {
Arguments.push_back("-target");
Arguments.push_back(context.Args.MakeArgString(Target));
}
// Add the runtime library link path, which is platform-specific and found
// relative to the compiler.
llvm::SmallString<128> RuntimeLibPath;
getRuntimeLibraryPath(RuntimeLibPath, context.Args, *this);
if (shouldProvideRPathToLinker()) {
// FIXME: We probably shouldn't be adding an rpath here unless we know
// ahead of time the standard library won't be copied.
Arguments.push_back("-Xlinker");
Arguments.push_back("-rpath");
Arguments.push_back("-Xlinker");
Arguments.push_back(context.Args.MakeArgString(RuntimeLibPath));
}
auto PreInputObjectPath = getPreInputObjectPath(RuntimeLibPath);
if (!PreInputObjectPath.empty()) {
Arguments.push_back(context.Args.MakeArgString(PreInputObjectPath));
}
addPrimaryInputsOfType(Arguments, context.Inputs, types::TY_Object);
addInputsOfType(Arguments, context.InputActions, types::TY_Object);
context.Args.AddAllArgs(Arguments, options::OPT_Xlinker);
context.Args.AddAllArgs(Arguments, options::OPT_linker_option_Group);
context.Args.AddAllArgs(Arguments, options::OPT_F);
if (!context.OI.SDKPath.empty()) {
Arguments.push_back("--sysroot");
Arguments.push_back(context.Args.MakeArgString(context.OI.SDKPath));
}
// 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;
getRuntimeStaticLibraryPath(StaticRuntimeLibPath, context.Args, *this);
Arguments.push_back(context.Args.MakeArgString(StaticRuntimeLibPath));
// The following libraries are required to build a satisfactory
// static program
Arguments.push_back("-ldl");
Arguments.push_back("-lpthread");
Arguments.push_back("-lbsd");
Arguments.push_back("-licui18n");
Arguments.push_back("-licuuc");
// The runtime uses dlopen to look for the protocol conformances.
// Therefore, we need to ensure they appear in the dynamic table.
// This happens automatically for dynamically-linked programs, but
// in this case we have to take additional measures.
Arguments.push_back("-Xlinker");
Arguments.push_back("-export-dynamic");
Arguments.push_back("-Xlinker");
Arguments.push_back("--exclude-libs");
Arguments.push_back("-Xlinker");
Arguments.push_back("ALL");
}
else {
Arguments.push_back(context.Args.MakeArgString(RuntimeLibPath));
}
// Explicitly pass the target to the linker
Arguments.push_back(context.Args.MakeArgString("--target=" + getTriple().str()));
if (context.Args.hasArg(options::OPT_profile_generate)) {
SmallString<128> LibProfile(RuntimeLibPath);
llvm::sys::path::remove_filename(LibProfile); // remove platform name
llvm::sys::path::append(LibProfile, "clang", "lib");
llvm::sys::path::append(LibProfile, getTriple().getOSName(),
Twine("libclang_rt.profile-") +
getTriple().getArchName() +
".a");
Arguments.push_back(context.Args.MakeArgString(LibProfile));
}
// Always add the stdlib
Arguments.push_back("-lswiftCore");
// Add any autolinking scripts to the arguments
for (const Job *Cmd : context.Inputs) {
auto &OutputInfo = Cmd->getOutput();
if (OutputInfo.getPrimaryOutputType() == types::TY_AutolinkFile)
Arguments.push_back(context.Args.MakeArgString(
Twine("@") + OutputInfo.getPrimaryOutputFilename()));
}
// Just before the output option, allow GenericUnix toolchains to add
// additional inputs.
auto PostInputObjectPath = getPostInputObjectPath(RuntimeLibPath);
if (!PostInputObjectPath.empty()) {
Arguments.push_back(context.Args.MakeArgString(PostInputObjectPath));
}
// This should be the last option, for convenience in checking output.
Arguments.push_back("-o");
Arguments.push_back(context.Output.getPrimaryOutputFilename().c_str());
return {"clang++", Arguments};
}
std::string
toolchains::Android::getTargetForLinker() const {
// Explicitly set the linker target to "androideabi", as opposed to the
// llvm::Triple representation of "armv7-none-linux-android".
// This is the only ABI we currently support for Android.
assert(
getTriple().getArch() == llvm::Triple::arm &&
getTriple().getSubArch() == llvm::Triple::SubArchType::ARMSubArch_v7 &&
"Only armv7 targets are supported for Android");
return "armv7-none-linux-androideabi";
}
bool toolchains::Android::shouldProvideRPathToLinker() const {
return false;
}
std::string toolchains::Cygwin::getDefaultLinker() const {
// Cygwin uses the default BFD linker, even on ARM.
return "";
}
std::string toolchains::Cygwin::getTargetForLinker() const {
return "";
}
std::string toolchains::Cygwin::getPreInputObjectPath(
StringRef RuntimeLibraryPath) const {
// Cygwin does not add "begin" and "end" objects.
return "";
}
std::string toolchains::Cygwin::getPostInputObjectPath(
StringRef RuntimeLibraryPath) const {
// Cygwin does not add "begin" and "end" objects.
return "";
}