blob: b1c3a1167c8845d63bd73e188726212cb93f90ed [file] [log] [blame]
//===--- driver.cpp - Swift Compiler Driver -------------------------------===//
// This source file is part of the open source project
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
// See for license information
// See for the list of Swift project authors
// This is the entry point to the swift compiler driver.
#include "swift/AST/DiagnosticEngine.h"
#include "swift/AST/DiagnosticsDriver.h"
#include "swift/Basic/LLVMInitialize.h"
#include "swift/Basic/PrettyStackTrace.h"
#include "swift/Basic/Program.h"
#include "swift/Basic/TaskQueue.h"
#include "swift/Basic/SourceManager.h"
#include "swift/Driver/Compilation.h"
#include "swift/Driver/Driver.h"
#include "swift/Driver/FrontendUtil.h"
#include "swift/Driver/Job.h"
#include "swift/Driver/ToolChain.h"
#include "swift/Frontend/Frontend.h"
#include "swift/Frontend/PrintingDiagnosticConsumer.h"
#include "swift/FrontendTool/FrontendTool.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/ConvertUTF.h"
#include "llvm/Support/Errno.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Host.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/PrettyStackTrace.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/Program.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/StringSaver.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/raw_ostream.h"
#include <memory>
#include <stdlib.h>
#if defined(_WIN32)
#include <windows.h>
using namespace swift;
using namespace swift::driver;
std::string getExecutablePath(const char *FirstArg) {
void *P = (void *)(intptr_t)getExecutablePath;
return llvm::sys::fs::getMainExecutable(FirstArg, P);
/// Run 'swift-autolink-extract'.
extern int autolink_extract_main(ArrayRef<const char *> Args, const char *Argv0,
void *MainAddr);
extern int modulewrap_main(ArrayRef<const char *> Args, const char *Argv0,
void *MainAddr);
/// Run 'swift-indent'
extern int swift_indent_main(ArrayRef<const char *> Args, const char *Argv0,
void *MainAddr);
/// Run 'swift-symbolgraph-extract'
extern int swift_symbolgraph_extract_main(ArrayRef<const char *> Args, const char *Argv0,
void *MainAddr);
/// Determine if the given invocation should run as a "subcommand".
/// Examples of "subcommands" are 'swift build' or 'swift test', which are
/// usually used to invoke the Swift package manager executables 'swift-build'
/// and 'swift-test', respectively.
/// \param ExecName The name of the argv[0] we were invoked as.
/// \param SubcommandName On success, the full name of the subcommand to invoke.
/// \param Args On return, the adjusted program arguments to use.
/// \returns True if running as a subcommand.
static bool shouldRunAsSubcommand(StringRef ExecName,
SmallString<256> &SubcommandName,
const ArrayRef<const char *> Args,
bool &isRepl) {
// If we are not run as 'swift', don't do anything special. This doesn't work
// with symlinks with alternate names, but we can't detect 'swift' vs 'swiftc'
// if we try and resolve using the actual executable path.
if (ExecName != "swift")
return false;
// If there are no program arguments, always invoke as normal.
if (Args.size() == 1)
return false;
// Otherwise, we have a program argument. If it looks like an option or a
// path, then invoke in interactive mode with the arguments as given.
StringRef FirstArg(Args[1]);
if (FirstArg.startswith("-") || FirstArg.find('.') != StringRef::npos ||
FirstArg.find('/') != StringRef::npos)
return false;
// Otherwise, we should have some sort of subcommand. Get the subcommand name
// and remove it from the program arguments.
StringRef Subcommand = Args[1];
// If the subcommand is the "built-in" 'repl', then use the
// normal driver.
if (Subcommand == "repl") {
isRepl = true;
return false;
// Form the subcommand name.
return true;
static bool shouldDisallowNewDriver(StringRef ExecName,
const ArrayRef<const char *> argv) {
// We are not invoking the driver, so don't forward.
if (ExecName != "swift" && ExecName != "swiftc") {
return true;
// If user specified using the old driver, don't forward.
if (llvm::find_if(argv, [](const char* arg) {
return StringRef(arg) == "-disallow-use-new-driver";
}) != argv.end()) {
return true;
if (llvm::sys::Process::GetEnv("SWIFT_USE_OLD_DRIVER").hasValue()) {
return true;
return false;
static int run_driver(StringRef ExecName,
const ArrayRef<const char *> argv) {
// Handle integrated tools.
if (argv.size() > 1) {
StringRef FirstArg(argv[1]);
if (FirstArg == "-frontend") {
return performFrontend(llvm::makeArrayRef(,,
argv[0], (void *)(intptr_t)getExecutablePath);
if (FirstArg == "-modulewrap") {
return modulewrap_main(llvm::makeArrayRef(,,
argv[0], (void *)(intptr_t)getExecutablePath);
// Run the integrated Swift frontend when called as "swift-frontend" but
// without a leading "-frontend".
if (!FirstArg.startswith("--driver-mode=")
&& ExecName == "swift-frontend") {
return performFrontend(llvm::makeArrayRef(,,
argv[0], (void *)(intptr_t)getExecutablePath);
std::string Path = getExecutablePath(argv[0]);
PrintingDiagnosticConsumer PDC;
SourceManager SM;
DiagnosticEngine Diags(SM);
std::string newDriverName = "swift-driver-new";
if (auto driverNameOp = llvm::sys::Process::GetEnv("SWIFT_USE_NEW_DRIVER")) {
newDriverName = driverNameOp.getValue();
// Forwarding calls to the swift driver if the C++ driver is invoked as `swift`
// or `swiftc`, and an environment variable SWIFT_USE_NEW_DRIVER is defined.
if (!shouldDisallowNewDriver(ExecName, argv)) {
SmallString<256> NewDriverPath(llvm::sys::path::parent_path(Path));
llvm::sys::path::append(NewDriverPath, newDriverName);
if (llvm::sys::fs::exists(NewDriverPath)) {
SmallVector<const char *, 256> subCommandArgs;
// Rewrite the program argument.
if (ExecName == "swiftc") {
} else {
assert(ExecName == "swift");
subCommandArgs.insert(subCommandArgs.end(), argv.begin() + 1, argv.end());
// Execute the subcommand.
Diags.diagnose(SourceLoc(), diag::remark_forwarding_to_new_driver,
// If we reach here then an error occurred (typically a missing path).
std::string ErrorString = llvm::sys::StrError();
llvm::errs() << "error: unable to invoke subcommand: " << subCommandArgs[0]
<< " (" << ErrorString << ")\n";
return 2;
Driver TheDriver(Path, ExecName, argv, Diags);
switch (TheDriver.getDriverKind()) {
case Driver::DriverKind::AutolinkExtract:
return autolink_extract_main(
argv[0], (void *)(intptr_t)getExecutablePath);
case Driver::DriverKind::SwiftIndent:
return swift_indent_main(
argv[0], (void *)(intptr_t)getExecutablePath);
case Driver::DriverKind::SymbolGraph:
return swift_symbolgraph_extract_main(TheDriver.getArgsWithoutProgramNameAndDriverMode(argv), argv[0], (void *)(intptr_t)getExecutablePath);
std::unique_ptr<llvm::opt::InputArgList> ArgList =
TheDriver.parseArgStrings(ArrayRef<const char*>(argv).slice(1));
if (Diags.hadAnyError())
return 1;
std::unique_ptr<ToolChain> TC = TheDriver.buildToolChain(*ArgList);
if (Diags.hadAnyError())
return 1;
std::unique_ptr<Compilation> C =
TheDriver.buildCompilation(*TC, std::move(ArgList));
if (Diags.hadAnyError())
return 1;
if (C) {
std::unique_ptr<sys::TaskQueue> TQ = TheDriver.buildTaskQueue(*C);
if (!TQ)
return 1;
return C->performJobs(std::move(TQ)).exitCode;
return 0;
int main(int argc_, const char **argv_) {
#if defined(_WIN32)
LPWSTR *wargv_ = CommandLineToArgvW(GetCommandLineW(), &argc_);
std::vector<std::string> utf8Args;
// We use UTF-8 as the internal character encoding. On Windows,
// arguments passed to wmain are encoded in UTF-16
for (int i = 0; i < argc_; i++) {
const wchar_t *wideArg = wargv_[i];
int wideArgLen = std::wcslen(wideArg);
llvm::ArrayRef<char> uRef((const char *)wideArg,
(const char *)(wideArg + wideArgLen));
llvm::convertUTF16ToUTF8String(uRef, utf8Args[i]);
std::vector<const char *> utf8CStrs;
std::transform(utf8Args.begin(), utf8Args.end(),
argv_ =;
// Expand any response files in the command line argument vector - arguments
// may be passed through response files in the event of command line length
// restrictions.
SmallVector<const char *, 256> ExpandedArgs(&argv_[0], &argv_[argc_]);
llvm::BumpPtrAllocator Allocator;
llvm::StringSaver Saver(Allocator);
? llvm::cl::TokenizeWindowsCommandLine
: llvm::cl::TokenizeGNUCommandLine,
// Initialize the stack trace using the parsed argument vector with expanded
// response files.
// PROGRAM_START/InitLLVM overwrites the passed in arguments with UTF-8
// versions of them on Windows. This also has the effect of overwriting the
// response file expansion. Since we handle the UTF-8 conversion above, we
// pass in a copy and throw away the modifications.
int ThrowawayExpandedArgc = ExpandedArgs.size();
const char **ThrowawayExpandedArgv =;
PROGRAM_START(ThrowawayExpandedArgc, ThrowawayExpandedArgv);
ArrayRef<const char *> argv(ExpandedArgs);
PrettyStackTraceSwiftVersion versionStackTrace;
// Check if this invocation should execute a subcommand.
StringRef ExecName = llvm::sys::path::stem(argv[0]);
SmallString<256> SubcommandName;
bool isRepl = false;
if (shouldRunAsSubcommand(ExecName, SubcommandName, argv, isRepl)) {
// Preserve argv for the stack trace.
SmallVector<const char *, 256> subCommandArgs(argv.begin(), argv.end());
// We are running as a subcommand, try to find the subcommand adjacent to
// the executable we are running as.
SmallString<256> SubcommandPath(
llvm::sys::path::append(SubcommandPath, SubcommandName);
// If we didn't find the tool there, let the OS search for it.
if (!llvm::sys::fs::exists(SubcommandPath)) {
// Search for the program and use the path if found. If there was an
// error, ignore it and just let the exec fail.
auto result = llvm::sys::findProgramByName(SubcommandName);
if (!result.getError())
SubcommandPath = *result;
// Rewrite the program argument.
subCommandArgs[0] = SubcommandPath.c_str();
// Execute the subcommand.
// If we reach here then an error occurred (typically a missing path).
std::string ErrorString = llvm::sys::StrError();
llvm::errs() << "error: unable to invoke subcommand: " << subCommandArgs[0]
<< " (" << ErrorString << ")\n";
return 2;
if (isRepl) {
// Preserve argv for the stack trace.
SmallVector<const char *, 256> replArgs(argv.begin(), argv.end());
return run_driver(ExecName, replArgs);
} else {
return run_driver(ExecName, argv);