blob: acc6cbf7f66a1881f368af540f349fb3c9c921c8 [file] [log] [blame]
//===--- Driver.cpp - Swift compiler driver -------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// This file contains implementations of parts of the compiler driver.
//
//===----------------------------------------------------------------------===//
#include "swift/Driver/Driver.h"
#include "ToolChains.h"
#include "swift/AST/DiagnosticEngine.h"
#include "swift/AST/DiagnosticsDriver.h"
#include "swift/AST/DiagnosticsFrontend.h"
#include "swift/Basic/LLVM.h"
#include "swift/Basic/OutputFileMap.h"
#include "swift/Basic/Range.h"
#include "swift/Basic/Statistic.h"
#include "swift/Basic/TaskQueue.h"
#include "swift/Basic/Version.h"
#include "swift/Config.h"
#include "swift/Driver/Action.h"
#include "swift/Driver/Compilation.h"
#include "swift/Driver/Job.h"
#include "swift/Driver/PrettyStackTrace.h"
#include "swift/Driver/ToolChain.h"
#include "swift/Option/Options.h"
#include "swift/Option/SanitizerOptions.h"
#include "swift/Parse/Lexer.h"
#include "swift/Strings.h"
#include "llvm/ADT/APInt.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Config/config.h"
#include "llvm/Option/Arg.h"
#include "llvm/Option/ArgList.h"
#include "llvm/Option/OptTable.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Host.h"
#include "llvm/Support/MD5.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/PrettyStackTrace.h"
#include "llvm/Support/raw_ostream.h"
#include "CompilationRecord.h"
#include <memory>
using namespace swift;
using namespace swift::driver;
using namespace llvm::opt;
Driver::Driver(StringRef DriverExecutable,
StringRef Name,
ArrayRef<const char *> Args,
DiagnosticEngine &Diags)
: Opts(createSwiftOptTable()), Diags(Diags),
Name(Name), DriverExecutable(DriverExecutable),
DefaultTargetTriple(llvm::sys::getDefaultTargetTriple()) {
// The driver kind must be parsed prior to parsing arguments, since that
// affects how arguments are parsed.
parseDriverKind(Args.slice(1));
}
Driver::~Driver() = default;
void Driver::parseDriverKind(ArrayRef<const char *> Args) {
// The default driver kind is determined by Name.
StringRef DriverName = Name;
std::string OptName;
// However, the driver kind may be overridden if the first argument is
// --driver-mode.
if (!Args.empty()) {
OptName = getOpts().getOption(options::OPT_driver_mode).getPrefixedName();
StringRef FirstArg(Args[0]);
if (FirstArg.startswith(OptName))
DriverName = FirstArg.drop_front(OptName.size());
}
Optional<DriverKind> Kind =
llvm::StringSwitch<Optional<DriverKind>>(DriverName)
.Case("swift", DriverKind::Interactive)
.Case("swiftc", DriverKind::Batch)
.Case("swift-autolink-extract", DriverKind::AutolinkExtract)
.Case("swift-format", DriverKind::SwiftFormat)
.Default(None);
if (Kind.hasValue())
driverKind = Kind.getValue();
else if (!OptName.empty())
Diags.diagnose({}, diag::error_invalid_arg_value, OptName, DriverName);
}
ArrayRef<const char *> Driver::getArgsWithoutProgramNameAndDriverMode(
ArrayRef<const char *> Args) const {
Args = Args.slice(1);
if (Args.empty())
return Args;
const std::string OptName =
getOpts().getOption(options::OPT_driver_mode).getPrefixedName();
if (StringRef(Args[0]).startswith(OptName))
Args = Args.slice(1);
return Args;
}
static void validateBridgingHeaderArgs(DiagnosticEngine &diags,
const ArgList &args) {
if (!args.hasArgNoClaim(options::OPT_import_objc_header))
return;
if (args.hasArgNoClaim(options::OPT_import_underlying_module))
diags.diagnose({}, diag::error_framework_bridging_header);
if (args.hasArgNoClaim(options::OPT_emit_parseable_module_interface,
options::OPT_emit_parseable_module_interface_path)) {
diags.diagnose({}, diag::error_bridging_header_parseable_interface);
}
}
static void validateDeploymentTarget(DiagnosticEngine &diags,
const ArgList &args) {
const Arg *A = args.getLastArg(options::OPT_target);
if (!A)
return;
// Check minimum supported OS versions.
llvm::Triple triple(llvm::Triple::normalize(A->getValue()));
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 validateWarningControlArgs(DiagnosticEngine &diags,
const ArgList &args) {
if (args.hasArg(options::OPT_suppress_warnings) &&
args.hasArg(options::OPT_warnings_as_errors)) {
diags.diagnose(SourceLoc(), diag::error_conflicting_options,
"-warnings-as-errors", "-suppress-warnings");
}
}
static void validateProfilingArgs(DiagnosticEngine &diags,
const ArgList &args) {
const Arg *ProfileGenerate = args.getLastArg(options::OPT_profile_generate);
const Arg *ProfileUse = args.getLastArg(options::OPT_profile_use);
if (ProfileGenerate && ProfileUse) {
diags.diagnose(SourceLoc(), diag::error_conflicting_options,
"-profile-generate", "-profile-use");
}
// Check if the profdata is missing
if (ProfileUse && !llvm::sys::fs::exists(ProfileUse->getValue())) {
diags.diagnose(SourceLoc(), diag::error_profile_missing,
ProfileUse->getValue());
}
}
static void validateDebugInfoArgs(DiagnosticEngine &diags,
const ArgList &args) {
// Check for missing debug option when verifying debug info.
if (args.hasArg(options::OPT_verify_debug_info)) {
Arg *debugOpt = args.getLastArg(swift::options::OPT_g_Group);
if (!debugOpt || debugOpt->getOption().matches(swift::options::OPT_gnone)) {
diags.diagnose(SourceLoc(),
diag::verify_debug_info_requires_debug_option);
}
}
// Check for any -debug-prefix-map options that aren't of the form
// 'original=remapped' (either side can be empty, however).
for (auto A : args.getAllArgValues(options::OPT_debug_prefix_map))
if (A.find('=') == StringRef::npos)
diags.diagnose(SourceLoc(), diag::error_invalid_debug_prefix_map, A);
}
static void validateCompilationConditionArgs(DiagnosticEngine &diags,
const ArgList &args) {
for (const Arg *A : args.filtered(options::OPT_D)) {
StringRef name = A->getValue();
if (name.find('=') != StringRef::npos) {
diags.diagnose(SourceLoc(),
diag::cannot_assign_value_to_conditional_compilation_flag,
name);
} else if (name.startswith("-D")) {
diags.diagnose(SourceLoc(), diag::redundant_prefix_compilation_flag,
name);
} else if (!Lexer::isIdentifier(name)) {
diags.diagnose(SourceLoc(), diag::invalid_conditional_compilation_flag,
name);
}
}
}
static void validateAutolinkingArgs(DiagnosticEngine &diags,
const ArgList &args) {
auto *forceLoadArg = args.getLastArg(options::OPT_autolink_force_load);
if (!forceLoadArg)
return;
auto *incrementalArg = args.getLastArg(options::OPT_incremental);
if (!incrementalArg)
return;
// Note: -incremental can itself be overridden by other arguments later
// on, but since -autolink-force-load is a rare and not-really-recommended
// option it's not worth modeling that complexity here (or moving the
// check somewhere else).
diags.diagnose(SourceLoc(), diag::error_option_not_supported,
forceLoadArg->getSpelling(), incrementalArg->getSpelling());
}
/// Perform miscellaneous early validation of arguments.
static void validateArgs(DiagnosticEngine &diags, const ArgList &args) {
validateBridgingHeaderArgs(diags, args);
validateDeploymentTarget(diags, args);
validateWarningControlArgs(diags, args);
validateProfilingArgs(diags, args);
validateDebugInfoArgs(diags, args);
validateCompilationConditionArgs(diags, args);
validateAutolinkingArgs(diags, args);
}
std::unique_ptr<ToolChain>
Driver::buildToolChain(const llvm::opt::InputArgList &ArgList) {
if (const Arg *A = ArgList.getLastArg(options::OPT_target))
DefaultTargetTriple = llvm::Triple::normalize(A->getValue());
const llvm::Triple target(DefaultTargetTriple);
switch (target.getOS()) {
case llvm::Triple::Darwin:
case llvm::Triple::MacOSX:
case llvm::Triple::IOS:
case llvm::Triple::TvOS:
case llvm::Triple::WatchOS:
return llvm::make_unique<toolchains::Darwin>(*this, target);
case llvm::Triple::Linux:
if (target.isAndroid())
return llvm::make_unique<toolchains::Android>(*this, target);
return llvm::make_unique<toolchains::GenericUnix>(*this, target);
case llvm::Triple::FreeBSD:
return llvm::make_unique<toolchains::GenericUnix>(*this, target);
case llvm::Triple::Win32:
if (target.isWindowsCygwinEnvironment())
return llvm::make_unique<toolchains::Cygwin>(*this, target);
return llvm::make_unique<toolchains::Windows>(*this, target);
case llvm::Triple::Haiku:
return llvm::make_unique<toolchains::GenericUnix>(*this, target);
default:
Diags.diagnose(SourceLoc(), diag::error_unknown_target,
ArgList.getLastArg(options::OPT_target)->getValue());
break;
}
return nullptr;
}
std::unique_ptr<sys::TaskQueue> Driver::buildTaskQueue(const Compilation &C) {
const auto &ArgList = C.getArgs();
unsigned NumberOfParallelCommands = 1;
if (const Arg *A = ArgList.getLastArg(options::OPT_j)) {
if (StringRef(A->getValue()).getAsInteger(10, NumberOfParallelCommands)) {
Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value,
A->getAsString(ArgList), A->getValue());
return nullptr;
}
}
if (environmentVariableRequestedMaximumDeterminism()) {
NumberOfParallelCommands = 1;
Diags.diagnose(SourceLoc(), diag::remark_max_determinism_overriding,
"-j");
}
const bool DriverSkipExecution =
ArgList.hasArg(options::OPT_driver_skip_execution,
options::OPT_driver_print_jobs);
if (DriverSkipExecution) {
return llvm::make_unique<sys::DummyTaskQueue>(NumberOfParallelCommands);
} else {
return llvm::make_unique<sys::TaskQueue>(NumberOfParallelCommands,
C.getStatsReporter());
}
}
static void computeArgsHash(SmallString<32> &out, const DerivedArgList &args) {
SmallVector<const Arg *, 32> interestingArgs;
interestingArgs.reserve(args.size());
std::copy_if(args.begin(), args.end(), std::back_inserter(interestingArgs),
[](const Arg *arg) {
return !arg->getOption().hasFlag(options::DoesNotAffectIncrementalBuild) &&
arg->getOption().getKind() != Option::InputClass;
});
llvm::array_pod_sort(interestingArgs.begin(), interestingArgs.end(),
[](const Arg * const *lhs, const Arg * const *rhs)->int {
auto cmpID = (*lhs)->getOption().getID() - (*rhs)->getOption().getID();
if (cmpID != 0)
return cmpID;
return (*lhs)->getIndex() - (*rhs)->getIndex();
});
llvm::MD5 hash;
for (const Arg *arg : interestingArgs) {
hash.update(arg->getOption().getID());
for (const char *value : const_cast<Arg *>(arg)->getValues())
hash.update(value);
}
llvm::MD5::MD5Result hashBuf;
hash.final(hashBuf);
llvm::MD5::stringifyResult(hashBuf, out);
}
class Driver::InputInfoMap
: public llvm::SmallDenseMap<const Arg *, CompileJobAction::InputInfo, 16> {
};
using InputInfoMap = Driver::InputInfoMap;
/// Get the filename for build record. Returns true if failed.
/// Additionally, set 'outputBuildRecordForModuleOnlyBuild' to true if this is
/// full compilation with swiftmodule.
static bool getCompilationRecordPath(std::string &buildRecordPath,
bool &outputBuildRecordForModuleOnlyBuild,
const OutputInfo &OI,
const Optional<OutputFileMap> &OFM,
DiagnosticEngine *Diags) {
if (!OFM) {
// FIXME: This should work without an output file map. We should have
// another way to specify a build record and where to put intermediates.
if (Diags)
Diags->diagnose(SourceLoc(), diag::incremental_requires_output_file_map);
return true;
}
if (auto *masterOutputMap = OFM->getOutputMapForSingleOutput())
buildRecordPath = masterOutputMap->lookup(file_types::TY_SwiftDeps);
if (buildRecordPath.empty()) {
if (Diags)
Diags->diagnose(SourceLoc(),
diag::incremental_requires_build_record_entry,
file_types::getTypeName(file_types::TY_SwiftDeps));
return true;
}
// In 'emit-module' only mode, use build-record filename suffixed with
// '~moduleonly'. So that module-only mode doesn't mess up build-record
// file for full compilation.
if (OI.CompilerOutputType == file_types::TY_SwiftModuleFile) {
buildRecordPath = buildRecordPath.append("~moduleonly");
} else if (OI.ShouldTreatModuleAsTopLevelOutput) {
// If we emit module along with full compilation, emit build record
// file for '-emit-module' only mode as well.
outputBuildRecordForModuleOnlyBuild = true;
}
return false;
}
static bool failedToReadOutOfDateMap(bool ShowIncrementalBuildDecisions,
StringRef buildRecordPath,
StringRef reason = "") {
if (ShowIncrementalBuildDecisions) {
llvm::outs() << "Incremental compilation has been disabled due to "
<< "malformed build record file '" << buildRecordPath << "'.";
if (!reason.empty()) {
llvm::outs() << " " << reason;
}
llvm::outs() << "\n";
}
return true;
}
/// Returns true on error.
static bool populateOutOfDateMap(InputInfoMap &map,
llvm::sys::TimePoint<> &LastBuildTime,
StringRef argsHashStr,
const InputFileList &inputs,
StringRef buildRecordPath,
bool ShowIncrementalBuildDecisions) {
// Treat a missing file as "no previous build".
auto buffer = llvm::MemoryBuffer::getFile(buildRecordPath);
if (!buffer) {
if (ShowIncrementalBuildDecisions)
llvm::outs() << "Incremental compilation could not read build record.\n";
return false;
}
namespace yaml = llvm::yaml;
using InputInfo = CompileJobAction::InputInfo;
llvm::SourceMgr SM;
yaml::Stream stream(buffer.get()->getMemBufferRef(), SM);
auto I = stream.begin();
if (I == stream.end() || !I->getRoot())
return failedToReadOutOfDateMap(ShowIncrementalBuildDecisions,
buildRecordPath);
auto *topLevelMap = dyn_cast<yaml::MappingNode>(I->getRoot());
if (!topLevelMap)
return failedToReadOutOfDateMap(ShowIncrementalBuildDecisions,
buildRecordPath);
SmallString<64> scratch;
llvm::StringMap<InputInfo> previousInputs;
bool versionValid = false;
bool optionsMatch = true;
auto readTimeValue = [&scratch](yaml::Node *node,
llvm::sys::TimePoint<> &timeValue) -> bool {
auto *seq = dyn_cast<yaml::SequenceNode>(node);
if (!seq)
return true;
auto seqI = seq->begin(), seqE = seq->end();
if (seqI == seqE)
return true;
auto *secondsRaw = dyn_cast<yaml::ScalarNode>(&*seqI);
if (!secondsRaw)
return true;
std::time_t parsedSeconds;
if (secondsRaw->getValue(scratch).getAsInteger(10, parsedSeconds))
return true;
++seqI;
if (seqI == seqE)
return true;
auto *nanosecondsRaw = dyn_cast<yaml::ScalarNode>(&*seqI);
if (!nanosecondsRaw)
return true;
std::chrono::system_clock::rep parsedNanoseconds;
if (nanosecondsRaw->getValue(scratch).getAsInteger(10, parsedNanoseconds))
return true;
++seqI;
if (seqI != seqE)
return true;
timeValue = llvm::sys::TimePoint<>(std::chrono::seconds(parsedSeconds));
timeValue += std::chrono::nanoseconds(parsedNanoseconds);
return false;
};
// FIXME: LLVM's YAML support does incremental parsing in such a way that
// for-range loops break.
SmallString<64> CompilationRecordSwiftVersion;
for (auto i = topLevelMap->begin(), e = topLevelMap->end(); i != e; ++i) {
auto *key = cast<yaml::ScalarNode>(i->getKey());
StringRef keyStr = key->getValue(scratch);
using compilation_record::TopLevelKey;
if (keyStr == compilation_record::getName(TopLevelKey::Version)) {
auto *value = dyn_cast<yaml::ScalarNode>(i->getValue());
if (!value) {
auto reason = ("Malformed value for key '" + keyStr + "'.")
.toStringRef(scratch);
return failedToReadOutOfDateMap(ShowIncrementalBuildDecisions,
buildRecordPath, reason);
}
// NB: We check against
// swift::version::Version::getCurrentLanguageVersion() here because any
// -swift-version argument is handled in the argsHashStr check that
// follows.
CompilationRecordSwiftVersion = value->getValue(scratch);
versionValid = (CompilationRecordSwiftVersion
== version::getSwiftFullVersion(
version::Version::getCurrentLanguageVersion()));
} else if (keyStr == compilation_record::getName(TopLevelKey::Options)) {
auto *value = dyn_cast<yaml::ScalarNode>(i->getValue());
if (!value)
return true;
optionsMatch = (argsHashStr == value->getValue(scratch));
} else if (keyStr == compilation_record::getName(TopLevelKey::BuildTime)) {
auto *value = dyn_cast<yaml::SequenceNode>(i->getValue());
if (!value) {
auto reason = ("Malformed value for key '" + keyStr + "'.")
.toStringRef(scratch);
return failedToReadOutOfDateMap(ShowIncrementalBuildDecisions,
buildRecordPath, reason);
}
llvm::sys::TimePoint<> timeVal;
if (readTimeValue(i->getValue(), timeVal))
return true;
LastBuildTime = timeVal;
} else if (keyStr == compilation_record::getName(TopLevelKey::Inputs)) {
auto *inputMap = dyn_cast<yaml::MappingNode>(i->getValue());
if (!inputMap) {
auto reason = ("Malformed value for key '" + keyStr + "'.")
.toStringRef(scratch);
return failedToReadOutOfDateMap(ShowIncrementalBuildDecisions,
buildRecordPath, reason);
}
// FIXME: LLVM's YAML support does incremental parsing in such a way that
// for-range loops break.
for (auto i = inputMap->begin(), e = inputMap->end(); i != e; ++i) {
auto *key = dyn_cast<yaml::ScalarNode>(i->getKey());
if (!key)
return true;
auto *value = dyn_cast<yaml::SequenceNode>(i->getValue());
if (!value)
return true;
using compilation_record::getInfoStatusForIdentifier;
auto previousBuildState =
getInfoStatusForIdentifier(value->getRawTag());
if (!previousBuildState)
return true;
llvm::sys::TimePoint<> timeValue;
if (readTimeValue(value, timeValue))
return true;
auto inputName = key->getValue(scratch);
previousInputs[inputName] = { *previousBuildState, timeValue };
}
}
}
if (!versionValid) {
if (ShowIncrementalBuildDecisions) {
auto v = version::getSwiftFullVersion(
version::Version::getCurrentLanguageVersion());
llvm::outs() << "Incremental compilation has been disabled, due to a "
<< "compiler version mismatch.\n"
<< "\tCompiling with: " << v << "\n"
<< "\tPreviously compiled with: "
<< CompilationRecordSwiftVersion << "\n";
}
return true;
}
if (!optionsMatch) {
if (ShowIncrementalBuildDecisions) {
llvm::outs() << "Incremental compilation has been disabled, because "
<< "different arguments were passed to the compiler.\n";
}
return true;
}
size_t numInputsFromPrevious = 0;
for (auto &inputPair : inputs) {
auto iter = previousInputs.find(inputPair.second->getValue());
if (iter == previousInputs.end()) {
map[inputPair.second] = InputInfo::makeNewlyAdded();
continue;
}
++numInputsFromPrevious;
map[inputPair.second] = iter->getValue();
}
if (numInputsFromPrevious == previousInputs.size()) {
return false;
} else {
// If a file was removed, we've lost its dependency info. Rebuild everything.
// FIXME: Can we do better?
if (ShowIncrementalBuildDecisions) {
llvm::DenseSet<StringRef> inputArgs;
for (auto &inputPair : inputs) {
inputArgs.insert(inputPair.second->getValue());
}
SmallVector<StringRef, 8> missingInputs;
for (auto &previousInput : previousInputs) {
auto previousInputArg = previousInput.getKey();
if (inputArgs.find(previousInputArg) == inputArgs.end()) {
missingInputs.push_back(previousInputArg);
}
}
llvm::outs() << "Incremental compilation has been disabled, because "
<< "the following inputs were used in the previous "
<< "compilation, but not in the current compilation:\n";
for (auto &missing : missingInputs) {
llvm::outs() << "\t" << missing << "\n";
}
}
return true;
}
}
// warn if -embed-bitcode is set and the output type is not an object
static void validateEmbedBitcode(DerivedArgList &Args, const OutputInfo &OI,
DiagnosticEngine &Diags) {
if (Args.hasArg(options::OPT_embed_bitcode) &&
OI.CompilerOutputType != file_types::TY_Object) {
Diags.diagnose(SourceLoc(), diag::warn_ignore_embed_bitcode);
Args.eraseArg(options::OPT_embed_bitcode);
}
if (Args.hasArg(options::OPT_embed_bitcode_marker) &&
OI.CompilerOutputType != file_types::TY_Object) {
Diags.diagnose(SourceLoc(), diag::warn_ignore_embed_bitcode_marker);
Args.eraseArg(options::OPT_embed_bitcode_marker);
}
}
/// Gets the filelist threshold to use. Diagnoses and returns true on error.
static bool getFilelistThreshold(DerivedArgList &Args, size_t &FilelistThreshold,
DiagnosticEngine &Diags) {
FilelistThreshold = 128;
// claim and diagnose deprecated -driver-use-filelists
bool HasUseFilelists = Args.hasArg(options::OPT_driver_use_filelists);
if (HasUseFilelists)
Diags.diagnose(SourceLoc(), diag::warn_use_filelists_deprecated);
if (const Arg *A = Args.getLastArg(options::OPT_driver_filelist_threshold)) {
// Use the supplied threshold
if (StringRef(A->getValue()).getAsInteger(10, FilelistThreshold)) {
Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value,
A->getAsString(Args), A->getValue());
return true;
}
} else if (HasUseFilelists) {
// Treat -driver-use-filelists as -driver-filelist-threshold=0
FilelistThreshold = 0;
} // else stick with the default
return false;
}
static unsigned
getDriverBatchSeed(llvm::opt::InputArgList &ArgList,
DiagnosticEngine &Diags) {
unsigned DriverBatchSeed = 0;
if (const Arg *A = ArgList.getLastArg(options::OPT_driver_batch_seed)) {
if (StringRef(A->getValue()).getAsInteger(10, DriverBatchSeed)) {
Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value,
A->getAsString(ArgList), A->getValue());
}
}
return DriverBatchSeed;
}
static Optional<unsigned>
getDriverBatchCount(llvm::opt::InputArgList &ArgList,
DiagnosticEngine &Diags)
{
if (const Arg *A = ArgList.getLastArg(options::OPT_driver_batch_count)) {
unsigned Count = 0;
if (StringRef(A->getValue()).getAsInteger(10, Count)) {
Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value,
A->getAsString(ArgList), A->getValue());
} else {
return Count;
}
}
return None;
}
static bool computeIncremental(const llvm::opt::InputArgList *ArgList,
const bool ShowIncrementalBuildDecisions) {
if (!ArgList->hasArg(options::OPT_incremental))
return false;
const char *ReasonToDisable =
ArgList->hasArg(options::OPT_whole_module_optimization)
? "is not compatible with whole module optimization."
: ArgList->hasArg(options::OPT_embed_bitcode)
? "is not currently compatible with embedding LLVM IR bitcode."
: nullptr;
if (!ReasonToDisable)
return true;
if (ShowIncrementalBuildDecisions) {
llvm::outs() << "Incremental compilation has been disabled, because it "
<< ReasonToDisable;
}
return false;
}
static std::string
computeWorkingDirectory(const llvm::opt::InputArgList *ArgList) {
if (auto *A = ArgList->getLastArg(options::OPT_working_directory)) {
SmallString<128> workingDirectory;
workingDirectory = A->getValue();
llvm::sys::fs::make_absolute(workingDirectory);
std::string result = workingDirectory.str().str();
return result;
}
return std::string();
}
static std::unique_ptr<UnifiedStatsReporter>
createStatsReporter(const llvm::opt::InputArgList *ArgList,
const InputFileList &Inputs, const OutputInfo OI,
StringRef DefaultTargetTriple) {
const Arg *A = ArgList->getLastArgNoClaim(options::OPT_stats_output_dir);
if (!A)
return nullptr;
StringRef OptType;
if (const Arg *OptA = ArgList->getLastArgNoClaim(options::OPT_O_Group)) {
OptType = OptA->getSpelling();
}
StringRef InputName;
if (Inputs.size() == 1) {
InputName = Inputs[0].second->getSpelling();
}
StringRef OutputType = file_types::getExtension(OI.CompilerOutputType);
return llvm::make_unique<UnifiedStatsReporter>("swift-driver",
OI.ModuleName,
InputName,
DefaultTargetTriple,
OutputType,
OptType,
A->getValue());
}
static bool
computeContinueBuildingAfterErrors(const bool BatchMode,
const llvm::opt::InputArgList *ArgList) {
// Note: Batch mode handling of serialized diagnostics requires that all
// batches get to run, in order to make sure that all diagnostics emitted
// during the compilation end up in at least one serialized diagnostic file.
// Therefore, treat batch mode as implying -continue-building-after-errors.
// (This behavior could be limited to only when serialized diagnostics are
// being emitted, but this seems more consistent and less surprising for
// users.)
// FIXME: We don't really need (or want) a full ContinueBuildingAfterErrors.
// If we fail to precompile a bridging header, for example, there's no need
// to go on to compilation of source files, and if compilation of source files
// fails, we shouldn't try to link. Instead, we'd want to let all jobs finish
// but not schedule any new ones.
return BatchMode ||
ArgList->hasArg(options::OPT_continue_building_after_errors);
}
static Optional<unsigned>
getDriverBatchSizeLimit(llvm::opt::InputArgList &ArgList,
DiagnosticEngine &Diags)
{
if (const Arg *A = ArgList.getLastArg(options::OPT_driver_batch_size_limit)) {
unsigned Limit = 0;
if (StringRef(A->getValue()).getAsInteger(10, Limit)) {
Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value,
A->getAsString(ArgList), A->getValue());
} else {
return Limit;
}
}
return None;
}
std::unique_ptr<Compilation>
Driver::buildCompilation(const ToolChain &TC,
std::unique_ptr<llvm::opt::InputArgList> ArgList) {
llvm::PrettyStackTraceString CrashInfo("Compilation construction");
llvm::sys::TimePoint<> StartTime = std::chrono::system_clock::now();
// Claim --driver-mode here, since it's already been handled.
(void) ArgList->hasArg(options::OPT_driver_mode);
DriverPrintBindings = ArgList->hasArg(options::OPT_driver_print_bindings);
const std::string workingDirectory = computeWorkingDirectory(ArgList.get());
std::unique_ptr<DerivedArgList> TranslatedArgList(
translateInputAndPathArgs(*ArgList, workingDirectory));
validateArgs(Diags, *TranslatedArgList);
if (Diags.hadAnyError())
return nullptr;
if (!handleImmediateArgs(*TranslatedArgList, TC)) {
return nullptr;
}
// Construct the list of inputs.
InputFileList Inputs;
buildInputs(TC, *TranslatedArgList, Inputs);
if (Diags.hadAnyError())
return nullptr;
// Determine the OutputInfo for the driver.
OutputInfo OI;
bool BatchMode = false;
OI.CompilerMode = computeCompilerMode(*TranslatedArgList, Inputs, BatchMode);
buildOutputInfo(TC, *TranslatedArgList, BatchMode, Inputs, OI);
if (Diags.hadAnyError())
return nullptr;
assert(OI.CompilerOutputType != file_types::ID::TY_INVALID &&
"buildOutputInfo() must set a valid output type!");
validateEmbedBitcode(*TranslatedArgList, OI, Diags);
if (OI.CompilerMode == OutputInfo::Mode::REPL)
// REPL mode expects no input files, so suppress the error.
SuppressNoInputFilesError = true;
Optional<OutputFileMap> OFM =
buildOutputFileMap(*TranslatedArgList, workingDirectory);
if (Diags.hadAnyError())
return nullptr;
if (ArgList->hasArg(options::OPT_driver_print_output_file_map)) {
if (OFM)
OFM->dump(llvm::errs(), true);
else
Diags.diagnose(SourceLoc(), diag::error_no_output_file_map_specified);
return nullptr;
}
const bool ShowIncrementalBuildDecisions =
ArgList->hasArg(options::OPT_driver_show_incremental);
const bool Incremental =
computeIncremental(ArgList.get(), ShowIncrementalBuildDecisions);
std::string buildRecordPath;
bool outputBuildRecordForModuleOnlyBuild = false;
getCompilationRecordPath(buildRecordPath, outputBuildRecordForModuleOnlyBuild,
OI, OFM, Incremental ? &Diags : nullptr);
SmallString<32> ArgsHash;
computeArgsHash(ArgsHash, *TranslatedArgList);
llvm::sys::TimePoint<> LastBuildTime = llvm::sys::TimePoint<>::min();
InputInfoMap outOfDateMap;
bool rebuildEverything = true;
if (Incremental && !buildRecordPath.empty()) {
if (populateOutOfDateMap(outOfDateMap, LastBuildTime, ArgsHash, Inputs,
buildRecordPath, ShowIncrementalBuildDecisions)) {
// FIXME: Distinguish errors from "file removed", which is benign.
} else {
rebuildEverything = false;
}
}
size_t DriverFilelistThreshold;
if (getFilelistThreshold(*TranslatedArgList, DriverFilelistThreshold, Diags))
return nullptr;
OutputLevel Level = OutputLevel::Normal;
if (const Arg *A =
ArgList->getLastArg(options::OPT_driver_print_jobs, options::OPT_v,
options::OPT_parseable_output)) {
if (A->getOption().matches(options::OPT_driver_print_jobs))
Level = OutputLevel::PrintJobs;
else if (A->getOption().matches(options::OPT_v))
Level = OutputLevel::Verbose;
else if (A->getOption().matches(options::OPT_parseable_output))
Level = OutputLevel::Parseable;
else
llvm_unreachable("Unknown OutputLevel argument!");
}
// About to move argument list, so capture some flags that will be needed
// later.
const bool DriverPrintActions =
ArgList->hasArg(options::OPT_driver_print_actions);
const bool DriverPrintDerivedOutputFileMap =
ArgList->hasArg(options::OPT_driver_print_derived_output_file_map);
const bool ContinueBuildingAfterErrors =
computeContinueBuildingAfterErrors(BatchMode, ArgList.get());
const bool ShowJobLifecycle =
ArgList->hasArg(options::OPT_driver_show_job_lifecycle);
// In order to confine the values below, while still moving the argument
// list, and preserving the interface to Compilation, enclose the call to the
// constructor in a block:
std::unique_ptr<Compilation> C;
{
const unsigned DriverBatchSeed = getDriverBatchSeed(*ArgList, Diags);
const Optional<unsigned> DriverBatchCount =
getDriverBatchCount(*ArgList, Diags);
const Optional<unsigned> DriverBatchSizeLimit =
getDriverBatchSizeLimit(*ArgList, Diags);
const bool SaveTemps = ArgList->hasArg(options::OPT_save_temps);
const bool ShowDriverTimeCompilation =
ArgList->hasArg(options::OPT_driver_time_compilation);
std::unique_ptr<UnifiedStatsReporter> StatsReporter =
createStatsReporter(ArgList.get(), Inputs, OI, DefaultTargetTriple);
const bool EnableExperimentalDependencies =
ArgList->hasArg(options::OPT_enable_experimental_dependencies);
const bool VerifyExperimentalDependencyGraphAfterEveryImport = ArgList->hasArg(
options::
OPT_driver_verify_experimental_dependency_graph_after_every_import);
const bool EmitExperimentalDependencyDotFileAfterEveryImport = ArgList->hasArg(
options::
OPT_driver_emit_experimental_dependency_dot_file_after_every_import);
const bool ExperimentalDependenciesIncludeIntrafileOnes = ArgList->hasArg(
options::OPT_experimental_dependency_include_intrafile);
// clang-format off
C = llvm::make_unique<Compilation>(
Diags, TC, OI, Level,
std::move(ArgList),
std::move(TranslatedArgList),
std::move(Inputs),
buildRecordPath,
outputBuildRecordForModuleOnlyBuild,
ArgsHash,
StartTime,
LastBuildTime,
DriverFilelistThreshold,
Incremental,
BatchMode,
DriverBatchSeed,
DriverBatchCount,
DriverBatchSizeLimit,
SaveTemps,
ShowDriverTimeCompilation,
std::move(StatsReporter),
EnableExperimentalDependencies,
VerifyExperimentalDependencyGraphAfterEveryImport,
EmitExperimentalDependencyDotFileAfterEveryImport,
ExperimentalDependenciesIncludeIntrafileOnes);
// clang-format on
}
// Construct the graph of Actions.
SmallVector<const Action *, 8> TopLevelActions;
buildActions(TopLevelActions, TC, OI,
rebuildEverything ? nullptr : &outOfDateMap, *C);
if (Diags.hadAnyError())
return nullptr;
if (DriverPrintActions) {
printActions(*C);
return nullptr;
}
buildJobs(TopLevelActions, OI, OFM ? OFM.getPointer() : nullptr,
workingDirectory, TC, *C);
if (DriverPrintDerivedOutputFileMap) {
C->getDerivedOutputFileMap().dump(llvm::outs(), true);
return nullptr;
}
// For getting bulk fixits, or for when users explicitly request to continue
// building despite errors.
if (ContinueBuildingAfterErrors)
C->setContinueBuildingAfterErrors();
if (ShowIncrementalBuildDecisions || ShowJobLifecycle)
C->setShowIncrementalBuildDecisions();
if (ShowJobLifecycle)
C->setShowJobLifecycle();
// This has to happen after building jobs, because otherwise we won't even
// emit .swiftdeps files for the next build.
if (rebuildEverything)
C->disableIncrementalBuild();
if (Diags.hadAnyError())
return nullptr;
if (DriverPrintBindings)
return nullptr;
return C;
}
static Arg *makeInputArg(const DerivedArgList &Args, OptTable &Opts,
StringRef Value) {
Arg *A = new Arg(Opts.getOption(options::OPT_INPUT), Value,
Args.getBaseArgs().MakeIndex(Value), Value.data());
A->claim();
return A;
}
using RemainingArgsHandler = llvm::function_ref<void(InputArgList &, unsigned)>;
std::unique_ptr<InputArgList>
parseArgsUntil(const llvm::opt::OptTable& Opts,
const char *const *ArgBegin,
const char *const *ArgEnd,
unsigned &MissingArgIndex,
unsigned &MissingArgCount,
unsigned FlagsToInclude,
unsigned FlagsToExclude,
llvm::opt::OptSpecifier UntilOption,
RemainingArgsHandler RemainingHandler) {
auto Args = llvm::make_unique<InputArgList>(ArgBegin, ArgEnd);
// FIXME: Handle '@' args (or at least error on them).
bool CheckUntil = UntilOption != options::OPT_INVALID;
MissingArgIndex = MissingArgCount = 0;
unsigned Index = 0, End = ArgEnd - ArgBegin;
while (Index < End) {
// Ignore empty arguments (other things may still take them as arguments).
StringRef Str = Args->getArgString(Index);
if (Str == "") {
++Index;
continue;
}
unsigned Prev = Index;
Arg *A = Opts.ParseOneArg(*Args, Index, FlagsToInclude, FlagsToExclude);
assert(Index > Prev && "Parser failed to consume argument.");
// Check for missing argument error.
if (!A) {
assert(Index >= End && "Unexpected parser error.");
assert(Index - Prev - 1 && "No missing arguments!");
MissingArgIndex = Prev;
MissingArgCount = Index - Prev - 1;
break;
}
Args->append(A);
if (CheckUntil && A->getOption().matches(UntilOption)) {
if (Index < End)
RemainingHandler(*Args, Index);
return Args;
}
}
return Args;
}
// Parse all args until we see an input, and then collect the remaining
// arguments into a synthesized "--" option.
static std::unique_ptr<InputArgList>
parseArgStringsForInteractiveDriver(const llvm::opt::OptTable& Opts,
ArrayRef<const char *> Args,
unsigned &MissingArgIndex,
unsigned &MissingArgCount,
unsigned FlagsToInclude,
unsigned FlagsToExclude) {
return parseArgsUntil(Opts, Args.begin(), Args.end(), MissingArgIndex,
MissingArgCount, FlagsToInclude, FlagsToExclude,
options::OPT_INPUT,
[&](InputArgList &Args, unsigned NextIndex) {
assert(NextIndex < Args.getNumInputArgStrings());
// Synthesize -- remaining args...
Arg *Remaining =
new Arg(Opts.getOption(options::OPT__DASH_DASH), "--", NextIndex);
for (unsigned N = Args.getNumInputArgStrings(); NextIndex != N;
++NextIndex) {
Remaining->getValues().push_back(Args.getArgString(NextIndex));
}
Args.append(Remaining);
});
}
std::unique_ptr<InputArgList>
Driver::parseArgStrings(ArrayRef<const char *> Args) {
unsigned IncludedFlagsBitmask = 0;
unsigned ExcludedFlagsBitmask = options::NoDriverOption;
unsigned MissingArgIndex, MissingArgCount;
std::unique_ptr<InputArgList> ArgList;
if (driverKind == DriverKind::Interactive) {
ArgList = parseArgStringsForInteractiveDriver(getOpts(), Args,
MissingArgIndex, MissingArgCount, IncludedFlagsBitmask,
ExcludedFlagsBitmask);
} else {
ArgList = llvm::make_unique<InputArgList>(
getOpts().ParseArgs(Args, MissingArgIndex, MissingArgCount,
IncludedFlagsBitmask, ExcludedFlagsBitmask));
}
assert(ArgList && "no argument list");
// Check for missing argument error.
if (MissingArgCount) {
Diags.diagnose(SourceLoc(), diag::error_missing_arg_value,
ArgList->getArgString(MissingArgIndex), MissingArgCount);
return nullptr;
}
// Check for unknown arguments.
for (const Arg *A : ArgList->filtered(options::OPT_UNKNOWN)) {
Diags.diagnose(SourceLoc(), diag::error_unknown_arg,
A->getAsString(*ArgList));
}
// Check for unsupported options
unsigned UnsupportedFlag = 0;
if (driverKind == DriverKind::Interactive)
UnsupportedFlag = options::NoInteractiveOption;
else if (driverKind == DriverKind::Batch)
UnsupportedFlag = options::NoBatchOption;
if (UnsupportedFlag)
for (const Arg *A : *ArgList)
if (A->getOption().hasFlag(UnsupportedFlag))
Diags.diagnose(SourceLoc(), diag::error_unsupported_option,
ArgList->getArgString(A->getIndex()), Name,
UnsupportedFlag == options::NoBatchOption ? "swift" : "swiftc");
return ArgList;
}
DerivedArgList *
Driver::translateInputAndPathArgs(const InputArgList &ArgList,
StringRef workingDirectory) const {
DerivedArgList *DAL = new DerivedArgList(ArgList);
auto addPath = [workingDirectory, DAL](Arg *A) {
assert(A->getNumValues() == 1 && "multiple values not handled");
StringRef path = A->getValue();
if (workingDirectory.empty() || path == "-" ||
llvm::sys::path::is_absolute(path)) {
DAL->append(A);
return;
}
SmallString<64> fullPath{workingDirectory};
llvm::sys::path::append(fullPath, path);
unsigned index = DAL->getBaseArgs().MakeIndex(fullPath);
Arg *newArg = new Arg(A->getOption(), A->getSpelling(), index,
DAL->getBaseArgs().getArgString(index), A);
DAL->AddSynthesizedArg(newArg);
DAL->append(newArg);
};
for (Arg *A : ArgList) {
if (A->getOption().hasFlag(options::ArgumentIsPath) ||
A->getOption().matches(options::OPT_INPUT)) {
addPath(A);
continue;
}
// If we're not in immediate mode, pick up inputs via the -- option.
if (driverKind != DriverKind::Interactive && A->getOption().matches(options::OPT__DASH_DASH)) {
A->claim();
for (unsigned i = 0, e = A->getNumValues(); i != e; ++i) {
addPath(makeInputArg(*DAL, *Opts, A->getValue(i)));
}
continue;
}
DAL->append(A);
}
return DAL;
}
/// Check that the file referenced by \p Input exists. If it doesn't,
/// issue a diagnostic and return false.
static bool checkInputExistence(const Driver &D, const DerivedArgList &Args,
DiagnosticEngine &Diags, StringRef Input) {
if (!D.getCheckInputFilesExist())
return true;
// stdin always exists.
if (Input == "-")
return true;
if (llvm::sys::fs::exists(Input))
return true;
Diags.diagnose(SourceLoc(), diag::error_no_such_file_or_directory, Input);
return false;
}
void Driver::buildInputs(const ToolChain &TC,
const DerivedArgList &Args,
InputFileList &Inputs) const {
llvm::DenseMap<StringRef, StringRef> SourceFileNames;
for (Arg *A : Args) {
if (A->getOption().getKind() == Option::InputClass) {
StringRef Value = A->getValue();
file_types::ID Ty = file_types::TY_INVALID;
// stdin must be handled specially.
if (Value.equals("-")) {
// By default, treat stdin as Swift input.
Ty = file_types::TY_Swift;
} else {
// Otherwise lookup by extension.
Ty = TC.lookupTypeForExtension(llvm::sys::path::extension(Value));
if (Ty == file_types::TY_INVALID) {
// By default, treat inputs with no extension, or with an
// extension that isn't recognized, as object files.
Ty = file_types::TY_Object;
}
}
if (checkInputExistence(*this, Args, Diags, Value))
Inputs.push_back(std::make_pair(Ty, A));
if (Ty == file_types::TY_Swift) {
StringRef Basename = llvm::sys::path::filename(Value);
if (!SourceFileNames.insert({Basename, Value}).second) {
Diags.diagnose(SourceLoc(), diag::error_two_files_same_name,
Basename, SourceFileNames[Basename], Value);
Diags.diagnose(SourceLoc(), diag::note_explain_two_files_same_name);
}
}
}
}
}
static bool maybeBuildingExecutable(const OutputInfo &OI,
const DerivedArgList &Args,
const InputFileList &Inputs) {
switch (OI.LinkAction) {
case LinkKind::Executable:
return true;
case LinkKind::DynamicLibrary:
return false;
case LinkKind::None:
break;
}
if (Args.hasArg(options::OPT_parse_as_library, options::OPT_parse_stdlib))
return false;
return Inputs.size() == 1;
}
static void diagnoseOutputModeArg(DiagnosticEngine &diags, const Arg *arg,
bool hasInputs, const DerivedArgList &args,
bool isInteractiveDriver,
StringRef driverName) {
switch (arg->getOption().getID()) {
case options::OPT_i:
diags.diagnose(SourceLoc(), diag::error_i_mode,
isInteractiveDriver ? driverName : "swift");
break;
case options::OPT_repl:
if (isInteractiveDriver && !hasInputs)
diags.diagnose(SourceLoc(), diag::warning_unnecessary_repl_mode,
args.getArgString(arg->getIndex()), driverName);
break;
default:
break;
}
}
static bool isSDKTooOld(StringRef sdkPath, llvm::VersionTuple minVersion,
StringRef firstPrefix, StringRef secondPrefix = {}) {
// FIXME: This is a hack.
// We should be looking at the SDKSettings.plist.
StringRef sdkDirName = llvm::sys::path::filename(sdkPath);
size_t versionStart = sdkDirName.rfind(firstPrefix);
if (versionStart != StringRef::npos) {
versionStart += firstPrefix.size();
} else if (!secondPrefix.empty()) {
versionStart = sdkDirName.rfind(secondPrefix);
if (versionStart != StringRef::npos)
versionStart += secondPrefix.size();
}
if (versionStart == StringRef::npos)
return false;
size_t versionEnd = sdkDirName.rfind(".Internal");
if (versionEnd == StringRef::npos)
versionEnd = sdkDirName.rfind(".sdk");
if (versionEnd == StringRef::npos)
return false;
llvm::VersionTuple version;
if (version.tryParse(sdkDirName.slice(versionStart, versionEnd)))
return false;
return version < minVersion;
}
/// Returns true if the given SDK path points to an SDK that is too old for
/// the given target.
static bool isSDKTooOld(StringRef sdkPath, const llvm::Triple &target) {
if (target.isMacOSX()) {
return isSDKTooOld(sdkPath, llvm::VersionTuple(10, 14), "OSX");
} else if (target.isiOS()) {
// Includes both iOS and TVOS.
return isSDKTooOld(sdkPath, llvm::VersionTuple(12, 0), "Simulator", "OS");
} else if (target.isWatchOS()) {
return isSDKTooOld(sdkPath, llvm::VersionTuple(5, 0), "Simulator", "OS");
} else {
return false;
}
}
void Driver::buildOutputInfo(const ToolChain &TC, const DerivedArgList &Args,
const bool BatchMode, const InputFileList &Inputs,
OutputInfo &OI) const {
// By default, the driver does not link its output; this will be updated
// appropriately below if linking is required.
OI.CompilerOutputType = driverKind == DriverKind::Interactive
? file_types::TY_Nothing
: file_types::TY_Object;
if (const Arg *A = Args.getLastArg(options::OPT_num_threads)) {
if (BatchMode) {
Diags.diagnose(SourceLoc(), diag::warning_cannot_multithread_batch_mode);
} else if (StringRef(A->getValue()).getAsInteger(10, OI.numThreads)) {
Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value,
A->getAsString(Args), A->getValue());
}
}
const Arg *const OutputModeArg = Args.getLastArg(options::OPT_modes_Group);
if (!OutputModeArg) {
if (Args.hasArg(options::OPT_emit_module, options::OPT_emit_module_path)) {
OI.CompilerOutputType = file_types::TY_SwiftModuleFile;
} else if (driverKind != DriverKind::Interactive) {
OI.LinkAction = LinkKind::Executable;
}
} else {
diagnoseOutputModeArg(Diags, OutputModeArg, !Inputs.empty(), Args,
driverKind == DriverKind::Interactive, Name);
switch (OutputModeArg->getOption().getID()) {
case options::OPT_emit_executable:
OI.LinkAction = LinkKind::Executable;
OI.CompilerOutputType = file_types::TY_Object;
break;
case options::OPT_emit_library:
OI.LinkAction = LinkKind::DynamicLibrary;
OI.CompilerOutputType = file_types::TY_Object;
break;
case options::OPT_emit_object:
OI.CompilerOutputType = file_types::TY_Object;
break;
case options::OPT_emit_assembly:
OI.CompilerOutputType = file_types::TY_Assembly;
break;
case options::OPT_emit_sil:
OI.CompilerOutputType = file_types::TY_SIL;
break;
case options::OPT_emit_silgen:
OI.CompilerOutputType = file_types::TY_RawSIL;
break;
case options::OPT_emit_sib:
OI.CompilerOutputType = file_types::TY_SIB;
break;
case options::OPT_emit_sibgen:
OI.CompilerOutputType = file_types::TY_RawSIB;
break;
case options::OPT_emit_ir:
OI.CompilerOutputType = file_types::TY_LLVM_IR;
break;
case options::OPT_emit_bc:
OI.CompilerOutputType = file_types::TY_LLVM_BC;
break;
case options::OPT_dump_ast:
OI.CompilerOutputType = file_types::TY_ASTDump;
break;
case options::OPT_emit_pch:
OI.CompilerMode = OutputInfo::Mode::SingleCompile;
OI.CompilerOutputType = file_types::TY_PCH;
break;
case options::OPT_emit_imported_modules:
OI.CompilerOutputType = file_types::TY_ImportedModules;
// We want the imported modules from the module as a whole, not individual
// files, so let's do it in one invocation rather than having to collate
// later.
OI.CompilerMode = OutputInfo::Mode::SingleCompile;
break;
case options::OPT_index_file:
OI.CompilerMode = OutputInfo::Mode::SingleCompile;
OI.CompilerOutputType = file_types::TY_IndexData;
break;
case options::OPT_update_code:
OI.CompilerOutputType = file_types::TY_Remapping;
OI.LinkAction = LinkKind::None;
break;
case options::OPT_parse:
case options::OPT_resolve_imports:
case options::OPT_typecheck:
case options::OPT_dump_parse:
case options::OPT_emit_syntax:
case options::OPT_print_ast:
case options::OPT_dump_type_refinement_contexts:
case options::OPT_dump_scope_maps:
case options::OPT_dump_interface_hash:
case options::OPT_dump_type_info:
case options::OPT_verify_debug_info:
OI.CompilerOutputType = file_types::TY_Nothing;
break;
case options::OPT_i:
// Keep the default output/mode; this flag was removed and should already
// have been diagnosed above.
assert(Diags.hadAnyError() && "-i flag was removed");
break;
case options::OPT_repl:
case options::OPT_deprecated_integrated_repl:
case options::OPT_lldb_repl:
OI.CompilerOutputType = file_types::TY_Nothing;
OI.CompilerMode = OutputInfo::Mode::REPL;
break;
default:
llvm_unreachable("unknown mode");
}
}
assert(OI.CompilerOutputType != file_types::ID::TY_INVALID);
if (const Arg *A = Args.getLastArg(options::OPT_g_Group)) {
if (A->getOption().matches(options::OPT_g))
OI.DebugInfoLevel = IRGenDebugInfoLevel::Normal;
else if (A->getOption().matches(options::OPT_gline_tables_only))
OI.DebugInfoLevel = IRGenDebugInfoLevel::LineTables;
else if (A->getOption().matches(options::OPT_gdwarf_types))
OI.DebugInfoLevel = IRGenDebugInfoLevel::DwarfTypes;
else
assert(A->getOption().matches(options::OPT_gnone) &&
"unknown -g<kind> option");
}
if (const Arg *A = Args.getLastArg(options::OPT_debug_info_format)) {
if (strcmp(A->getValue(), "dwarf") == 0)
OI.DebugInfoFormat = IRGenDebugInfoFormat::DWARF;
else if (strcmp(A->getValue(), "codeview") == 0)
OI.DebugInfoFormat = IRGenDebugInfoFormat::CodeView;
else
Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value,
A->getAsString(Args), A->getValue());
} else if (OI.DebugInfoLevel > IRGenDebugInfoLevel::None) {
// If -g was specified but not -debug-info-format, DWARF is assumed.
OI.DebugInfoFormat = IRGenDebugInfoFormat::DWARF;
}
if (Args.hasArg(options::OPT_debug_info_format) &&
!Args.hasArg(options::OPT_g_Group)) {
const Arg *debugFormatArg = Args.getLastArg(options::OPT_debug_info_format);
Diags.diagnose(SourceLoc(), diag::error_option_missing_required_argument,
debugFormatArg->getAsString(Args), "-g");
}
if (OI.DebugInfoFormat == IRGenDebugInfoFormat::CodeView &&
(OI.DebugInfoLevel == IRGenDebugInfoLevel::LineTables ||
OI.DebugInfoLevel == IRGenDebugInfoLevel::DwarfTypes)) {
const Arg *debugFormatArg = Args.getLastArg(options::OPT_debug_info_format);
Diags.diagnose(SourceLoc(), diag::error_argument_not_allowed_with,
debugFormatArg->getAsString(Args),
OI.DebugInfoLevel == IRGenDebugInfoLevel::LineTables
? "-gline-tables-only"
: "-gdwarf_types");
}
if (Args.hasArg(options::OPT_emit_module, options::OPT_emit_module_path)) {
// The user has requested a module, so generate one and treat it as
// top-level output.
OI.ShouldGenerateModule = true;
OI.ShouldTreatModuleAsTopLevelOutput = true;
} else if (OI.DebugInfoLevel > IRGenDebugInfoLevel::LineTables &&
OI.shouldLink()) {
// An option has been passed which requires a module, but the user hasn't
// requested one. Generate a module, but treat it as an intermediate output.
OI.ShouldGenerateModule = true;
OI.ShouldTreatModuleAsTopLevelOutput = false;
} else if (Args.hasArg(options::OPT_emit_objc_header,
options::OPT_emit_objc_header_path,
options::OPT_emit_parseable_module_interface,
options::OPT_emit_parseable_module_interface_path) &&
OI.CompilerMode != OutputInfo::Mode::SingleCompile) {
// An option has been passed which requires whole-module knowledge, but we
// don't have that. Generate a module, but treat it as an intermediate
// output.
OI.ShouldGenerateModule = true;
OI.ShouldTreatModuleAsTopLevelOutput = false;
} else {
// No options require a module, so don't generate one.
OI.ShouldGenerateModule = false;
OI.ShouldTreatModuleAsTopLevelOutput = false;
}
if (OI.ShouldGenerateModule &&
(OI.CompilerMode == OutputInfo::Mode::REPL ||
OI.CompilerMode == OutputInfo::Mode::Immediate)) {
Diags.diagnose(SourceLoc(), diag::error_mode_cannot_emit_module);
return;
}
if (const Arg *A = Args.getLastArg(options::OPT_module_name)) {
OI.ModuleName = A->getValue();
} else if (OI.CompilerMode == OutputInfo::Mode::REPL) {
// REPL mode should always use the REPL module.
OI.ModuleName = "REPL";
} else if (const Arg *A = Args.getLastArg(options::OPT_o)) {
OI.ModuleName = llvm::sys::path::stem(A->getValue());
if (OI.LinkAction == LinkKind::DynamicLibrary &&
!llvm::sys::path::extension(A->getValue()).empty() &&
StringRef(OI.ModuleName).startswith("lib")) {
// Chop off a "lib" prefix if we're building a library.
OI.ModuleName.erase(0, strlen("lib"));
}
} else if (Inputs.size() == 1) {
OI.ModuleName = llvm::sys::path::stem(Inputs.front().second->getValue());
}
if (!Lexer::isIdentifier(OI.ModuleName) ||
(OI.ModuleName == STDLIB_NAME &&
!Args.hasArg(options::OPT_parse_stdlib))) {
OI.ModuleNameIsFallback = true;
if (OI.CompilerOutputType == file_types::TY_Nothing ||
maybeBuildingExecutable(OI, Args, Inputs))
OI.ModuleName = "main";
else if (!Inputs.empty() || OI.CompilerMode == OutputInfo::Mode::REPL) {
// Having an improper module name is only bad if we have inputs or if
// we're in REPL mode.
auto DID = (OI.ModuleName == STDLIB_NAME) ? diag::error_stdlib_module_name
: diag::error_bad_module_name;
Diags.diagnose(SourceLoc(), DID,
OI.ModuleName, !Args.hasArg(options::OPT_module_name));
OI.ModuleName = "__bad__";
}
}
{
if (const Arg *A = Args.getLastArg(options::OPT_sdk)) {
OI.SDKPath = A->getValue();
} else if (const char *SDKROOT = getenv("SDKROOT")) {
OI.SDKPath = SDKROOT;
} else if (OI.CompilerMode == OutputInfo::Mode::Immediate ||
OI.CompilerMode == OutputInfo::Mode::REPL) {
if (TC.getTriple().isMacOSX()) {
// In immediate modes, use the SDK provided by xcrun.
// This will prefer the SDK alongside the Swift found by "xcrun swift".
// We don't do this in compilation modes because defaulting to the
// latest SDK may not be intended.
auto xcrunPath = llvm::sys::findProgramByName("xcrun");
if (!xcrunPath.getError()) {
const char *args[] = {
"--show-sdk-path", "--sdk", "macosx", nullptr
};
sys::TaskQueue queue;
queue.addTask(xcrunPath->c_str(), args);
queue.execute(nullptr,
[&OI](sys::ProcessId PID, int returnCode,
StringRef output, StringRef errors,
sys::TaskProcessInformation ProcInfo,
void *unused) -> sys::TaskFinishedResponse {
if (returnCode == 0) {
output = output.rtrim();
auto lastLineStart = output.find_last_of("\n\r");
if (lastLineStart != StringRef::npos)
output = output.substr(lastLineStart+1);
if (output.empty())
OI.SDKPath = "/";
else
OI.SDKPath = output.str();
}
return sys::TaskFinishedResponse::ContinueExecution;
});
}
}
}
if (!OI.SDKPath.empty()) {
// Delete a trailing /.
if (OI.SDKPath.size() > 1 &&
llvm::sys::path::is_separator(OI.SDKPath.back())) {
OI.SDKPath.erase(OI.SDKPath.end()-1);
}
if (!llvm::sys::fs::exists(OI.SDKPath)) {
Diags.diagnose(SourceLoc(), diag::warning_no_such_sdk, OI.SDKPath);
} else if (isSDKTooOld(OI.SDKPath, TC.getTriple())) {
Diags.diagnose(SourceLoc(), diag::error_sdk_too_old,
llvm::sys::path::filename(OI.SDKPath));
}
}
}
if (const Arg *A = Args.getLastArg(options::OPT_sanitize_EQ))
OI.SelectedSanitizers = parseSanitizerArgValues(
Args, A, TC.getTriple(), Diags,
[&](StringRef sanitizerName, bool shared) {
return TC.sanitizerRuntimeLibExists(Args, sanitizerName, shared);
});
if (const Arg *A = Args.getLastArg(options::OPT_sanitize_coverage_EQ)) {
// Check that the sanitizer coverage flags are supported if supplied.
// Dismiss the output, as we will grab the value later.
(void)parseSanitizerCoverageArgValue(A, TC.getTriple(), Diags,
OI.SelectedSanitizers);
}
}
OutputInfo::Mode
Driver::computeCompilerMode(const DerivedArgList &Args,
const InputFileList &Inputs,
bool &BatchModeOut) const {
if (driverKind == Driver::DriverKind::Interactive)
return Inputs.empty() ? OutputInfo::Mode::REPL
: OutputInfo::Mode::Immediate;
const Arg *ArgRequiringSingleCompile = Args.getLastArg(
options::OPT_whole_module_optimization, options::OPT_index_file);
BatchModeOut = Args.hasFlag(options::OPT_enable_batch_mode,
options::OPT_disable_batch_mode,
false);
if (!ArgRequiringSingleCompile)
return OutputInfo::Mode::StandardCompile;
// Override batch mode if given -wmo or -index-file.
if (BatchModeOut) {
BatchModeOut = false;
// Emit a warning about such overriding (FIXME: we might conditionalize
// this based on the user or xcode passing -disable-batch-mode).
Diags.diagnose(SourceLoc(), diag::warn_ignoring_batch_mode,
ArgRequiringSingleCompile->getOption().getPrefixedName());
}
return OutputInfo::Mode::SingleCompile;
}
void Driver::buildActions(SmallVectorImpl<const Action *> &TopLevelActions,
const ToolChain &TC, const OutputInfo &OI,
const InputInfoMap *OutOfDateMap,
Compilation &C) const {
const DerivedArgList &Args = C.getArgs();
ArrayRef<InputPair> Inputs = C.getInputFiles();
if (!SuppressNoInputFilesError && Inputs.empty()) {
Diags.diagnose(SourceLoc(), diag::error_no_input_files);
return;
}
SmallVector<const Action *, 2> AllModuleInputs;
SmallVector<const Action *, 2> AllLinkerInputs;
switch (OI.CompilerMode) {
case OutputInfo::Mode::StandardCompile: {
// If the user is importing a textual (.h) bridging header and we're in
// standard-compile (non-WMO) mode, we take the opportunity to precompile
// the header into a temporary PCH, and replace the import argument with the
// PCH in the subsequent frontend jobs.
JobAction *PCH = nullptr;
if (Args.hasFlag(options::OPT_enable_bridging_pch,
options::OPT_disable_bridging_pch,
true)) {
if (Arg *A = Args.getLastArg(options::OPT_import_objc_header)) {
StringRef Value = A->getValue();
auto Ty = TC.lookupTypeForExtension(llvm::sys::path::extension(Value));
if (Ty == file_types::TY_ObjCHeader) {
auto *HeaderInput = C.createAction<InputAction>(*A, Ty);
StringRef PersistentPCHDir;
if (const Arg *A = Args.getLastArg(options::OPT_pch_output_dir)) {
PersistentPCHDir = A->getValue();
}
PCH = C.createAction<GeneratePCHJobAction>(HeaderInput,
PersistentPCHDir);
}
}
}
for (const InputPair &Input : Inputs) {
file_types::ID InputType = Input.first;
const Arg *InputArg = Input.second;
Action *Current = C.createAction<InputAction>(*InputArg, InputType);
switch (InputType) {
case file_types::TY_Swift:
case file_types::TY_SIL:
case file_types::TY_SIB: {
// Source inputs always need to be compiled.
assert(file_types::isPartOfSwiftCompilation(InputType));
CompileJobAction::InputInfo previousBuildState = {
CompileJobAction::InputInfo::NeedsCascadingBuild,
llvm::sys::TimePoint<>::min()
};
if (OutOfDateMap)
previousBuildState = OutOfDateMap->lookup(InputArg);
if (Args.hasArg(options::OPT_embed_bitcode)) {
Current = C.createAction<CompileJobAction>(
Current, file_types::TY_LLVM_BC, previousBuildState);
if (PCH)
cast<JobAction>(Current)->addInput(PCH);
AllModuleInputs.push_back(Current);
Current = C.createAction<BackendJobAction>(Current,
OI.CompilerOutputType, 0);
} else {
Current = C.createAction<CompileJobAction>(Current,
OI.CompilerOutputType,
previousBuildState);
if (PCH)
cast<JobAction>(Current)->addInput(PCH);
AllModuleInputs.push_back(Current);
}
AllLinkerInputs.push_back(Current);
break;
}
case file_types::TY_SwiftModuleFile:
case file_types::TY_SwiftModuleDocFile:
if (OI.ShouldGenerateModule && !OI.shouldLink()) {
// When generating a .swiftmodule as a top-level output (as opposed
// to, for example, linking an image), treat .swiftmodule files as
// inputs to a MergeModule action.
AllModuleInputs.push_back(Current);
break;
} else if (OI.shouldLink()) {
// Otherwise, if linking, pass .swiftmodule files as inputs to the
// linker, so that their debug info is available.
AllLinkerInputs.push_back(Current);
break;
} else {
Diags.diagnose(SourceLoc(), diag::error_unexpected_input_file,
InputArg->getValue());
continue;
}
case file_types::TY_AutolinkFile:
case file_types::TY_Object:
// Object inputs are only okay if linking.
if (OI.shouldLink()) {
AllLinkerInputs.push_back(Current);
break;
}
LLVM_FALLTHROUGH;
case file_types::TY_ASTDump:
case file_types::TY_Image:
case file_types::TY_dSYM:
case file_types::TY_Dependencies:
case file_types::TY_Assembly:
case file_types::TY_LLVM_IR:
case file_types::TY_LLVM_BC:
case file_types::TY_SerializedDiagnostics:
case file_types::TY_ObjCHeader:
case file_types::TY_ClangModuleFile:
case file_types::TY_SwiftDeps:
case file_types::TY_Remapping:
case file_types::TY_IndexData:
case file_types::TY_PCH:
case file_types::TY_ImportedModules:
case file_types::TY_TBD:
case file_types::TY_ModuleTrace:
case file_types::TY_OptRecord:
case file_types::TY_SwiftParseableInterfaceFile:
// We could in theory handle assembly or LLVM input, but let's not.
// FIXME: What about LTO?
Diags.diagnose(SourceLoc(), diag::error_unexpected_input_file,
InputArg->getValue());
continue;
case file_types::TY_RawSIB:
case file_types::TY_RawSIL:
case file_types::TY_Nothing:
case file_types::TY_INVALID:
llvm_unreachable("these types should never be inferred");
}
}
break;
}
case OutputInfo::Mode::SingleCompile: {
if (Inputs.empty()) break;
if (Args.hasArg(options::OPT_embed_bitcode)) {
// Make sure we can handle the inputs.
bool HandledHere = true;
for (const InputPair &Input : Inputs) {
file_types::ID InputType = Input.first;
if (!file_types::isPartOfSwiftCompilation(InputType)) {
HandledHere = false;
break;
}
}
if (HandledHere) {
// Create a single CompileJobAction and a single BackendJobAction.
JobAction *CA =
C.createAction<CompileJobAction>(file_types::TY_LLVM_BC);
AllModuleInputs.push_back(CA);
int InputIndex = 0;
for (const InputPair &Input : Inputs) {
file_types::ID InputType = Input.first;
const Arg *InputArg = Input.second;
CA->addInput(C.createAction<InputAction>(*InputArg, InputType));
if (OI.isMultiThreading()) {
// With multi-threading we need a backend job for each output file
// of the compilation.
auto *BJA = C.createAction<BackendJobAction>(CA,
OI.CompilerOutputType,
InputIndex);
AllLinkerInputs.push_back(BJA);
}
InputIndex++;
}
if (!OI.isMultiThreading()) {
// No multi-threading: the compilation only produces a single output
// file.
CA = C.createAction<BackendJobAction>(CA, OI.CompilerOutputType, 0);
AllLinkerInputs.push_back(CA);
}
break;
}
}
// Create a single CompileJobAction for all of the driver's inputs.
auto *CA = C.createAction<CompileJobAction>(OI.CompilerOutputType);
for (const InputPair &Input : Inputs) {
file_types::ID InputType = Input.first;
const Arg *InputArg = Input.second;
CA->addInput(C.createAction<InputAction>(*InputArg, InputType));
}
AllModuleInputs.push_back(CA);
AllLinkerInputs.push_back(CA);
break;
}
case OutputInfo::Mode::BatchModeCompile: {
llvm_unreachable("Batch mode should not be used to build actions");
}
case OutputInfo::Mode::Immediate: {
if (Inputs.empty())
return;
assert(OI.CompilerOutputType == file_types::TY_Nothing);
auto *CA = C.createAction<InterpretJobAction>();
for (const InputPair &Input : Inputs) {
file_types::ID InputType = Input.first;
const Arg *InputArg = Input.second;
CA->addInput(C.createAction<InputAction>(*InputArg, InputType));
}
TopLevelActions.push_back(CA);
return;
}
case OutputInfo::Mode::REPL: {
if (!Inputs.empty()) {
// REPL mode requires no inputs.
Diags.diagnose(SourceLoc(), diag::error_repl_requires_no_input_files);
return;
}
REPLJobAction::Mode Mode = REPLJobAction::Mode::PreferLLDB;
if (const Arg *A = Args.getLastArg(options::OPT_lldb_repl,
options::OPT_deprecated_integrated_repl)) {
if (A->getOption().matches(options::OPT_lldb_repl))
Mode = REPLJobAction::Mode::RequireLLDB;
else
Mode = REPLJobAction::Mode::Integrated;
}
TopLevelActions.push_back(C.createAction<REPLJobAction>(Mode));
return;
}
}
JobAction *MergeModuleAction = nullptr;
if (OI.ShouldGenerateModule &&
OI.CompilerMode != OutputInfo::Mode::SingleCompile &&
!AllModuleInputs.empty()) {
// We're performing multiple compilations; set up a merge module step
// so we generate a single swiftmodule as output.
MergeModuleAction = C.createAction<MergeModuleJobAction>(AllModuleInputs);
}
if (OI.shouldLink() && !AllLinkerInputs.empty()) {
auto *LinkAction = C.createAction<LinkJobAction>(AllLinkerInputs,
OI.LinkAction);
// On ELF platforms there's no built in autolinking mechanism, so we
// pull the info we need from the .o files directly and pass them as an
// argument input file to the linker.
SmallVector<const Action *, 2> AutolinkExtractInputs;
for (const Action *A : AllLinkerInputs)
if (A->getType() == file_types::TY_Object)
AutolinkExtractInputs.push_back(A);
if (!AutolinkExtractInputs.empty() &&
(TC.getTriple().getObjectFormat() == llvm::Triple::ELF ||
TC.getTriple().isOSCygMing())) {
auto *AutolinkExtractAction =
C.createAction<AutolinkExtractJobAction>(AutolinkExtractInputs);
// Takes the same inputs as the linker...
// ...and gives its output to the linker.
LinkAction->addInput(AutolinkExtractAction);
}
if (MergeModuleAction) {
if (OI.DebugInfoLevel == IRGenDebugInfoLevel::Normal) {
if (TC.getTriple().getObjectFormat() == llvm::Triple::ELF ||
TC.getTriple().getObjectFormat() == llvm::Triple::COFF) {
auto *ModuleWrapAction =
C.createAction<ModuleWrapJobAction>(MergeModuleAction);
LinkAction->addInput(ModuleWrapAction);
} else {
LinkAction->addInput(MergeModuleAction);
}
// FIXME: Adding the MergeModuleAction as top-level regardless would
// allow us to get rid of the special case flag for that.
} else {
TopLevelActions.push_back(MergeModuleAction);
}
}
TopLevelActions.push_back(LinkAction);
if (TC.getTriple().isOSDarwin() &&
OI.DebugInfoLevel > IRGenDebugInfoLevel::None) {
auto *dSYMAction = C.createAction<GenerateDSYMJobAction>(LinkAction);
TopLevelActions.push_back(dSYMAction);
if (Args.hasArg(options::OPT_verify_debug_info)) {
TopLevelActions.push_back(
C.createAction<VerifyDebugInfoJobAction>(dSYMAction));
}
}
} else {
// We can't rely on the merge module action being the only top-level
// action that needs to run. There may be other actions (e.g.
// BackendJobActions) that are not merge-module inputs but should be run
// anyway.
if (MergeModuleAction)
TopLevelActions.push_back(MergeModuleAction);
TopLevelActions.append(AllLinkerInputs.begin(), AllLinkerInputs.end());
}
}
bool Driver::handleImmediateArgs(const ArgList &Args, const ToolChain &TC) {
if (Args.hasArg(options::OPT_help)) {
printHelp(false);
return false;
}
if (Args.hasArg(options::OPT_help_hidden)) {
printHelp(true);
return false;
}
if (Args.hasArg(options::OPT_version)) {
// Follow gcc/clang behavior and use stdout for --version and stderr for -v.
printVersion(TC, llvm::outs());
return false;
}
if (Args.hasArg(options::OPT_v)) {
printVersion(TC, llvm::errs());
SuppressNoInputFilesError = true;
}
if (const Arg *A = Args.getLastArg(options::OPT_driver_use_frontend_path)) {
DriverExecutable = A->getValue();
std::string commandString =
Args.getLastArgValue(options::OPT_driver_use_frontend_path);
SmallVector<StringRef, 10> commandArgs;
StringRef(commandString).split(commandArgs, ';', -1, false);
DriverExecutable = commandArgs[0];
DriverExecutableArgs.assign(std::begin(commandArgs) + 1,
std::end(commandArgs));
}
return true;
}
Optional<OutputFileMap>
Driver::buildOutputFileMap(const llvm::opt::DerivedArgList &Args,
StringRef workingDirectory) const {
const Arg *A = Args.getLastArg(options::OPT_output_file_map);
if (!A)
return None;
// TODO: perform some preflight checks to ensure the file exists.
llvm::Expected<OutputFileMap> OFM =
OutputFileMap::loadFromPath(A->getValue(), workingDirectory);
if (auto Err = OFM.takeError()) {
Diags.diagnose(SourceLoc(), diag::error_unable_to_load_output_file_map,
llvm::toString(std::move(Err)), A->getValue());
return None;
}
return *OFM;
}
void Driver::buildJobs(ArrayRef<const Action *> TopLevelActions,
const OutputInfo &OI, const OutputFileMap *OFM,
StringRef workingDirectory, const ToolChain &TC,
Compilation &C) const {
llvm::PrettyStackTraceString CrashInfo("Building compilation jobs");
const DerivedArgList &Args = C.getArgs();
JobCacheMap JobCache;
if (Args.hasArg(options::OPT_o) && !OI.shouldLink() &&
!OI.ShouldTreatModuleAsTopLevelOutput) {
bool ShouldComplain;
if (OI.isMultiThreading()) {
// Multi-threading compilation has multiple outputs unless there's only
// one input.
ShouldComplain = C.getInputFiles().size() > 1;
} else {
// Single-threaded compilation is a problem if we're compiling more than
// one file.
ShouldComplain = 1 < llvm::count_if(C.getActions(), [](const Action *A) {
return isa<CompileJobAction>(A);
});
}
if (ShouldComplain) {
Diags.diagnose(SourceLoc(),
diag::error_cannot_specify__o_for_multiple_outputs);
}
}
for (const Action *A : TopLevelActions) {
if (auto *JA = dyn_cast<JobAction>(A)) {
(void)buildJobsForAction(C, JA, OFM, workingDirectory, /*TopLevel=*/true,
JobCache);
}
}
}
/// Form a filename based on \p base in \p result, optionally setting its
/// extension to \p newExt and in \p workingDirectory.
static void formFilenameFromBaseAndExt(StringRef base, StringRef newExt,
StringRef workingDirectory,
SmallVectorImpl<char> &result) {
if (workingDirectory.empty() || llvm::sys::path::is_absolute(base)) {
result.assign(base.begin(), base.end());
} else {
assert(!base.empty() && base != "-" && "unexpected basename");
result.assign(workingDirectory.begin(), workingDirectory.end());
llvm::sys::path::append(result, base);
}
if (!newExt.empty()) {
llvm::sys::path::replace_extension(result, newExt);
}
}
static Optional<StringRef> getOutputFilenameFromPathArgOrAsTopLevel(
const OutputInfo &OI, const llvm::opt::DerivedArgList &Args,
llvm::opt::OptSpecifier PathArg, file_types::ID ExpectedOutputType,
bool TreatAsTopLevelOutput, StringRef workingDirectory,
llvm::SmallString<128> &Buffer) {
if (const Arg *A = Args.getLastArg(PathArg))
return StringRef(A->getValue());
if (TreatAsTopLevelOutput) {
if (const Arg *A = Args.getLastArg(options::OPT_o)) {
if (OI.CompilerOutputType == ExpectedOutputType)
return StringRef(A->getValue());
// Otherwise, put the file next to the top-level output.
Buffer = A->getValue();
llvm::sys::path::remove_filename(Buffer);
llvm::sys::path::append(Buffer, OI.ModuleName);
llvm::sys::path::replace_extension(
Buffer, file_types::getExtension(ExpectedOutputType));
return Buffer.str();
}
// A top-level output wasn't specified, so just output to
// <ModuleName>.<ext>.
formFilenameFromBaseAndExt(OI.ModuleName,
file_types::getExtension(ExpectedOutputType),
workingDirectory,
Buffer);
return Buffer.str();
}
return None;
}
static StringRef assignOutputName(Compilation &C, const JobAction *JA,
llvm::SmallString<128> &Buffer,
StringRef BaseName,
PreserveOnSignal ShouldPreserveOnSignal) {
// We should output to a temporary file, since we're not at the top level
// (or are generating a bridging PCH, which is currently always a temp).
StringRef Stem = llvm::sys::path::stem(BaseName);
StringRef Suffix = file_types::getExtension(JA->getType());
std::error_code EC = llvm::sys::fs::createTemporaryFile(Stem, Suffix, Buffer);
if (EC) {
C.getDiags().diagnose(SourceLoc(),
diag::error_unable_to_make_temporary_file,
EC.message());
return {};
}
C.addTemporaryFile(Buffer.str(), ShouldPreserveOnSignal);
return Buffer.str();
}
static StringRef baseNameForImage(const JobAction *JA, const OutputInfo &OI,
const llvm::Triple &Triple,
llvm::SmallString<128> &Buffer,
StringRef BaseInput, StringRef BaseName) {
if (JA->size() == 1 && OI.ModuleNameIsFallback && BaseInput != "-")
return llvm::sys::path::stem(BaseInput);
auto link = dyn_cast<LinkJobAction>(JA);
if (!link)
return BaseName;
if (link->getKind() != LinkKind::DynamicLibrary)
return BaseName;
Buffer = Triple.isOSWindows() ? "" : "lib";
Buffer.append(BaseName);
if (Triple.isOSDarwin())
Buffer.append(".dylib");
else if (Triple.isOSWindows())
Buffer.append(".dll");
else
Buffer.append(".so");
return Buffer.str();
}
static StringRef getOutputFilename(Compilation &C,
const JobAction *JA,
const TypeToPathMap *OutputMap,
StringRef workingDirectory,
bool AtTopLevel,
StringRef BaseInput,
StringRef PrimaryInput,
llvm::SmallString<128> &Buffer) {
if (JA->getType() == file_types::TY_Nothing)
return {};
// If available, check the OutputMap first.
if (OutputMap) {
auto iter = OutputMap->find(JA->getType());
if (iter != OutputMap->end())
return iter->second;
}
// Process Action-specific output-specifying options next,
// since we didn't find anything applicable in the OutputMap.
const OutputInfo &OI = C.getOutputInfo();
const llvm::opt::DerivedArgList &Args = C.getArgs();
if (isa<MergeModuleJobAction>(JA)) {
auto optFilename = getOutputFilenameFromPathArgOrAsTopLevel(
OI, Args, options::OPT_emit_module_path, file_types::TY_SwiftModuleFile,
OI.ShouldTreatModuleAsTopLevelOutput, workingDirectory, Buffer);
if (optFilename)
return *optFilename;
}
// dSYM actions are never treated as top-level.
if (isa<GenerateDSYMJobAction>(JA)) {
Buffer = PrimaryInput;
Buffer.push_back('.');
Buffer.append(file_types::getExtension(JA->getType()));
return Buffer.str();
}
auto ShouldPreserveOnSignal = PreserveOnSignal::No;
if (auto *PCHAct = dyn_cast<GeneratePCHJobAction>(JA)) {
// For a persistent PCH we don't use an output, the frontend determines
// the filename to use for the PCH.
assert(!PCHAct->isPersistentPCH());
ShouldPreserveOnSignal = PreserveOnSignal::Yes;
}
// We don't have an output from an Action-specific command line option,
// so figure one out using the defaults.
if (AtTopLevel) {
if (Arg *FinalOutput = Args.getLastArg(options::OPT_o))
return FinalOutput->getValue();
if (file_types::isTextual(JA->getType()))
return "-";
}
assert(!BaseInput.empty() &&
"A Job which produces output must have a BaseInput!");
StringRef BaseName(BaseInput);
if (isa<MergeModuleJobAction>(JA) ||
(OI.CompilerMode == OutputInfo::Mode::SingleCompile &&
!OI.isMultiThreading()) ||
JA->getType() == file_types::TY_Image)
BaseName = OI.ModuleName;
// We don't yet have a name, assign one.
if (!AtTopLevel)
return assignOutputName(C, JA, Buffer, BaseName, ShouldPreserveOnSignal);
if (JA->getType() == file_types::TY_Image) {
const llvm::Triple &Triple = C.getToolChain().getTriple();
SmallString<16> Base =
baseNameForImage(JA, OI, Triple, Buffer, BaseInput, BaseName);
formFilenameFromBaseAndExt(Base, /*newExt=*/"", workingDirectory, Buffer);
return Buffer.str();
}
StringRef Suffix = file_types::getExtension(JA->getType());
assert(Suffix.data() &&
"All types used for output should have a suffix.");
formFilenameFromBaseAndExt(llvm::sys::path::filename(BaseName), Suffix,
workingDirectory, Buffer);
return Buffer.str();
}
static bool hasExistingAdditionalOutput(CommandOutput &output,
file_types::ID outputType,
StringRef outputPath = StringRef()) {
auto existing = output.getAdditionalOutputForType(outputType);
if (!existing.empty()) {
assert(outputPath.empty() || outputPath == existing);
return true;
}
return false;
}
static void addAuxiliaryOutput(
Compilation &C, CommandOutput &output, file_types::ID outputType,
const TypeToPathMap *outputMap, StringRef workingDirectory,
StringRef outputPath = StringRef(),
llvm::opt::OptSpecifier requireArg = llvm::opt::OptSpecifier()) {
if (hasExistingAdditionalOutput(output, outputType, outputPath))
return;
StringRef outputMapPath;
if (outputMap) {
auto iter = outputMap->find(outputType);
if (iter != outputMap->end())
outputMapPath = iter->second;
}
if (!outputMapPath.empty()) {
// Prefer a path from the OutputMap.
output.setAdditionalOutputForType(outputType, outputMapPath);
} else if (!outputPath.empty()) {
output.setAdditionalOutputForType(outputType, outputPath);
} else if (requireArg.isValid() && !C.getArgs().getLastArg(requireArg)) {
// This auxiliary output only exists if requireArg is passed, but it
// wasn't this time.
return;
} else {
// Put the auxiliary output file next to "the" primary output file.
//
// FIXME: when we're in WMO and have multiple primary outputs, we derive the
// additional filename here from the _first_ primary output name, which
// means that in the derived OFM (in Job.cpp) the additional output will
// have a possibly-surprising name. But that's only half the problem: it
// also get associated with the first primary _input_, even when there are
// multiple primary inputs; really it should be associated with the build as
// a whole -- derived OFM input "" -- but that's a more general thing to
// fix.
llvm::SmallString<128> path;
if (output.getPrimaryOutputType() != file_types::TY_Nothing)
path = output.getPrimaryOutputFilenames()[0];
else if (!output.getBaseInput(0).empty())
path = llvm::sys::path::filename(output.getBaseInput(0));
else {
formFilenameFromBaseAndExt(C.getOutputInfo().ModuleName, /*newExt=*/"",
workingDirectory, path);
}
assert(!path.empty());
bool isTempFile = C.isTemporaryFile(path);
llvm::sys::path::replace_extension(
path, file_types::getExtension(outputType));
output.setAdditionalOutputForType(outputType, path);
if (isTempFile)
C.addTemporaryFile(path);
}
}
static void addDiagFileOutputForPersistentPCHAction(
Compilation &C, const GeneratePCHJobAction *JA, CommandOutput &output,
const TypeToPathMap *outputMap, StringRef workingDirectory) {
assert(JA->isPersistentPCH());
// For a persistent PCH we don't use an output, the frontend determines
// the filename to use for the PCH. For the diagnostics file, try to
// determine an invocation-specific path inside the directory where the
// PCH is going to be written, and fallback to a temporary file if we
// cannot determine such a path.
StringRef pchOutDir = JA->getPersistentPCHDir();
StringRef headerPath = output.getBaseInput(JA->getInputIndex());
StringRef stem = llvm::sys::path::stem(headerPath);
StringRef suffix =
file_types::getExtension(file_types::TY_SerializedDiagnostics);
SmallString<256> outPathBuf;
if (const Arg *A = C.getArgs().getLastArg(options::OPT_emit_module_path)) {
// The module file path is unique for a specific module and architecture
// (it won't be concurrently written to) so we can use the path as hash
// for determining the filename to use for the diagnostic file.
StringRef ModuleOutPath = A->getValue();
outPathBuf = pchOutDir;
llvm::sys::path::append(outPathBuf, stem);
outPathBuf += '-';
auto code = llvm::hash_value(ModuleOutPath);
outPathBuf += llvm::APInt(64, code).toString(36, /*Signed=*/false);
llvm::sys::path::replace_extension(outPathBuf, suffix);
}
if (outPathBuf.empty()) {
// Fallback to creating a temporary file.
std::error_code EC =
llvm::sys::fs::createTemporaryFile(stem, suffix, outPathBuf);
if (EC) {
C.getDiags().diagnose(SourceLoc(),
diag::error_unable_to_make_temporary_file,
EC.message());
return;
}
C.addTemporaryFile(outPathBuf.str());
}
if (!outPathBuf.empty()) {
addAuxiliaryOutput(C, output, file_types::TY_SerializedDiagnostics,
outputMap, workingDirectory, outPathBuf.str());
}
}
/// If the file at \p input has not been modified since the last build (i.e. its
/// mtime has not changed), adjust the Job's condition accordingly.
static void
handleCompileJobCondition(Job *J, CompileJobAction::InputInfo inputInfo,
StringRef input, bool alwaysRebuildDependents) {
if (inputInfo.status == CompileJobAction::InputInfo::NewlyAdded) {
J->setCondition(Job::Condition::NewlyAdded);
return;
}
bool hasValidModTime = false;
llvm::sys::fs::file_status inputStatus;
if (!llvm::sys::fs::status(input, inputStatus)) {
J->setInputModTime(inputStatus.getLastModificationTime());
hasValidModTime = true;
}
Job::Condition condition;
if (hasValidModTime && J->getInputModTime() == inputInfo.previousModTime) {
switch (inputInfo.status) {
case CompileJobAction::InputInfo::UpToDate:
if (llvm::sys::fs::exists(J->getOutput().getPrimaryOutputFilename()))
condition = Job::Condition::CheckDependencies;
else
condition = Job::Condition::RunWithoutCascading;
break;
case CompileJobAction::InputInfo::NeedsCascadingBuild:
condition = Job::Condition::Always;
break;
case CompileJobAction::InputInfo::NeedsNonCascadingBuild:
condition = Job::Condition::RunWithoutCascading;
break;
case CompileJobAction::InputInfo::NewlyAdded:
llvm_unreachable("handled above");
}
} else {
if (alwaysRebuildDependents ||
inputInfo.status == CompileJobAction::InputInfo::NeedsCascadingBuild) {
condition = Job::Condition::Always;
} else {
condition = Job::Condition::RunWithoutCascading;
}
}
J->setCondition(condition);
}
Job *Driver::buildJobsForAction(Compilation &C, const JobAction *JA,
const OutputFileMap *OFM,
StringRef workingDirectory,
bool AtTopLevel, JobCacheMap &JobCache) const {
PrettyStackTraceDriverAction CrashInfo("building jobs", JA);
// 1. See if we've already got this cached.
const ToolChain &TC = C.getToolChain();
std::pair<const Action *, const ToolChain *> Key(JA, &TC);
{
auto CacheIter = JobCache.find(Key);
if (CacheIter != JobCache.end()) {
return CacheIter->second;
}
}
// 2. Build up the list of input jobs.
SmallVector<const Action *, 4> InputActions;
SmallVector<const Job *, 4> InputJobs;
for (const Action *Input : *JA) {
if (auto *InputJobAction = dyn_cast<JobAction>(Input)) {
InputJobs.push_back(buildJobsForAction(
C, InputJobAction, OFM, workingDirectory, false, JobCache));
} else {
InputActions.push_back(Input);
}
}
// 3. Determine the CommandOutput for the job.
StringRef BaseInput;
StringRef PrimaryInput;
if (!InputActions.empty()) {
// Use the first InputAction as our BaseInput and PrimaryInput.
const InputAction *IA = cast<InputAction>(InputActions[0]);
BaseInput = IA->getInputArg().getValue();
PrimaryInput = BaseInput;
} else if (!InputJobs.empty()) {
const CommandOutput &Out = InputJobs.front()->getOutput();
size_t i = JA->getInputIndex();
// Use the first Job's BaseInput as our BaseInput.
BaseInput = Out.getBaseInput(i);
// Use the first Job's Primary Output as our PrimaryInput.
PrimaryInput = Out.getPrimaryOutputFilenames()[i];
}
// With -index-file option, the primary input is the one passed with
// -index-file-path.
// FIXME: Figure out how this better fits within the driver infrastructure.
if (JA->getType() == file_types::TY_IndexData) {
if (Arg *A = C.getArgs().getLastArg(options::OPT_index_file_path)) {
BaseInput = A->getValue();
PrimaryInput = A->getValue();
}
}
const OutputInfo &OI = C.getOutputInfo();
const TypeToPathMap *OutputMap = nullptr;
if (OFM) {
if (isa<CompileJobAction>(JA)) {
if (OI.CompilerMode == OutputInfo::Mode::SingleCompile) {
OutputMap = OFM->getOutputMapForSingleOutput();
} else {
OutputMap = OFM->getOutputMapForInput(BaseInput);
}
} else if (isa<BackendJobAction>(JA)) {
OutputMap = OFM->getOutputMapForInput(BaseInput);
}
}
std::unique_ptr<CommandOutput> Output(
new CommandOutput(JA->getType(), C.getDerivedOutputFileMap()));
PrettyStackTraceDriverCommandOutput CrashInfo2("determining output",
Output.get());
llvm::SmallString<128> Buf;
computeMainOutput(C, JA, OFM, AtTopLevel, InputActions, InputJobs, OutputMap,
workingDirectory, BaseInput, PrimaryInput, Buf,
Output.get());
if (OI.ShouldGenerateModule && isa<CompileJobAction>(JA))
chooseSwiftModuleOutputPath(C, OutputMap, workingDirectory, Output.get());
if (OI.ShouldGenerateModule &&
(isa<CompileJobAction>(JA) || isa<MergeModuleJobAction>(JA)))
chooseSwiftModuleDocOutputPath(C, OutputMap, workingDirectory,
Output.get());
if (C.getArgs().hasArg(options::OPT_emit_parseable_module_interface,
options::OPT_emit_parseable_module_interface_path))
chooseParseableInterfacePath(C, JA, workingDirectory, Buf, Output.get());
if (C.getArgs().hasArg(options::OPT_update_code) && isa<CompileJobAction>(JA))
chooseRemappingOutputPath(C, OutputMap, Output.get());
if (isa<CompileJobAction>(JA) || isa<GeneratePCHJobAction>(JA)) {
// Choose the serialized diagnostics output path.
if (C.getArgs().hasArg(options::OPT_serialize_diagnostics))
chooseSerializedDiagnosticsPath(C, JA, OutputMap, workingDirectory,
Output.get());
}
if (isa<MergeModuleJobAction>(JA) ||
(isa<CompileJobAction>(JA) &&
OI.CompilerMode == OutputInfo::Mode::SingleCompile)) {
// An emit-tbd argument gets passed down to a job that sees the whole
// module, either the -merge-modules job or a -wmo compiler invocation.
chooseTBDPath(C, OutputMap, workingDirectory, Buf, Output.get());
}
if (isa<CompileJobAction>(JA))
chooseDependenciesOutputPaths(C, OutputMap, workingDirectory, Buf,
Output.get());
if (C.getArgs().hasArg(options::OPT_save_optimization_record,
options::OPT_save_optimization_record_path))
chooseOptimizationRecordPath(C, workingDirectory, Buf, Output.get());
if ((isa<MergeModuleJobAction>(JA) ||
(isa<CompileJobAction>(JA) &&
OI.CompilerMode == OutputInfo::Mode::SingleCompile)) &&
C.getArgs().hasArg(options::OPT_emit_objc_header,
options::OPT_emit_objc_header_path))
chooseObjectiveCHeaderOutputPath(C, OutputMap, workingDirectory,
Output.get());
// 4. Construct a Job which produces the right CommandOutput.
std::unique_ptr<Job> ownedJob = TC.constructJob(*JA, C, std::move(InputJobs),
InputActions,
std::move(Output), OI);
Job *J = C.addJob(std::move(ownedJob));
// If we track dependencies for this job, we may be able to avoid running it.
if (!J->getOutput()
.getAdditionalOutputForType(file_types::TY_SwiftDeps)
.empty()) {
if (InputActions.size() == 1) {
auto compileJob = cast<CompileJobAction>(JA);
bool alwaysRebuildDependents =
C.getArgs().hasArg(options::OPT_driver_always_rebuild_dependents);
handleCompileJobCondition(J, compileJob->getInputInfo(), BaseInput,
alwaysRebuildDependents);
}
}
// 5. Add it to the JobCache, so we don't construct the same Job multiple
// times.
JobCache[Key] = J;
if (DriverPrintBindings) {
llvm::outs() << "# \"" << TC.getTriple().str()
<< "\" - \"" << llvm::sys::path::filename(J->getExecutable())
<< "\", inputs: [";
interleave(InputActions.begin(), InputActions.end(),
[](const Action *A) {
auto Input = cast<InputAction>(A);
llvm::outs() << '"' << Input->getInputArg().getValue() << '"';
},
[] { llvm::outs() << ", "; });
if (!InputActions.empty() && !J->getInputs().empty())
llvm::outs() << ", ";
interleave(J->getInputs().begin(), J->getInputs().end(),
[](const Job *Input) {
auto FileNames = Input->getOutput().getPrimaryOutputFilenames();
interleave(FileNames.begin(), FileNames.end(),
[](const std::string &FileName) {
llvm::outs() << '"' << FileName << '"';
},
[] { llvm::outs() << ", "; });
},
[] { llvm::outs() << ", "; });
llvm::outs() << "], output: {";
auto OutputFileNames = J->getOutput().getPrimaryOutputFilenames();
StringRef TypeName =
file_types::getTypeName(J->getOutput().getPrimaryOutputType());
interleave(OutputFileNames.begin(), OutputFileNames.end(),
[TypeName](const std::string &FileName) {
llvm::outs() << TypeName << ": \"" << FileName << '"';
},
[] { llvm::outs() << ", "; });
file_types::forAllTypes([&J](file_types::ID Ty) {
StringRef AdditionalOutput =
J->getOutput().getAdditionalOutputForType(Ty);
if (!AdditionalOutput.empty()) {
llvm::outs() << ", " << file_types::getTypeName(Ty) << ": \""
<< AdditionalOutput << '"';
}
});
llvm::outs() << '}';
switch (J->getCondition()) {
case Job::Condition::Always:
break;
case Job::Condition::RunWithoutCascading:
llvm::outs() << ", condition: run-without-cascading";
break;
case Job::Condition::CheckDependencies:
llvm::outs() << ", condition: check-dependencies";
break;
case Job::Condition::NewlyAdded:
llvm::outs() << ", condition: newly-added";
break;
}
llvm::outs() << '\n';
}
return J;
}
void Driver::computeMainOutput(
Compilation &C, const JobAction *JA, const OutputFileMap *OFM,
bool AtTopLevel, SmallVectorImpl<const Action *> &InputActions,
SmallVectorImpl<const Job *> &InputJobs, const TypeToPathMap *OutputMap,
StringRef workingDirectory, StringRef BaseInput, StringRef PrimaryInput,
llvm::SmallString<128> &Buf, CommandOutput *Output) const {
StringRef OutputFile;
if (C.getOutputInfo().isMultiThreading() && isa<CompileJobAction>(JA) &&
file_types::isAfterLLVM(JA->getType())) {
// Multi-threaded compilation: A single frontend command produces multiple
// output file: one for each input files.
// In batch mode, the driver will try to reserve multiple differing main
// outputs to a bridging header. Another assertion will trip, but the cause
// will be harder to track down. Since the driver now ignores -num-threads
// in batch mode, the user should never be able to falsify this assertion.
assert(!C.getBatchModeEnabled() && "Batch mode produces only one main "
"output per input action, cannot have "
"batch mode & num-threads");
auto OutputFunc = [&](StringRef Base, StringRef Primary) {
const TypeToPathMap *OMForInput = nullptr;
if (OFM)
OMForInput = OFM->getOutputMapForInput(Base);
OutputFile = getOutputFilename(C, JA, OMForInput, workingDirectory,
AtTopLevel, Base, Primary, Buf);
Output->addPrimaryOutput(CommandInputPair(Base, Primary),
OutputFile);
};
// Add an output file for each input action.
for (const Action *A : InputActions) {
const InputAction *IA = cast<InputAction>(A);
StringRef IV = IA->getInputArg().getValue();
OutputFunc(IV, IV);
}
// Add an output file for each primary output of each input job.
for (const Job *IJ : InputJobs) {
size_t i = 0;
CommandOutput const &Out = IJ->getOutput();
for (auto OutPrimary : Out.getPrimaryOutputFilenames()) {
OutputFunc(Out.getBaseInput(i++), OutPrimary);
}
}
} else {
// The common case: there is a single output file.
OutputFile = getOutputFilename(C, JA, OutputMap, workingDirectory,
AtTopLevel, BaseInput, PrimaryInput, Buf);
Output->addPrimaryOutput(CommandInputPair(BaseInput, PrimaryInput),
OutputFile);
}
}
void Driver::chooseSwiftModuleOutputPath(Compilation &C,
const TypeToPathMap *OutputMap,
StringRef workingDirectory,
CommandOutput *Output) const {
if (hasExistingAdditionalOutput(*Output, file_types::TY_SwiftModuleFile))
return;
StringRef OFMModuleOutputPath;
if (OutputMap) {
auto iter = OutputMap->find(file_types::TY_SwiftModuleFile);
if (iter != OutputMap->end())
OFMModuleOutputPath = iter->second;
}
const OutputInfo &OI = C.getOutputInfo();
const Arg *A = C.getArgs().getLastArg(options::OPT_emit_module_path);
using file_types::TY_SwiftModuleFile;
if (!OFMModuleOutputPath.empty()) {
// Prefer a path from the OutputMap.
Output->setAdditionalOutputForType(TY_SwiftModuleFile, OFMModuleOutputPath);
} else if (A && OI.CompilerMode == OutputInfo::Mode::SingleCompile) {
// We're performing a single compilation (and thus no merge module step),
// so prefer to use -emit-module-path, if present.
Output->setAdditionalOutputForType(TY_SwiftModuleFile, A->getValue());
} else if (Output->getPrimaryOutputType() == TY_SwiftModuleFile) {
// If the primary type is already a module type, we're out of
// options for overriding the primary name choice: stop now.
assert(!Output->getPrimaryOutputFilename().empty());
return;
} else if (OI.CompilerMode == OutputInfo::Mode::SingleCompile &&
OI.ShouldTreatModuleAsTopLevelOutput) {
// We're performing a single compile and don't have -emit-module-path,
// but have been told to treat the module as a top-level output.
// Determine an appropriate path.
llvm::SmallString<128> Path;
if (const Arg *A = C.getArgs().getLastArg(options::OPT_o)) {
// Put the module next to the top-level output.
Path = A->getValue();
llvm::sys::path::remove_filename(Path);
} else {
// A top-level output wasn't specified, so just output to
// <ModuleName>.swiftmodule in the current directory.
}
llvm::sys::path::append(Path, OI.ModuleName);
llvm::sys::path::replace_extension(
Path, file_types::getExtension(TY_SwiftModuleFile));
Output->setAdditionalOutputForType(TY_SwiftModuleFile, Path);
} else if (Output->getPrimaryOutputType() != file_types::TY_Nothing) {
// We're only generating the module as an intermediate, so put it next
// to the primary output of the compile command.
llvm::SmallString<128> Path(Output->getPrimaryOutputFilenames()[0]);
assert(!Path.empty());
bool isTempFile = C.isTemporaryFile(Path);
llvm::sys::path::replace_extension(
Path, file_types::getExtension(TY_SwiftModuleFile));
Output->setAdditionalOutputForType(TY_SwiftModuleFile, Path);
if (isTempFile)
C.addTemporaryFile(Path);
}
}
void Driver::chooseSwiftModuleDocOutputPath(Compilation &C,
const TypeToPathMap *OutputMap,
StringRef workingDirectory,
CommandOutput *Output) const {
if (hasExistingAdditionalOutput(*Output, file_types::TY_SwiftModuleDocFile))
return;
StringRef OFMModuleDocOutputPath;
if (OutputMap) {
auto iter = OutputMap->find(file_types::TY_SwiftModuleDocFile);
if (iter != OutputMap->end())
OFMModuleDocOutputPath = iter->second;
}
if (!OFMModuleDocOutputPath.empty()) {
// Prefer a path from the OutputMap.
Output->setAdditionalOutputForType(file_types::TY_SwiftModuleDocFile,
OFMModuleDocOutputPath);
} else if (Output->getPrimaryOutputType() != file_types::TY_Nothing) {
// Otherwise, put it next to the swiftmodule file.
llvm::SmallString<128> Path(
Output->getAnyOutputForType(file_types::TY_SwiftModuleFile));
bool isTempFile = C.isTemporaryFile(Path);
llvm::sys::path::replace_extension(
Path, file_types::getExtension(file_types::TY_SwiftModuleDocFile));
Output->setAdditionalOutputForType(file_types::TY_SwiftModuleDocFile, Path);
if (isTempFile)
C.addTemporaryFile(Path);
}
}
void Driver::chooseRemappingOutputPath(Compilation &C,
const TypeToPathMap *OutputMap,
CommandOutput *Output) const {
if (hasExistingAdditionalOutput(*Output, file_types::TY_Remapping))
return;
StringRef OFMFixitsOutputPath;
if (OutputMap) {
auto iter = OutputMap->find(file_types::TY_Remapping);
if (iter != OutputMap->end())
OFMFixitsOutputPath = iter->second;
}
if (!OFMFixitsOutputPath.empty()) {
Output->setAdditionalOutputForType(file_types::ID::TY_Remapping,
OFMFixitsOutputPath);
} else {
llvm::SmallString<128> Path(Output->getPrimaryOutputFilenames()[0]);
bool isTempFile = C.isTemporaryFile(Path);
llvm::sys::path::replace_extension(Path,
file_types::getExtension(file_types::ID::TY_Remapping));
Output->setAdditionalOutputForType(file_types::ID::TY_Remapping, Path);
if (isTempFile)
C.addTemporaryFile(Path);
}
}
void Driver::chooseParseableInterfacePath(Compilation &C, const JobAction *JA,
StringRef workingDirectory,
llvm::SmallString<128> &buffer,
CommandOutput *output) const {
switch (C.getOutputInfo().CompilerMode) {
case OutputInfo::Mode::StandardCompile:
case OutputInfo::Mode::BatchModeCompile:
if (!isa<MergeModuleJobAction>(JA))
return;
break;
case OutputInfo::Mode::SingleCompile:
if (!isa<CompileJobAction>(JA))
return;
break;
case OutputInfo::Mode::Immediate:
case OutputInfo::Mode::REPL:
llvm_unreachable("these modes aren't usable with 'swiftc'");
}
StringRef outputPath = *getOutputFilenameFromPathArgOrAsTopLevel(
C.getOutputInfo(), C.getArgs(),
options::OPT_emit_parseable_module_interface_path,
file_types::TY_SwiftParseableInterfaceFile,
/*TreatAsTopLevelOutput*/true, workingDirectory, buffer);
output->setAdditionalOutputForType(file_types::TY_SwiftParseableInterfaceFile,
outputPath);
}
void Driver::chooseSerializedDiagnosticsPath(Compilation &C,
const JobAction *JA,
const TypeToPathMap *OutputMap,
StringRef workingDirectory,
CommandOutput *Output) const {
if (C.getArgs().hasArg(options::OPT_serialize_diagnostics)) {
auto pchJA = dyn_cast<GeneratePCHJobAction>(JA);
if (pchJA && pchJA->isPersistentPCH()) {
addDiagFileOutputForPersistentPCHAction(C, pchJA, *Output, OutputMap,
workingDirectory);
} else {
addAuxiliaryOutput(C, *Output, file_types::TY_SerializedDiagnostics,
OutputMap, workingDirectory);
}
// Remove any existing diagnostics files so that clients can detect their
// presence to determine if a command was run.
StringRef OutputPath =
Output->getAnyOutputForType(file_types::TY_SerializedDiagnostics);
if (llvm::sys::fs::is_regular_file(OutputPath))
llvm::sys::fs::remove(OutputPath);
}
}
void Driver::chooseDependenciesOutputPaths(Compilation &C,
const TypeToPathMap *OutputMap,
StringRef workingDirectory,
llvm::SmallString<128> &Buf,
CommandOutput *Output) const {
if (C.getArgs().hasArg(options::OPT_emit_dependencies)) {
addAuxiliaryOutput(C, *Output, file_types::TY_Dependencies, OutputMap,
workingDirectory);
}
if (C.getIncrementalBuildEnabled()) {
addAuxiliaryOutput(C, *Output, file_types::TY_SwiftDeps, OutputMap,
workingDirectory);
}
chooseLoadedModuleTracePath(C, workingDirectory, Buf, Output);
}
void Driver::chooseLoadedModuleTracePath(Compilation &C,
StringRef workingDirectory,
llvm::SmallString<128> &Buf,
CommandOutput *Output) const {
// The loaded-module-trace is the same for all compile jobs: all `import`
// statements are processed, even ones from non-primary files. Thus, only
// one of those jobs needs to emit the file, and we can get it to write
// straight to the desired final location.
auto tracePathEnvVar = getenv("SWIFT_LOADED_MODULE_TRACE_FILE");
auto shouldEmitTrace =
tracePathEnvVar ||
C.getArgs().hasArg(options::OPT_emit_loaded_module_trace,
options::OPT_emit_loaded_module_trace_path);
if (shouldEmitTrace &&
C.requestPermissionForFrontendToEmitLoadedModuleTrace()) {
StringRef filename;
// Prefer the environment variable.
if (tracePathEnvVar)
filename = StringRef(tracePathEnvVar);
else {
// By treating this as a top-level output, the return value always
// exists.
filename = *getOutputFilenameFromPathArgOrAsTopLevel(
C.getOutputInfo(), C.getArgs(),
options::OPT_emit_loaded_module_trace_path,
file_types::TY_ModuleTrace,
/*TreatAsTopLevelOutput=*/true, workingDirectory, Buf);
}
Output->setAdditionalOutputForType(file_types::TY_ModuleTrace, filename);
}
}
void Driver::chooseTBDPath(Compilation &C, const TypeToPathMap *OutputMap,
StringRef workingDirectory,
llvm::SmallString<128> &Buf,
CommandOutput *Output) const {
StringRef pathFromArgs;
if (const Arg *A = C.getArgs().getLastArg(options::OPT_emit_tbd_path)) {
pathFromArgs = A->getValue();
}
addAuxiliaryOutput(C, *Output, file_types::TY_TBD, OutputMap,
workingDirectory, pathFromArgs,
/*requireArg=*/options::OPT_emit_tbd);
}
void Driver::chooseOptimizationRecordPath(Compilation &C,
StringRef workingDirectory,
llvm::SmallString<128> &Buf,
CommandOutput *Output) const {
const OutputInfo &OI = C.getOutputInfo();
if (OI.CompilerMode == OutputInfo::Mode::SingleCompile) {
auto filename = *getOutputFilenameFromPathArgOrAsTopLevel(
OI, C.getArgs(), options::OPT_save_optimization_record_path,
file_types::TY_OptRecord, /*TreatAsTopLevelOutput=*/true,
workingDirectory, Buf);
Output->setAdditionalOutputForType(file_types::TY_OptRecord, filename);
} else
// FIXME: We should use the OutputMap in this case.
Diags.diagnose({}, diag::warn_opt_remark_disabled);
}
void Driver::chooseObjectiveCHeaderOutputPath(Compilation &C,
const TypeToPathMap *OutputMap,
StringRef workingDirectory,
CommandOutput *Output) const {
if (hasExistingAdditionalOutput(*Output, file_types::TY_ObjCHeader))
return;
StringRef ObjCHeaderPath;
if (OutputMap) {
auto iter = OutputMap->find(file_types::TY_ObjCHeader);
if (iter != OutputMap->end())
ObjCHeaderPath = iter->second;
}
if (ObjCHeaderPath.empty())
if (auto A = C.getArgs().getLastArg(options::OPT_emit_objc_header_path))
ObjCHeaderPath = A->getValue();
if (!ObjCHeaderPath.empty()) {
Output->setAdditionalOutputForType(file_types::TY_ObjCHeader,
ObjCHeaderPath);
} else {
// Put the header next to the primary output file.
// FIXME: That's not correct if the user /just/ passed -emit-header
// and not -emit-module.
addAuxiliaryOutput(C, *Output, file_types::TY_ObjCHeader,
/*output file map*/ nullptr, workingDirectory);
}
}
static unsigned printActions(const Action *A,
llvm::DenseMap<const Action *, unsigned> &Ids) {
if (Ids.count(A))
return Ids[A];
std::string str;
llvm::raw_string_ostream os(str);
os << Action::getClassName(A->getKind()) << ", ";
if (const auto *IA = dyn_cast<InputAction>(A)) {
os << "\"" << IA->getInputArg().getValue() << "\"";
} else {
os << "{";
interleave(*cast<JobAction>(A),
[&](const Action *Input) { os << printActions(Input, Ids); },
[&] { os << ", "; });
os << "}";
}
unsigned Id = Ids.size();
Ids[A] = Id;
llvm::errs() << Id << ": " << os.str() << ", "
<< file_types::getTypeName(A->getType()) << "\n";
return Id;
}
void Driver::printActions(const Compilation &C) const {
llvm::DenseMap<const Action *, unsigned> Ids;
for (const Action *A : C.getActions()) {
::printActions(A, Ids);
}
}
void Driver::printVersion(const ToolChain &TC, raw_ostream &OS) const {
OS << version::getSwiftFullVersion(
version::Version::getCurrentLanguageVersion()) << '\n';
OS << "Target: " << TC.getTriple().str() << '\n';
}
void Driver::printHelp(bool ShowHidden) const {
unsigned IncludedFlagsBitmask = 0;
unsigned ExcludedFlagsBitmask = options::NoDriverOption;
switch (driverKind) {
case DriverKind::Interactive:
ExcludedFlagsBitmask |= options::NoInteractiveOption;
break;
case DriverKind::Batch:
case DriverKind::AutolinkExtract:
case DriverKind::SwiftFormat:
ExcludedFlagsBitmask |= options::NoBatchOption;
break;
}
if (!ShowHidden)
ExcludedFlagsBitmask |= HelpHidden;
getOpts().PrintHelp(llvm::outs(), Name.c_str(), "Swift compiler",
IncludedFlagsBitmask, ExcludedFlagsBitmask,
/*ShowAllAliases*/false);
}
bool OutputInfo::mightHaveExplicitPrimaryInputs(
const CommandOutput &Output) const {
switch (CompilerMode) {
case Mode::StandardCompile:
case Mode::BatchModeCompile:
return true;
case Mode::SingleCompile:
return false;
case Mode::Immediate:
case Mode::REPL:
llvm_unreachable("REPL and immediate modes handled elsewhere");
}
llvm_unreachable("unhandled mode");
}