Files
swift-mirror/lib/Driver/Driver.cpp
Jordan Rose 9c6ddc085f [driver] Add -lldb-repl and -integrated-repl modes.
Since LLDB is taking over as the REPL for Swift, we eventually want "swift"
and "swift -repl" to invoke "lldb --repl" rather than the frontend. However,
we only want to do this if the LLDB that's present is related to the Swift
that's present -- we don't want to invoke some random LLDB on the system
or in some other Xcode installation. Therefore, Swift searches for LLDB--
first next to the driver, then in the usr/bin/ outside of a toolchain--
before choosing to use it.

If the user just passes -repl and an LLDB is not found relative to the
driver, the existing "integrated" REPL will be launched instead.*

If the user passes -lldb-repl and an LLDB is not found relative to the
driver, one from the user's PATH will be chosen (like the linker).

The user can also pass -integrated-repl to get the existing behavior.
"swift -frontend -repl" always uses the integrated REPL.

* Since LLDB's not quite ready to be the REPL yet, "swift -repl" still
invokes the integrated REPL. "swift -repl -experimental-prefer-lldb" tests
the new behavior; this option will become the default (and the flag removed)
in <rdar://problem/16776719>.

<rdar://problem/16776705>

Swift SVN r17134
2014-05-01 02:15:53 +00:00

1170 lines
39 KiB
C++

//===-- Driver.cpp - Swift compiler driver --------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file contains implementations of parts of the compiler driver.
//
//===----------------------------------------------------------------------===//
#include "swift/Driver/Driver.h"
#include "Tools.h"
#include "ToolChains.h"
#include "swift/Strings.h"
#include "swift/AST/DiagnosticEngine.h"
#include "swift/AST/DiagnosticsDriver.h"
#include "swift/AST/DiagnosticsFrontend.h"
#include "swift/Basic/LLVM.h"
#include "swift/Basic/Version.h"
#include "swift/Basic/Range.h"
#include "swift/Driver/Action.h"
#include "swift/Driver/Compilation.h"
#include "swift/Driver/Job.h"
#include "swift/Driver/Options.h"
#include "swift/Driver/OutputFileMap.h"
#include "swift/Driver/ToolChain.h"
#include "swift/Parse/Lexer.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.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/Path.h"
#include "llvm/Support/PrettyStackTrace.h"
#include "llvm/Support/Program.h"
#include "llvm/Support/raw_ostream.h"
#include <memory>
using namespace swift;
using namespace swift::driver;
using namespace llvm::opt;
Driver::Driver(StringRef DriverExecutable,
DiagnosticEngine &Diags)
: Opts(createDriverOptTable()), Diags(Diags),
DriverExecutable(DriverExecutable),
DefaultTargetTriple(llvm::sys::getDefaultTargetTriple()) {
Name = llvm::sys::path::stem(DriverExecutable);
Dir = llvm::sys::path::parent_path(DriverExecutable);
}
Driver::~Driver() {
llvm::DeleteContainerSeconds(ToolChains);
}
std::unique_ptr<Compilation> Driver::buildCompilation(
ArrayRef<const char *> Args) {
llvm::PrettyStackTraceString CrashInfo("Compilation construction");
std::unique_ptr<InputArgList> ArgList(parseArgStrings(Args.slice(1)));
if (Diags.hadAnyError())
return nullptr;
bool DriverPrintActions = ArgList->hasArg(options::OPT_driver_print_actions);
bool DriverPrintOutputFileMap =
ArgList->hasArg(options::OPT_driver_print_output_file_map);
DriverPrintBindings = ArgList->hasArg(options::OPT_driver_print_bindings);
bool DriverPrintJobs = ArgList->hasArg(options::OPT_driver_print_jobs);
bool DriverSkipExecution =
ArgList->hasArg(options::OPT_driver_skip_execution);
std::unique_ptr<DerivedArgList> TranslatedArgList(
translateInputArgs(*ArgList));
if (const Arg *A = ArgList->getLastArg(options::OPT_target))
DefaultTargetTriple = A->getValue();
const ToolChain &TC = getToolChain(*ArgList);
if (Diags.hadAnyError())
return nullptr;
if (!handleImmediateArgs(*TranslatedArgList, TC)) {
return nullptr;
}
// Construct the list of inputs.
InputList Inputs;
buildInputs(TC, *TranslatedArgList, Inputs);
if (Diags.hadAnyError())
return nullptr;
// Determine the OutputInfo for the driver.
OutputInfo OI;
buildOutputInfo(*TranslatedArgList, Inputs, OI);
if (Diags.hadAnyError())
return nullptr;
assert(OI.CompilerOutputType != types::ID::TY_INVALID &&
"buildOutputInfo() must set a valid output type!");
if (OI.CompilerMode == OutputInfo::Mode::REPL)
// REPL mode expects no input files, so suppress the error.
SuppressNoInputFilesError = true;
// Construct the graph of Actions.
ActionList Actions;
buildActions(TC, *TranslatedArgList, Inputs, OI, Actions);
if (Diags.hadAnyError())
return nullptr;
if (DriverPrintActions) {
printActions(Actions);
return nullptr;
}
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;
}
}
std::unique_ptr<OutputFileMap> OFM;
buildOutputFileMap(*TranslatedArgList, OFM);
if (Diags.hadAnyError())
return nullptr;
if (DriverPrintOutputFileMap) {
if (OFM)
OFM->dump(llvm::errs(), true);
else
Diags.diagnose(SourceLoc(), diag::error_no_output_file_map_specified);
return nullptr;
}
OutputLevel Level = OutputLevel::Normal;
if (TranslatedArgList->hasArg(options::OPT_v))
Level = OutputLevel::Verbose;
std::unique_ptr<Compilation> C(new Compilation(*this, TC, Diags, Level,
std::move(ArgList),
std::move(TranslatedArgList),
NumberOfParallelCommands,
DriverSkipExecution));
buildJobs(Actions, OI, OFM.get(), *C);
if (Diags.hadAnyError())
return nullptr;
if (DriverPrintBindings)
return nullptr;
if (DriverPrintJobs) {
printJobs(C->getJobs());
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;
}
InputArgList *Driver::parseArgStrings(ArrayRef<const char *> Args) {
unsigned IncludedFlagsBitmask = 0;
unsigned ExcludedFlagsBitmask = options::NoDriverOption;
unsigned MissingArgIndex, MissingArgCount;
InputArgList *ArgList = getOpts().ParseArgs(Args.begin(), Args.end(),
MissingArgIndex, MissingArgCount,
IncludedFlagsBitmask,
ExcludedFlagsBitmask);
// 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 : make_range(ArgList->filtered_begin(options::OPT_UNKNOWN),
ArgList->filtered_end())) {
Diags.diagnose(SourceLoc(), diag::error_unknown_arg,
A->getAsString(*ArgList));
}
return ArgList;
}
DerivedArgList *Driver::translateInputArgs(const InputArgList &ArgList) const {
DerivedArgList *DAL = new DerivedArgList(ArgList);
bool ImmediateMode = ArgList.hasArgNoClaim(options::OPT_i);
for (Arg *A : ArgList) {
// If we're not in immediate mode, pick up inputs via the -- option.
if (!ImmediateMode && A->getOption().matches(options::OPT__DASH_DASH)) {
A->claim();
for (unsigned i = 0, e = A->getNumValues(); i != e; ++i) {
DAL->append(makeInputArg(*DAL, *Opts, A->getValue(i)));
}
continue;
}
DAL->append(A);
}
return DAL;
}
/// \brief Check that the file referenced by Value exists. If it doesn't,
/// issue a diagnostic and return false.
static bool diagnoseInputExistence(const Driver &D, const DerivedArgList &Args,
DiagnosticEngine &Diags, StringRef Value) {
// FIXME: provide opt-out for checking input file existence
// stdin always exists.
if (Value == "-")
return true;
llvm::SmallString<64> Path(Value);
if (Arg *WorkDir = Args.getLastArg(options::OPT_working_directory)) {
if (!llvm::sys::path::is_absolute(Path.str())) {
Path.assign(WorkDir->getValue());
llvm::sys::path::append(Path, Value);
}
}
if (llvm::sys::fs::exists(Twine(Path)))
return true;
Diags.diagnose(SourceLoc(), diag::error_no_such_file_or_directory, Value);
return false;
}
void Driver::buildInputs(const ToolChain &TC,
const DerivedArgList &Args,
InputList &Inputs) const {
types::ID InputType = types::TY_Nothing;
Arg *InputTypeArg = nullptr;
for (Arg *A : Args) {
if (A->getOption().getKind() == Option::InputClass) {
const char *Value = A->getValue();
types::ID Ty = types::TY_INVALID;
if (InputType == types::TY_Nothing) {
// If there was an explicit arg for this, claim it.
if (InputTypeArg)
InputTypeArg->claim();
// stdin must be handled specially.
if (memcmp(Value, "-", 2) == 0) {
// By default, treat stdin as Swift input.
// FIXME: should we limit this inference to specific modes?
Ty = types::TY_Swift;
} else {
// Otherwise lookup by extension.
if (const char *Ext = strrchr(Value, '.')) {
Ty = TC.lookupTypeForExtension(Ext + 1);
}
if (Ty == types::TY_INVALID) {
// FIXME: should we adjust this inference in certain modes?
Ty = types::TY_Object;
}
}
} else {
assert(InputTypeArg && "InputType set w/o InputTypeArg");
InputTypeArg->claim();
Ty = InputType;
}
if (diagnoseInputExistence(*this, Args, Diags, Value))
Inputs.push_back(std::make_pair(Ty, A));
}
// FIXME: add -x support (or equivalent)
}
}
static bool maybeBuildingExecutable(const OutputInfo &OI,
const DerivedArgList &Args,
const Driver::InputList &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;
}
void Driver::buildOutputInfo(const DerivedArgList &Args,
const InputList &Inputs, OutputInfo &OI) const {
// By default, the driver does not link its output; this will be updated
// appropariately below if linking is required.
if (Args.hasArg(options::OPT_force_single_frontend_invocation))
OI.CompilerMode = OutputInfo::Mode::SingleCompile;
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 = types::TY_SwiftModuleFile;
} else if (Inputs.empty() && !Args.hasArg(options::OPT_v)) {
// No inputs and no mode arguments imply REPL mode
// (Treat -v as a mode, since -v and no other mode arguments means
// "print the version and exit".)
OI.CompilerOutputType = types::TY_Nothing;
OI.CompilerMode = OutputInfo::Mode::REPL;
} else {
OI.LinkAction = LinkKind::Executable;
OI.CompilerOutputType = types::TY_Object;
}
} else {
switch (OutputModeArg->getOption().getID()) {
case options::OPT_emit_executable:
OI.LinkAction = LinkKind::Executable;
OI.CompilerOutputType = types::TY_Object;
break;
case options::OPT_emit_library:
OI.LinkAction = LinkKind::DynamicLibrary;
OI.CompilerOutputType = types::TY_Object;
break;
case options::OPT_c:
OI.CompilerOutputType = types::TY_Object;
break;
case options::OPT_S:
OI.CompilerOutputType = types::TY_Assembly;
break;
case options::OPT_emit_sil:
OI.CompilerOutputType = types::TY_SIL;
break;
case options::OPT_emit_silgen:
OI.CompilerOutputType = types::TY_RawSIL;
break;
case options::OPT_emit_ir:
OI.CompilerOutputType = types::TY_LLVM_IR;
break;
case options::OPT_emit_bc:
OI.CompilerOutputType = types::TY_LLVM_BC;
break;
case options::OPT_parse:
case options::OPT_dump_parse:
case options::OPT_dump_ast:
case options::OPT_print_ast:
OI.CompilerOutputType = types::TY_Nothing;
break;
case options::OPT_i:
OI.CompilerOutputType = types::TY_Nothing;
OI.CompilerMode = OutputInfo::Mode::Immediate;
break;
case options::OPT_repl:
case options::OPT_integrated_repl:
case options::OPT_lldb_repl:
OI.CompilerOutputType = types::TY_Nothing;
OI.CompilerMode = OutputInfo::Mode::REPL;
break;
default:
llvm_unreachable("unknown mode");
}
}
assert(OI.CompilerOutputType != types::ID::TY_INVALID);
bool shouldEmitObjCHeader = Args.hasArg(options::OPT_emit_objc_header,
options::OPT_emit_objc_header_path);
if (shouldEmitObjCHeader &&
OI.CompilerMode == OutputInfo::Mode::SingleCompile) {
Diags.diagnose(SourceLoc(),
diag::error_emit_objc_header_with_single_compile);
}
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 ((Args.hasArg(options::OPT_g) && OI.shouldLink()) ||
shouldEmitObjCHeader) {
// 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 {
// 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());
} 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 == 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 {
const char *SDKROOT = getenv("SDKROOT");
if (SDKROOT)
OI.SDKPath = SDKROOT;
}
if (!OI.SDKPath.empty()) {
if (!llvm::sys::fs::exists(OI.SDKPath)) {
Diags.diagnose(SourceLoc(), diag::warning_no_such_sdk, OI.SDKPath);
}
}
}
}
void Driver::buildActions(const ToolChain &TC,
const DerivedArgList &Args,
const InputList &Inputs, const OutputInfo &OI,
ActionList &Actions) const {
if (!SuppressNoInputFilesError && Inputs.empty()) {
Diags.diagnose(SourceLoc(), diag::error_no_input_files);
return;
}
ActionList CompileActions;
switch (OI.CompilerMode) {
case OutputInfo::Mode::StandardCompile: {
for (const InputPair &Input : Inputs) {
types::ID InputType = Input.first;
const Arg *InputArg = Input.second;
std::unique_ptr<Action> Current(new InputAction(*InputArg, InputType));
if (InputType == types::TY_Swift || InputType == types::TY_SIL)
Current.reset(new CompileJobAction(Current.release(),
OI.CompilerOutputType));
CompileActions.push_back(Current.release());
}
break;
}
case OutputInfo::Mode::SingleCompile:
case OutputInfo::Mode::Immediate: {
std::unique_ptr<Action> CA(new CompileJobAction(OI.CompilerOutputType));
for (const InputPair &Input : Inputs) {
types::ID InputType = Input.first;
const Arg *InputArg = Input.second;
CA->addInput(new InputAction(*InputArg, InputType));
}
CompileActions.push_back(CA.release());
break;
}
case OutputInfo::Mode::REPL: {
if (!Inputs.empty()) {
// REPL mode requires no inputs.
Diags.diagnose(SourceLoc(), diag::error_repl_requires_no_input_files);
return;
}
// FIXME: Change this to PreferLLDB and drop the -experimental-prefer-lldb
// option once LLDB is ready.
REPLJobAction::Mode Mode = REPLJobAction::Mode::Integrated;
if (const Arg *A = Args.getLastArg(options::OPT_lldb_repl,
options::OPT_integrated_repl,
options::OPT_experimental_prefer_lldb)) {
if (A->getOption().matches(options::OPT_lldb_repl))
Mode = REPLJobAction::Mode::RequireLLDB;
else if (A->getOption().matches(options::OPT_experimental_prefer_lldb))
Mode = REPLJobAction::Mode::PreferLLDB;
else
Mode = REPLJobAction::Mode::Integrated;
}
CompileActions.push_back(new REPLJobAction(Mode));
break;
}
}
if (CompileActions.empty())
// If there are no compile actions, don't attempt to set up any downstream
// actions.
return;
std::unique_ptr<Action> MergeModuleAction;
if (OI.ShouldGenerateModule &&
OI.CompilerMode != OutputInfo::Mode::SingleCompile) {
// We're performing multiple compilations; set up a merge module step
// so we generate a single swiftmodule as output.
MergeModuleAction.reset(new MergeModuleJobAction(CompileActions));
}
if (OI.shouldLink()) {
Action *LinkAction = new LinkJobAction(CompileActions, OI.LinkAction);
if (MergeModuleAction) {
// We have a MergeModuleJobAction; this needs to be an input to the
// LinkJobAction. It shares inputs with the LinkAction, so tell it that it
// no longer owns its inputs.
MergeModuleAction->setOwnsInputs(false);
if (Args.hasArg(options::OPT_g))
LinkAction->addInput(MergeModuleAction.release());
else
Actions.push_back(MergeModuleAction.release());
}
Actions.push_back(LinkAction);
} else if (MergeModuleAction) {
Actions.push_back(MergeModuleAction.release());
} else {
Actions = CompileActions;
}
}
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;
}
return true;
}
void
Driver::buildOutputFileMap(const llvm::opt::DerivedArgList &Args,
std::unique_ptr<OutputFileMap> &OFM) const {
if (const Arg *A = Args.getLastArg(options::OPT_output_file_map)) {
// TODO: perform some preflight checks to ensure the file exists.
OFM = OutputFileMap::loadFromPath(A->getValue());
if (!OFM)
// TODO: emit diagnostic with error string
Diags.diagnose(SourceLoc(), diag::error_unable_to_load_output_file_map);
} else {
// We don't have an OutputFileMap, so reset the unique_ptr.
OFM.reset();
}
}
void Driver::buildJobs(const ActionList &Actions, const OutputInfo &OI,
const OutputFileMap *OFM, Compilation &C) const {
llvm::PrettyStackTraceString CrashInfo("Building compilation jobs");
const DerivedArgList &Args = C.getArgs();
const ToolChain &TC = C.getDefaultToolChain();
JobCacheMap JobCache;
Arg *FinalOutput = Args.getLastArg(options::OPT_o);
if (FinalOutput) {
unsigned NumOutputs = 0;
for (const Action *A : Actions) {
types::ID Type = A->getType();
if (Type != types::TY_Nothing && Type != types::TY_SwiftModuleFile) {
// Only increment NumOutputs if this is an output which must have its
// path specified using -o.
// (Module outputs can be specified using -module-output-path, or will
// be inferred if there are other top-level outputs.)
++NumOutputs;
}
}
if (NumOutputs > 1) {
Diags.diagnose(SourceLoc(),
diag::error_cannot_specify__o_for_multiple_outputs);
FinalOutput = nullptr;
}
}
// Collect the list of architectures.
llvm::StringSet<> ArchNames;
if (TC.getTriple().isOSDarwin()) {
for (const Arg *A : Args){
if (A->getOption().matches(options::OPT_arch)) {
ArchNames.insert(A->getValue());
}
}
}
for (const Action *A : Actions) {
Job *J = buildJobsForAction(C, A, OI, OFM, C.getDefaultToolChain(), true,
JobCache);
C.addJob(J);
}
}
static StringRef getBaseInputForJob(Job *J) {
if (Command *Cmd = dyn_cast<Command>(J)) {
return Cmd->getOutput().getBaseInput();
} else if (JobList *JL = dyn_cast<JobList>(J)) {
return getBaseInputForJob(JL->getJobs()[0]);
} else {
llvm_unreachable("Unknown Job class; cannot get base input");
}
}
static void printJobOutputs(const Job *J) {
if (const Command *Cmd = dyn_cast<Command>(J)) {
llvm::outs() << '"' << Cmd->getOutput().getPrimaryOutputFilename() << '"';
} else if (const JobList *JL = dyn_cast<JobList>(J)) {
for (unsigned long i = 0, e = JL->size(); i != e; ++i) {
printJobOutputs(JL->getJobs()[i]);
if (i+1 != e) {
llvm::outs() << ", ";
}
}
} else {
llvm_unreachable("Unknown Job class");
}
}
static StringRef getOutputFilename(const JobAction *JA,
const OutputInfo &OI,
const TypeToPathMap *OutputMap,
const llvm::opt::DerivedArgList &Args,
bool AtTopLevel,
StringRef BaseInput,
DiagnosticEngine &Diags,
llvm::SmallString<128> &Buffer) {
if (JA->getType() == 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.
if (isa<MergeModuleJobAction>(JA)) {
if (const Arg *A = Args.getLastArg(options::OPT_emit_module_path))
return A->getValue();
if (OI.ShouldTreatModuleAsTopLevelOutput) {
if (const Arg *A = Args.getLastArg(options::OPT_o)) {
if (OI.CompilerOutputType == types::TY_SwiftModuleFile)
return A->getValue();
// Otherwise, put the module 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, SERIALIZED_MODULE_EXTENSION);
return Buffer.str();
}
// A top-level output wasn't specified, so just output to
// <ModuleName>.swiftmodule.
Buffer = OI.ModuleName;
llvm::sys::path::replace_extension(Buffer, SERIALIZED_MODULE_EXTENSION);
return Buffer.str();
}
}
// 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 (types::isTextual(JA->getType()))
return "-";
}
assert(!BaseInput.empty() &&
"A Command which produces output must have a BaseInput!");
StringRef BaseName(BaseInput);
if (isa<MergeModuleJobAction>(JA) ||
OI.CompilerMode == OutputInfo::Mode::SingleCompile ||
JA->getType() == types::TY_Image)
BaseName = OI.ModuleName;
// We don't yet have a name, assign one.
if (!AtTopLevel) {
// We should output to a temporary file, since we're not at
// the top level.
StringRef Stem = llvm::sys::path::stem(BaseName);
StringRef Suffix = types::getTypeTempSuffix(JA->getType());
llvm::error_code EC = llvm::sys::fs::createTemporaryFile(Stem, Suffix,
Buffer);
if (EC) {
Diags.diagnose(SourceLoc(),
diag::error_unable_to_make_temporary_file,
EC.message());
return {};
}
return Buffer.str();
}
if (JA->getType() == types::TY_Image) {
if (JA->size() == 1 && OI.ModuleNameIsFallback && BaseInput != "-")
BaseName = llvm::sys::path::stem(BaseInput);
return BaseName;
}
StringRef Suffix = types::getTypeTempSuffix(JA->getType());
assert(Suffix.data() &&
"All types used for output should have a suffix.");
Buffer = llvm::sys::path::filename(BaseName);
llvm::sys::path::replace_extension(Buffer, Suffix);
return Buffer.str();
}
Job *Driver::buildJobsForAction(const Compilation &C, const Action *A,
const OutputInfo &OI,
const OutputFileMap *OFM,
const ToolChain &TC, bool AtTopLevel,
JobCacheMap &JobCache) const {
// 1. See if we've already got this cached.
std::pair<const Action *, const ToolChain *> Key(A, &TC);
{
auto CacheIter = JobCache.find(Key);
if (CacheIter != JobCache.end()) {
return CacheIter->second;
}
}
// 2. Build up the list of input jobs.
ActionList InputActions;
std::unique_ptr<JobList> InputJobs(new JobList);
InputJobs->setOwnsJobs(A->getOwnsInputs());
for (Action *Input : *A) {
if (isa<InputAction>(Input)) {
InputActions.push_back(Input);
} else {
InputJobs->addJob(buildJobsForAction(C, Input, OI, OFM,
C.getDefaultToolChain(), false,
JobCache));
}
}
// 3. Select the right tool for the job.
const JobAction *JA = cast<JobAction>(A);
const Tool *T = TC.selectTool(*JA);
if (!T)
return nullptr;
// 4. Determine the CommandOutput for the job.
StringRef BaseInput;
if (!InputActions.empty()) {
// Use the first InputAction as our BaseInput.
InputAction *IA = cast<InputAction>(InputActions[0]);
BaseInput = IA->getInputArg().getValue();
} else if (!InputJobs->empty()) {
// Use the first Job's BaseInput as our BaseInput.
Job *J = InputJobs->getJobs()[0];
BaseInput = getBaseInputForJob(J);
}
const TypeToPathMap *OutputMap = nullptr;
if (OFM && isa<CompileJobAction>(JA) &&
OI.CompilerMode != OutputInfo::Mode::SingleCompile)
OutputMap = OFM->getOutputMapForInput(BaseInput);
llvm::SmallString<128> Buf;
StringRef OutputFile = getOutputFilename(JA, OI, OutputMap, C.getArgs(),
AtTopLevel, BaseInput, Diags, Buf);
std::unique_ptr<CommandOutput> Output(new CommandOutput(JA->getType(),
OutputFile,
BaseInput));
// Choose the swiftmodule output path.
if (OI.ShouldGenerateModule && isa<CompileJobAction>(JA) &&
Output->getPrimaryOutputType() != types::TY_SwiftModuleFile) {
StringRef OFMModuleOutputPath;
if (OutputMap) {
auto iter = OutputMap->find(types::TY_SwiftModuleFile);
if (iter != OutputMap->end())
OFMModuleOutputPath = iter->second;
}
const Arg *A = C.getArgs().getLastArg(options::OPT_emit_module_path);
if (!OFMModuleOutputPath.empty()) {
// Prefer a path from the OutputMap.
Output->setAdditionalOutputForType(types::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(types::TY_SwiftModuleFile,
A->getValue());
} 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.
if (const Arg *A = C.getArgs().getLastArg(options::OPT_o)) {
// Put the module next to the top-level output.
llvm::SmallString<128> Path(A->getValue());
llvm::sys::path::remove_filename(Path);
llvm::sys::path::append(Path, OI.ModuleName);
llvm::sys::path::replace_extension(Path, SERIALIZED_MODULE_EXTENSION);
Output->setAdditionalOutputForType(types::TY_SwiftModuleFile, Path);
} else {
// A top-level output wasn't specified, so just output to
// <ModuleName>.swiftmodule.
llvm::SmallString<128> Path(OI.ModuleName);
llvm::sys::path::replace_extension(Path, SERIALIZED_MODULE_EXTENSION);
Output->setAdditionalOutputForType(types::TY_SwiftModuleFile, Path);
}
} else {
// 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->getPrimaryOutputFilename());
llvm::sys::path::replace_extension(Path, SERIALIZED_MODULE_EXTENSION);
Output->setAdditionalOutputForType(types::ID::TY_SwiftModuleFile, Path);
}
}
// Choose the swiftdoc output path.
if (OI.ShouldGenerateModule &&
(isa<CompileJobAction>(JA) || isa<MergeModuleJobAction>(JA))) {
StringRef OFMModuleDocOutputPath;
if (OutputMap) {
auto iter = OutputMap->find(types::TY_SwiftModuleDocFile);
if (iter != OutputMap->end())
OFMModuleDocOutputPath = iter->second;
}
if (!OFMModuleDocOutputPath.empty()) {
// Prefer a path from the OutputMap.
Output->setAdditionalOutputForType(types::TY_SwiftModuleDocFile,
OFMModuleDocOutputPath);
} else {
// Otherwise, put it next to the swiftmodule file.
llvm::SmallString<128> Path(
Output->getAnyOutputForType(types::TY_SwiftModuleFile));
llvm::sys::path::replace_extension(Path,
SERIALIZED_MODULE_DOC_EXTENSION);
Output->setAdditionalOutputForType(types::TY_SwiftModuleDocFile, Path);
}
}
// Choose the serialized diagnostics output path.
if (C.getArgs().hasArg(options::OPT_serialize_diagnostics) &&
isa<CompileJobAction>(JA)) {
StringRef OFMSerializeDiagnosticsPath;
if (OutputMap) {
auto iter = OutputMap->find(types::TY_SerializedDiagnostics);
if (iter != OutputMap->end())
OFMSerializeDiagnosticsPath = iter->second;
}
if (!OFMSerializeDiagnosticsPath.empty()) {
// Prefer a path from the OutputMap.
Output->setAdditionalOutputForType(types::TY_SerializedDiagnostics,
OFMSerializeDiagnosticsPath);
} else {
// Put the serialized diagnostics next to the primary output file.
llvm::SmallString<128> Path;
if (Output->getPrimaryOutputType() != types::TY_Nothing)
Path = Output->getPrimaryOutputFilename();
else if (!Output->getBaseInput().empty())
Path = llvm::sys::path::stem(Output->getBaseInput());
else
Path = OI.ModuleName;
llvm::sys::path::replace_extension(Path, "dia");
Output->setAdditionalOutputForType(types::TY_SerializedDiagnostics, Path);
}
// Remove any existing diagnostics files so that clients can detect their
// presence to determine if a command was run.
StringRef OutputPath =
Output->getAnyOutputForType(types::TY_SerializedDiagnostics);
if (llvm::sys::fs::can_write(OutputPath) &&
llvm::sys::fs::is_regular_file(OutputPath))
llvm::sys::fs::remove(OutputPath);
}
// Choose the Objective-C header output path.
if (isa<MergeModuleJobAction>(JA) &&
C.getArgs().hasArg(options::OPT_emit_objc_header,
options::OPT_emit_objc_header_path)) {
StringRef ObjCHeaderPath;
if (OutputMap) {
auto iter = OutputMap->find(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(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.
llvm::SmallString<128> Path;
if (Output->getPrimaryOutputType() != types::TY_Nothing)
Path = Output->getPrimaryOutputFilename();
else if (!Output->getBaseInput().empty())
Path = llvm::sys::path::stem(Output->getBaseInput());
else
Path = OI.ModuleName;
llvm::sys::path::replace_extension(Path, "h");
Output->setAdditionalOutputForType(types::TY_ObjCHeader, Path);
}
}
if (DriverPrintBindings) {
llvm::outs() << "# \"" << T->getToolChain().getTripleString() << '"'
<< " - \"" << T->getName()
<< "\", inputs: [";
// print inputs
for (unsigned i = 0, e = InputActions.size(); i != e; ++i) {
const InputAction *IA = cast<InputAction>(InputActions[i]);
llvm::outs() << '"' << IA->getInputArg().getValue() << '"';
if (i+1 != e || !InputJobs->empty())
llvm::outs() << ", ";
}
printJobOutputs(InputJobs.get());
llvm::outs() << "], output: {"
<< types::getTypeName(Output->getPrimaryOutputType())
<< ": \"" << Output->getPrimaryOutputFilename() << '"';
for (unsigned i = (types::TY_INVALID + 1), e = types::TY_LAST; i != e; ++i){
StringRef AdditionalOutput =
Output->getAdditionalOutputForType((types::ID)i);
if (!AdditionalOutput.empty()) {
llvm::outs() << ", " << types::getTypeName((types::ID)i) << ": \""
<< AdditionalOutput << '"';
}
}
llvm::outs() << "}\n";
}
// 5. Construct a Job which produces the right CommandOutput.
Job *J = T->constructJob(*JA, std::move(InputJobs), std::move(Output),
InputActions, C.getArgs(), OI);
// 6. Add it to the JobCache, so we don't construct the same Job multiple
// times.
JobCache[Key] = J;
return J;
}
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 InputAction *IA = dyn_cast<InputAction>(A)) {
os << "\"" << IA->getInputArg().getValue() << "\"";
} else {
os << "{";
for (auto it = A->begin(), ie = A->end(); it != ie;) {
os << printActions(*it, Ids);
++it;
if (it != ie)
os << ", ";
}
os << "}";
}
unsigned Id = Ids.size();
Ids[A] = Id;
llvm::errs() << Id << ": " << os.str() << ", "
<< types::getTypeName(A->getType()) << "\n";
return Id;
}
void Driver::printActions(const ActionList &Actions) const {
llvm::DenseMap<const Action *, unsigned> Ids;
for (const Action *A : Actions) {
::printActions(A, Ids);
}
}
static void printJob(const Job *J, llvm::DenseSet<const Job *> &VisitedJobs) {
if (!VisitedJobs.insert(J).second)
return;
if (const JobList *JL = dyn_cast<JobList>(J)) {
for (const Job *Job : *JL) {
printJob(Job, VisitedJobs);
}
} else if (const Command *Cmd = dyn_cast<Command>(J)) {
const JobList &Inputs = Cmd->getInputs();
printJob(&Inputs, VisitedJobs);
Cmd->printCommandLine(llvm::outs());
} else {
llvm_unreachable("Unknown JobClass");
}
}
void Driver::printJobs(const JobList &Jobs) const {
llvm::DenseSet<const Job *> VisitedJobs;
for (const Job *J : Jobs) {
printJob(J, VisitedJobs);
}
}
void Driver::printVersion(const ToolChain &TC, raw_ostream &OS) const {
OS << version::getSwiftFullVersion() << '\n';
OS << "Target: " << TC.getTripleString() << '\n';
}
void Driver::printHelp(bool ShowHidden) const {
unsigned IncludedFlagsBitmask = 0;
unsigned ExcludedFlagsBitmask = options::NoDriverOption;
if (!ShowHidden)
ExcludedFlagsBitmask |= HelpHidden;
getOpts().PrintHelp(llvm::outs(), Name.c_str(), "Swift compiler",
IncludedFlagsBitmask, ExcludedFlagsBitmask);
}
std::string Driver::getProgramPath(StringRef Name, const ToolChain &TC) const {
// TODO: perform ToolChain-specific lookup
std::string P(llvm::sys::FindProgramByName(Name));
if (!P.empty()) {
return P;
}
return Name;
}
static void setTargetFromArch(DiagnosticEngine &diags, llvm::Triple &target,
StringRef archName) {
llvm::Triple::ArchType archValue
= tools::darwin::getArchTypeForDarwinArchName(archName);
if (archValue != llvm::Triple::UnknownArch) {
target.setArch(archValue);
} else {
diags.diagnose(SourceLoc(), diag::error_invalid_arch, archName);
}
}
static llvm::Triple computeTargetTriple(DiagnosticEngine &diags,
StringRef DefaultTargetTriple,
const ArgList &Args,
StringRef DarwinArchName) {
// FIXME: need to check -target for overrides
llvm::Triple target(llvm::Triple::normalize(DefaultTargetTriple));
// Handle Darwin-specific options available here.
if (target.isOSDarwin()) {
// If an explict Darwin arch name is given, that trumps all.
if (!DarwinArchName.empty()) {
setTargetFromArch(diags, target, DarwinArchName);
// Handle the Darwin '-arch' flag.
} else if (Arg *A = Args.getLastArg(options::OPT_arch)) {
setTargetFromArch(diags, target, A->getValue());
}
}
// TODO: handle other target/pseudo-target flags as necessary.
return target;
}
const ToolChain &Driver::getToolChain(const ArgList &Args,
StringRef DarwinArchName) const {
llvm::Triple Target = computeTargetTriple(Diags, DefaultTargetTriple, Args,
DarwinArchName);
ToolChain *&TC = ToolChains[Target.str()];
if (!TC) {
switch (Target.getOS()) {
case llvm::Triple::Darwin:
case llvm::Triple::MacOSX:
case llvm::Triple::IOS:
TC = new toolchains::Darwin(*this, Target);
break;
default:
llvm_unreachable("No tool chain available for Triple");
}
}
return *TC;
}