Files
swift-mirror/lib/Driver/ToolChains.cpp
Ankit Aggarwal 92d09f4e19 Extend @available to support PackageDescription
<rdar://problem/46548531> Extend @available to support PackageDescription

This introduces a new private availability kind "_PackageDescription" to
allow availability testing by an arbitary version that can be passed
using a new command-line flag "-swiftpm-manifest-version". The semantics
are exactly same as Swift version specific availability. In longer term,
it maybe possible to remove this enhancement once there is
a language-level availability support for 3rd party libraries.

Motivation:

Swift packages are configured using a Package.swift manifest file. The
manifest file uses a library called PackageDescription, which contains
various settings that can be configured for a package. The new additions
in the PackageDescription APIs are gated behind a "tools version" that
every manifest must declare. This means, packages don't automatically
get access to the new APIs. They need to update their declared tools
version in order to use the new API. This is basically similar to the
minimum deployment target version we have for our OSes.

This gating is important for allowing packages to maintain backwards
compatibility. SwiftPM currently checks for API usages at runtime in
order to implement this gating. This works reasonably well but can lead
to a poor experience with features like code-completion and module
interface generation in IDEs and editors (that use sourcekit-lsp) as
SwiftPM has no control over these features.
2018-12-08 09:38:40 +05:30

1082 lines
41 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 - 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/STLExtras.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/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;
bool ToolChain::JobContext::shouldUseInputFileList() const {
return getTopLevelInputFiles().size() > C.getFilelistThreshold();
}
bool ToolChain::JobContext::shouldUsePrimaryInputFileListInFrontendInvocation()
const {
return InputActions.size() > C.getFilelistThreshold();
}
bool ToolChain::JobContext::shouldUseMainOutputFileListInFrontendInvocation()
const {
return Output.getPrimaryOutputFilenames().size() > C.getFilelistThreshold();
}
bool ToolChain::JobContext::
shouldUseSupplementaryOutputFileMapInFrontendInvocation() const {
static const unsigned UpperBoundOnSupplementaryOutputFileTypes =
file_types::TY_INVALID;
return InputActions.size() * UpperBoundOnSupplementaryOutputFileTypes >
C.getFilelistThreshold();
}
bool ToolChain::JobContext::shouldFilterFrontendInputsByType() const {
// FIXME: SingleCompile has not filtered its inputs in the past and now people
// rely upon that. But we would like the compilation modes to be consistent.
return OI.CompilerMode != OutputInfo::Mode::SingleCompile;
}
void ToolChain::addInputsOfType(ArgStringList &Arguments,
ArrayRef<const Action *> Inputs,
file_types::ID InputType,
const char *PrefixArgument) const {
for (auto &Input : Inputs) {
if (Input->getType() != InputType)
continue;
if (PrefixArgument)
Arguments.push_back(PrefixArgument);
Arguments.push_back(cast<InputAction>(Input)->getInputArg().getValue());
}
}
void ToolChain::addInputsOfType(ArgStringList &Arguments,
ArrayRef<const Job *> Jobs,
const llvm::opt::ArgList &Args,
file_types::ID InputType,
const char *PrefixArgument) const {
for (const Job *Cmd : Jobs) {
auto output = Cmd->getOutput().getAnyOutputForType(InputType);
if (!output.empty()) {
if (PrefixArgument)
Arguments.push_back(PrefixArgument);
Arguments.push_back(Args.MakeArgString(output));
}
}
}
void ToolChain::addPrimaryInputsOfType(ArgStringList &Arguments,
ArrayRef<const Job *> Jobs,
const llvm::opt::ArgList &Args,
file_types::ID InputType,
const char *PrefixArgument) const {
for (const Job *Cmd : Jobs) {
auto &outputInfo = Cmd->getOutput();
if (outputInfo.getPrimaryOutputType() == InputType) {
for (auto Output : outputInfo.getPrimaryOutputFilenames()) {
if (PrefixArgument)
Arguments.push_back(PrefixArgument);
Arguments.push_back(Args.MakeArgString(Output));
}
}
}
}
static bool addOutputsOfType(ArgStringList &Arguments,
CommandOutput const &Output,
const llvm::opt::ArgList &Args,
file_types::ID OutputType,
const char *PrefixArgument = nullptr) {
bool Added = false;
for (auto Output : Output.getAdditionalOutputsForType(OutputType)) {
assert(!Output.empty());
if (PrefixArgument)
Arguments.push_back(PrefixArgument);
Arguments.push_back(Args.MakeArgString(Output));
Added = true;
}
return Added;
}
/// 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) {
const llvm::Triple &Triple = TC.getTriple();
// Only pass -target to the REPL or immediate modes if it was explicitly
// specified on the command line.
switch (OI.CompilerMode) {
case OutputInfo::Mode::REPL:
case OutputInfo::Mode::Immediate:
if (!inputArgs.hasArg(options::OPT_target))
break;
LLVM_FALLTHROUGH;
case OutputInfo::Mode::StandardCompile:
case OutputInfo::Mode::SingleCompile:
case OutputInfo::Mode::BatchModeCompile:
arguments.push_back("-target");
arguments.push_back(inputArgs.MakeArgString(Triple.str()));
break;
}
// 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.
inputArgs.AddLastArg(arguments, options::OPT_target_cpu);
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, options::OPT_Fsystem);
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_warn_swift3_objc_inference_minimal,
options::OPT_warn_swift3_objc_inference_complete);
inputArgs.AddLastArg(arguments, options::OPT_warn_implicit_overrides);
inputArgs.AddLastArg(arguments, options::OPT_typo_correction_limit);
inputArgs.AddLastArg(arguments, options::OPT_enable_app_extension);
inputArgs.AddLastArg(arguments, options::OPT_enable_testing);
inputArgs.AddLastArg(arguments, options::OPT_enable_private_imports);
inputArgs.AddLastArg(arguments, options::OPT_g_Group);
inputArgs.AddLastArg(arguments, options::OPT_debug_info_format);
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_enable_parseable_module_interface);
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_value_recursion_threshold);
inputArgs.AddLastArg(arguments, options::OPT_warn_swift3_objc_inference);
inputArgs.AddLastArg(arguments, options::OPT_Rpass_EQ);
inputArgs.AddLastArg(arguments, options::OPT_Rpass_missed_EQ);
inputArgs.AddLastArg(arguments, options::OPT_suppress_warnings);
inputArgs.AddLastArg(arguments, options::OPT_profile_generate);
inputArgs.AddLastArg(arguments, options::OPT_profile_use);
inputArgs.AddLastArg(arguments, options::OPT_profile_coverage_mapping);
inputArgs.AddLastArg(arguments, options::OPT_warnings_as_errors);
inputArgs.AddLastArg(arguments, options::OPT_sanitize_EQ);
inputArgs.AddLastArg(arguments, options::OPT_sanitize_coverage_EQ);
inputArgs.AddLastArg(arguments, options::OPT_swift_version);
inputArgs.AddLastArg(arguments, options::OPT_enforce_exclusivity_EQ);
inputArgs.AddLastArg(arguments, options::OPT_stats_output_dir);
inputArgs.AddLastArg(arguments, options::OPT_trace_stats_events);
inputArgs.AddLastArg(arguments, options::OPT_profile_stats_events);
inputArgs.AddLastArg(arguments, options::OPT_profile_stats_entities);
inputArgs.AddLastArg(arguments,
options::OPT_solver_shrink_unsolved_threshold);
inputArgs.AddLastArg(arguments, options::OPT_O_Group);
inputArgs.AddLastArg(arguments, options::OPT_RemoveRuntimeAsserts);
inputArgs.AddLastArg(arguments, options::OPT_AssumeSingleThreaded);
inputArgs.AddLastArg(arguments,
options::OPT_enable_experimental_dependencies);
inputArgs.AddLastArg(arguments, options::OPT_package_description_version);
// Pass on any build config options
inputArgs.AddAllArgs(arguments, options::OPT_D);
// Pass on file paths that should be remapped in debug info.
inputArgs.AddAllArgs(arguments, options::OPT_debug_prefix_map);
// Pass through the values passed to -Xfrontend.
inputArgs.AddAllArgValues(arguments, options::OPT_Xfrontend);
if (auto *A = inputArgs.getLastArg(options::OPT_working_directory)) {
// Add -Xcc -working-directory before any other -Xcc options to ensure it is
// overridden by an explicit -Xcc -working-directory, although having a
// different working directory is probably incorrect.
SmallString<128> workingDirectory(A->getValue());
llvm::sys::fs::make_absolute(workingDirectory);
arguments.push_back("-Xcc");
arguments.push_back("-working-directory");
arguments.push_back("-Xcc");
arguments.push_back(inputArgs.MakeArgString(workingDirectory));
}
// Pass through any subsystem flags.
inputArgs.AddAllArgs(arguments, options::OPT_Xllvm);
inputArgs.AddAllArgs(arguments, options::OPT_Xcc);
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;
II.allowsResponseFiles = true;
Arguments.push_back("-frontend");
{
// Determine the frontend mode option.
const char *FrontendModeOption = context.computeFrontendModeForCompile();
assert(FrontendModeOption != nullptr &&
"No frontend mode option specified!");
Arguments.push_back(FrontendModeOption);
}
context.addFrontendInputAndOutputArguments(Arguments, II.FilelistInfos);
// Forward migrator flags.
if (auto DataPath =
context.Args.getLastArg(options::OPT_api_diff_data_file)) {
Arguments.push_back("-api-diff-data-file");
Arguments.push_back(DataPath->getValue());
}
if (auto DataDir = context.Args.getLastArg(options::OPT_api_diff_data_dir)) {
Arguments.push_back("-api-diff-data-dir");
Arguments.push_back(DataDir->getValue());
}
if (context.Args.hasArg(options::OPT_dump_usr)) {
Arguments.push_back("-dump-usr");
}
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 along an -import-objc-header arg, replacing the argument with the name
// of any input PCH to the current action if one is present.
if (context.Args.hasArgNoClaim(options::OPT_import_objc_header)) {
bool ForwardAsIs = true;
bool bridgingPCHIsEnabled =
context.Args.hasFlag(options::OPT_enable_bridging_pch,
options::OPT_disable_bridging_pch, true);
bool usePersistentPCH = bridgingPCHIsEnabled &&
context.Args.hasArg(options::OPT_pch_output_dir);
if (!usePersistentPCH) {
for (auto *IJ : context.Inputs) {
if (!IJ->getOutput().getAnyOutputForType(file_types::TY_PCH).empty()) {
Arguments.push_back("-import-objc-header");
addInputsOfType(Arguments, context.Inputs, context.Args,
file_types::TY_PCH);
ForwardAsIs = false;
break;
}
}
}
if (ForwardAsIs) {
context.Args.AddLastArg(Arguments, options::OPT_import_objc_header);
}
if (usePersistentPCH) {
context.Args.AddLastArg(Arguments, options::OPT_pch_output_dir);
if (context.OI.CompilerMode == OutputInfo::Mode::StandardCompile) {
// In the 'multiple invocations for each file' mode we don't need to
// validate the PCH every time, it has been validated with the initial
// -emit-pch invocation.
Arguments.push_back("-pch-disable-validation");
}
}
}
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));
addOutputsOfType(Arguments, context.Output, context.Args,
file_types::TY_OptRecord, "-save-optimization-record-path");
if (context.Args.hasArg(options::OPT_migrate_keep_objc_visibility)) {
Arguments.push_back("-migrate-keep-objc-visibility");
}
addOutputsOfType(Arguments, context.Output, context.Args,
file_types::TY_Remapping, "-emit-remap-file-path");
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() != file_types::TY_Nothing) {
if (context.shouldUseMainOutputFileListInFrontendInvocation()) {
Arguments.push_back("-output-filelist");
Arguments.push_back(context.getTemporaryFilePath("outputs", ""));
II.FilelistInfos.push_back({Arguments.back(),
context.Output.getPrimaryOutputType(),
FilelistInfo::WhichFiles::Output});
} else {
for (auto FileName : context.Output.getPrimaryOutputFilenames()) {
Arguments.push_back("-o");
Arguments.push_back(context.Args.MakeArgString(FileName));
}
}
}
if (context.Args.hasArg(options::OPT_embed_bitcode_marker))
Arguments.push_back("-embed-bitcode-marker");
if (context.Args.hasArg(options::OPT_index_store_path)) {
context.Args.AddLastArg(Arguments, options::OPT_index_store_path);
if (!context.Args.hasArg(options::OPT_index_ignore_system_modules))
Arguments.push_back("-index-system-modules");
}
if (context.Args.hasArg(options::OPT_debug_info_store_invocation) ||
shouldStoreInvocationInDebugInfo()) {
Arguments.push_back("-debug-info-store-invocation");
}
return II;
}
const char *ToolChain::JobContext::computeFrontendModeForCompile() const {
switch (OI.CompilerMode) {
case OutputInfo::Mode::StandardCompile:
case OutputInfo::Mode::SingleCompile:
case OutputInfo::Mode::BatchModeCompile:
break;
case OutputInfo::Mode::Immediate:
case OutputInfo::Mode::REPL:
llvm_unreachable("REPL and immediate modes handled elsewhere");
}
switch (Output.getPrimaryOutputType()) {
case file_types::TY_Object:
return "-c";
case file_types::TY_PCH:
return "-emit-pch";
case file_types::TY_ASTDump:
return "-dump-ast";
case file_types::TY_RawSIL:
return "-emit-silgen";
case file_types::TY_SIL:
return "-emit-sil";
case file_types::TY_RawSIB:
return "-emit-sibgen";
case file_types::TY_SIB:
return "-emit-sib";
case file_types::TY_LLVM_IR:
return "-emit-ir";
case file_types::TY_LLVM_BC:
return "-emit-bc";
case file_types::TY_Assembly:
return "-S";
case file_types::TY_SwiftModuleFile:
// Since this is our primary output, we need to specify the option here.
return "-emit-module";
case file_types::TY_ImportedModules:
return "-emit-imported-modules";
case file_types::TY_IndexData:
return "-typecheck";
case file_types::TY_Remapping:
return "-update-code";
case file_types::TY_Nothing:
// We were told to output nothing, so get the last mode option and use that.
if (const Arg *A = Args.getLastArg(options::OPT_modes_Group))
return A->getSpelling().data();
else
llvm_unreachable("We were told to perform a standard compile, "
"but no mode option was passed to the driver.");
case file_types::TY_Swift:
case file_types::TY_dSYM:
case file_types::TY_AutolinkFile:
case file_types::TY_Dependencies:
case file_types::TY_SwiftModuleDocFile:
case file_types::TY_ClangModuleFile:
case file_types::TY_SerializedDiagnostics:
case file_types::TY_ObjCHeader:
case file_types::TY_Image:
case file_types::TY_SwiftDeps:
case file_types::TY_ModuleTrace:
case file_types::TY_TBD:
case file_types::TY_OptRecord:
case file_types::TY_SwiftParseableInterfaceFile:
llvm_unreachable("Output type can never be primary output.");
case file_types::TY_INVALID:
llvm_unreachable("Invalid type ID");
}
llvm_unreachable("unhandled output type");
}
void ToolChain::JobContext::addFrontendInputAndOutputArguments(
ArgStringList &Arguments, std::vector<FilelistInfo> &FilelistInfos) const {
switch (OI.CompilerMode) {
case OutputInfo::Mode::StandardCompile:
assert(InputActions.size() == 1 &&
"Standard-compile mode takes exactly one input (the primary file)");
break;
case OutputInfo::Mode::BatchModeCompile:
case OutputInfo::Mode::SingleCompile:
break;
case OutputInfo::Mode::Immediate:
case OutputInfo::Mode::REPL:
llvm_unreachable("REPL and immediate modes handled elsewhere");
}
const bool UseFileList = shouldUseInputFileList();
const bool MayHavePrimaryInputs = OI.mightHaveExplicitPrimaryInputs(Output);
const bool UsePrimaryFileList =
MayHavePrimaryInputs &&
shouldUsePrimaryInputFileListInFrontendInvocation();
const bool FilterInputsByType = shouldFilterFrontendInputsByType();
const bool UseSupplementaryOutputFileList =
shouldUseSupplementaryOutputFileMapInFrontendInvocation();
assert((C.getFilelistThreshold() != Compilation::NEVER_USE_FILELIST ||
!UseFileList && !UsePrimaryFileList &&
!UseSupplementaryOutputFileList) &&
"No filelists are used if FilelistThreshold=NEVER_USE_FILELIST");
if (UseFileList) {
Arguments.push_back("-filelist");
Arguments.push_back(getAllSourcesPath());
}
if (UsePrimaryFileList) {
Arguments.push_back("-primary-filelist");
Arguments.push_back(getTemporaryFilePath("primaryInputs", ""));
FilelistInfos.push_back({Arguments.back(), file_types::TY_Swift,
FilelistInfo::WhichFiles::PrimaryInputs});
}
if (!UseFileList || !UsePrimaryFileList) {
addFrontendCommandLineInputArguments(MayHavePrimaryInputs, UseFileList,
UsePrimaryFileList, FilterInputsByType,
Arguments);
}
if (UseSupplementaryOutputFileList) {
Arguments.push_back("-supplementary-output-file-map");
Arguments.push_back(getTemporaryFilePath("supplementaryOutputs", ""));
FilelistInfos.push_back({Arguments.back(), file_types::TY_INVALID,
FilelistInfo::WhichFiles::SupplementaryOutput});
} else {
addFrontendSupplementaryOutputArguments(Arguments);
}
}
void ToolChain::JobContext::addFrontendCommandLineInputArguments(
const bool mayHavePrimaryInputs, const bool useFileList,
const bool usePrimaryFileList, const bool filterByType,
ArgStringList &arguments) const {
llvm::DenseSet<StringRef> primaries;
if (mayHavePrimaryInputs) {
for (const Action *A : InputActions) {
const auto *IA = cast<InputAction>(A);
const llvm::opt::Arg &InArg = IA->getInputArg();
primaries.insert(InArg.getValue());
}
}
// -index-file compilations are weird. They are processed as SingleCompiles
// (WMO), but must indicate that there is one primary file, designated by
// -index-file-path.
if (Arg *A = Args.getLastArg(options::OPT_index_file_path)) {
assert(primaries.empty() &&
"index file jobs should be treated as single (WMO) compiles");
primaries.insert(A->getValue());
}
for (auto inputPair : getTopLevelInputFiles()) {
if (filterByType && !file_types::isPartOfSwiftCompilation(inputPair.first))
continue;
const char *inputName = inputPair.second->getValue();
const bool isPrimary = primaries.count(inputName);
if (isPrimary && !usePrimaryFileList) {
arguments.push_back("-primary-file");
arguments.push_back(inputName);
}
if ((!isPrimary || usePrimaryFileList) && !useFileList)
arguments.push_back(inputName);
}
}
void ToolChain::JobContext::addFrontendSupplementaryOutputArguments(
ArgStringList &arguments) const {
// FIXME: Get these and other argument strings from the same place for both
// driver and frontend.
addOutputsOfType(arguments, Output, Args, file_types::ID::TY_SwiftModuleFile,
"-emit-module-path");
addOutputsOfType(arguments, Output, Args, file_types::TY_SwiftModuleDocFile,
"-emit-module-doc-path");
addOutputsOfType(arguments, Output, Args,
file_types::ID::TY_SwiftParseableInterfaceFile,
"-emit-parseable-module-interface-path");
addOutputsOfType(arguments, Output, Args,
file_types::TY_SerializedDiagnostics,
"-serialize-diagnostics-path");
if (addOutputsOfType(arguments, Output, Args, file_types::ID::TY_ObjCHeader,
"-emit-objc-header-path")) {
assert(OI.CompilerMode == OutputInfo::Mode::SingleCompile &&
"The Swift tool should only emit an Obj-C header in single compile"
"mode!");
}
addOutputsOfType(arguments, Output, Args, file_types::TY_Dependencies,
"-emit-dependencies-path");
addOutputsOfType(arguments, Output, Args, file_types::TY_SwiftDeps,
"-emit-reference-dependencies-path");
addOutputsOfType(arguments, Output, Args, file_types::TY_ModuleTrace,
"-emit-loaded-module-trace-path");
addOutputsOfType(arguments, Output, Args, file_types::TY_TBD,
"-emit-tbd-path");
}
ToolChain::InvocationInfo
ToolChain::constructInvocation(const InterpretJobAction &job,
const JobContext &context) const {
assert(context.OI.CompilerMode == OutputInfo::Mode::Immediate);
InvocationInfo II{SWIFT_EXECUTABLE_NAME};
ArgStringList &Arguments = II.Arguments;
II.allowsResponseFiles = true;
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);
context.Args.AddLastArg(Arguments, options::OPT_import_objc_header);
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 II;
}
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 file_types::TY_Object:
FrontendModeOption = "-c";
break;
case file_types::TY_LLVM_IR:
FrontendModeOption = "-emit-ir";
break;
case file_types::TY_LLVM_BC:
FrontendModeOption = "-emit-bc";
break;
case file_types::TY_Assembly:
FrontendModeOption = "-S";
break;
case file_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 file_types::TY_ImportedModules:
case file_types::TY_TBD:
case file_types::TY_SwiftModuleFile:
case file_types::TY_ASTDump:
case file_types::TY_RawSIL:
case file_types::TY_RawSIB:
case file_types::TY_SIL:
case file_types::TY_SIB:
case file_types::TY_PCH:
case file_types::TY_IndexData:
llvm_unreachable("Cannot be output from backend job");
case file_types::TY_Swift:
case file_types::TY_dSYM:
case file_types::TY_AutolinkFile:
case file_types::TY_Dependencies:
case file_types::TY_SwiftModuleDocFile:
case file_types::TY_ClangModuleFile:
case file_types::TY_SerializedDiagnostics:
case file_types::TY_ObjCHeader:
case file_types::TY_Image:
case file_types::TY_SwiftDeps:
case file_types::TY_Remapping:
case file_types::TY_ModuleTrace:
case file_types::TY_OptRecord:
case file_types::TY_SwiftParseableInterfaceFile:
llvm_unreachable("Output type can never be primary output.");
case file_types::TY_INVALID:
llvm_unreachable("Invalid type ID");
}
break;
}
case OutputInfo::Mode::BatchModeCompile:
case OutputInfo::Mode::Immediate:
case OutputInfo::Mode::REPL:
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(context.Args.MakeArgString(
Cmd->getOutput().getPrimaryOutputFilename()));
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(
context.Args.MakeArgString(OutNames[job.getInputIndex()]));
break;
}
case OutputInfo::Mode::BatchModeCompile:
case OutputInfo::Mode::Immediate:
case OutputInfo::Mode::REPL:
llvm_unreachable("invalid mode for backend job");
}
// Add flags implied by -embed-bitcode.
Arguments.push_back("-embed-bitcode");
// -embed-bitcode only supports a restricted set of flags.
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.
context.Args.AddLastArg(Arguments, options::OPT_target_cpu);
// Enable optimizations, but disable all LLVM-IR-level transformations.
context.Args.AddLastArg(Arguments, options::OPT_O_Group);
Arguments.push_back("-disable-llvm-optzns");
context.Args.AddLastArg(Arguments, options::OPT_parse_stdlib);
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() != file_types::TY_Nothing) {
for (auto FileName : context.Output.getPrimaryOutputFilenames()) {
Arguments.push_back("-o");
Arguments.push_back(context.Args.MakeArgString(FileName));
}
}
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;
II.allowsResponseFiles = true;
Arguments.push_back("-frontend");
Arguments.push_back("-merge-modules");
Arguments.push_back("-emit-module");
if (context.shouldUseInputFileList()) {
Arguments.push_back("-filelist");
Arguments.push_back(context.getTemporaryFilePath("inputs", ""));
II.FilelistInfos.push_back({Arguments.back(),
file_types::TY_SwiftModuleFile,
FilelistInfo::WhichFiles::Input});
addInputsOfType(Arguments, context.InputActions,
file_types::TY_SwiftModuleFile);
} else {
size_t origLen = Arguments.size();
(void)origLen;
addInputsOfType(Arguments, context.Inputs, context.Args,
file_types::TY_SwiftModuleFile);
addInputsOfType(Arguments, context.InputActions,
file_types::TY_SwiftModuleFile);
assert(Arguments.size() - origLen >=
context.Inputs.size() + context.InputActions.size() ||
context.OI.CompilerOutputType == file_types::TY_Nothing);
assert((Arguments.size() - origLen == context.Inputs.size() ||
context.OI.CompilerOutputType == file_types::TY_Nothing ||
!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");
// Merge serialized SIL from partial modules.
Arguments.push_back("-sil-merge-partial-modules");
// Disable SIL optimization passes; we've already optimized the code in each
// partial mode.
Arguments.push_back("-disable-diagnostic-passes");
Arguments.push_back("-disable-sil-perf-optzns");
addCommonFrontendArgs(*this, context.OI, context.Output, context.Args,
Arguments);
addOutputsOfType(Arguments, context.Output, context.Args,
file_types::TY_SwiftModuleDocFile, "-emit-module-doc-path");
addOutputsOfType(Arguments, context.Output, context.Args,
file_types::ID::TY_SwiftParseableInterfaceFile,
"-emit-parseable-module-interface-path");
addOutputsOfType(Arguments, context.Output, context.Args,
file_types::TY_SerializedDiagnostics,
"-serialize-diagnostics-path");
addOutputsOfType(Arguments, context.Output, context.Args,
file_types::TY_ObjCHeader, "-emit-objc-header-path");
addOutputsOfType(Arguments, context.Output, context.Args, file_types::TY_TBD,
"-emit-tbd-path");
context.Args.AddLastArg(Arguments, options::OPT_import_objc_header);
Arguments.push_back("-module-name");
Arguments.push_back(context.Args.MakeArgString(context.OI.ModuleName));
assert(context.Output.getPrimaryOutputType() ==
file_types::TY_SwiftModuleFile &&
"The MergeModule tool only produces swiftmodule files!");
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 {
InvocationInfo II{SWIFT_EXECUTABLE_NAME};
ArgStringList &Arguments = II.Arguments;
II.allowsResponseFiles = true;
Arguments.push_back("-modulewrap");
addInputsOfType(Arguments, context.Inputs, context.Args,
file_types::TY_SwiftModuleFile);
addInputsOfType(Arguments, context.InputActions,
file_types::TY_SwiftModuleFile);
assert(Arguments.size() == 2 &&
"ModuleWrap expects exactly one merged swiftmodule as input");
assert(context.Output.getPrimaryOutputType() == file_types::TY_Object &&
"The -modulewrap mode only produces object files");
Arguments.push_back("-target");
Arguments.push_back(context.Args.MakeArgString(getTriple().str()));
Arguments.push_back("-o");
Arguments.push_back(
context.Args.MakeArgString(context.Output.getPrimaryOutputFilename()));
return II;
}
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.AddLastArg(FrontendArgs, options::OPT_import_objc_header);
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() == file_types::TY_dSYM);
ArgStringList Arguments;
auto 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 VerifyDebugInfoJobAction &job,
const JobContext &context) const {
assert(context.Inputs.size() == 1);
assert(context.InputActions.empty());
// This mirrors the clang driver's --verify-debug-info option.
ArgStringList Arguments;
Arguments.push_back("--verify");
Arguments.push_back("--debug-info");
Arguments.push_back("--eh-frame");
Arguments.push_back("--quiet");
auto inputPath =
context.Inputs.front()->getOutput().getPrimaryOutputFilename();
Arguments.push_back(context.Args.MakeArgString(inputPath));
return {"dwarfdump", Arguments};
}
ToolChain::InvocationInfo
ToolChain::constructInvocation(const GeneratePCHJobAction &job,
const JobContext &context) const {
assert(context.Inputs.empty());
assert(context.InputActions.size() == 1);
assert((!job.isPersistentPCH() &&
context.Output.getPrimaryOutputType() == file_types::TY_PCH) ||
(job.isPersistentPCH() &&
context.Output.getPrimaryOutputType() == file_types::TY_Nothing));
InvocationInfo II{SWIFT_EXECUTABLE_NAME};
ArgStringList &Arguments = II.Arguments;
II.allowsResponseFiles = true;
Arguments.push_back("-frontend");
addCommonFrontendArgs(*this, context.OI, context.Output, context.Args,
Arguments);
addOutputsOfType(Arguments, context.Output, context.Args,
file_types::TY_SerializedDiagnostics,
"-serialize-diagnostics-path");
addInputsOfType(Arguments, context.InputActions, file_types::TY_ObjCHeader);
context.Args.AddLastArg(Arguments, options::OPT_index_store_path);
if (job.isPersistentPCH()) {
Arguments.push_back("-emit-pch");
Arguments.push_back("-pch-output-dir");
Arguments.push_back(context.Args.MakeArgString(job.getPersistentPCHDir()));
} else {
Arguments.push_back("-emit-pch");
Arguments.push_back("-o");
Arguments.push_back(
context.Args.MakeArgString(context.Output.getPrimaryOutputFilename()));
}
return II;
}
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");
}
void ToolChain::addPathEnvironmentVariableIfNeeded(
Job::EnvironmentVector &env, const char *name, const char *separator,
options::ID optionID, const ArgList &args, StringRef extraEntry) const {
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));
}
void ToolChain::addLinkRuntimeLib(const ArgList &Args, ArgStringList &Arguments,
StringRef LibName) const {
SmallString<128> P;
getClangLibraryPath(Args, P);
llvm::sys::path::append(P, LibName);
Arguments.push_back(Args.MakeArgString(P));
}
void ToolChain::getClangLibraryPath(const ArgList &Args,
SmallString<128> &LibPath) const {
const llvm::Triple &T = getTriple();
getRuntimeLibraryPath(LibPath, Args, /*Shared=*/true);
// Remove platform name.
llvm::sys::path::remove_filename(LibPath);
llvm::sys::path::append(LibPath, "clang", "lib",
T.isOSDarwin() ? "darwin"
: getPlatformNameForTriple(T));
}
/// Get the runtime library link path, which is platform-specific and found
/// relative to the compiler.
void ToolChain::getRuntimeLibraryPath(SmallVectorImpl<char> &runtimeLibPath,
const llvm::opt::ArgList &args,
bool shared) const {
// 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 = 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",
shared ? "swift" : "swift_static");
}
llvm::sys::path::append(runtimeLibPath,
getPlatformNameForTriple(getTriple()));
}
bool ToolChain::sanitizerRuntimeLibExists(const ArgList &args,
StringRef sanitizerName,
bool shared) const {
SmallString<128> sanitizerLibPath;
getClangLibraryPath(args, sanitizerLibPath);
llvm::sys::path::append(sanitizerLibPath,
sanitizerRuntimeLibName(sanitizerName, shared));
return llvm::sys::fs::exists(sanitizerLibPath.str());
}