diff options
Diffstat (limited to 'tools/driver/driver.cpp')
-rw-r--r-- | tools/driver/driver.cpp | 394 |
1 files changed, 226 insertions, 168 deletions
diff --git a/tools/driver/driver.cpp b/tools/driver/driver.cpp index 9f93837..e1f9367 100644 --- a/tools/driver/driver.cpp +++ b/tools/driver/driver.cpp @@ -18,7 +18,9 @@ #include "clang/Driver/Driver.h" #include "clang/Driver/DriverDiagnostic.h" #include "clang/Driver/Options.h" +#include "clang/Frontend/ChainedDiagnosticConsumer.h" #include "clang/Frontend/CompilerInvocation.h" +#include "clang/Frontend/SerializedDiagnosticPrinter.h" #include "clang/Frontend/TextDiagnosticPrinter.h" #include "clang/Frontend/Utils.h" #include "llvm/ADT/ArrayRef.h" @@ -61,8 +63,8 @@ std::string GetExecutablePath(const char *Argv0, bool CanonicalPrefixes) { return llvm::sys::fs::getMainExecutable(Argv0, P); } -static const char *SaveStringInSet(std::set<std::string> &SavedStrings, - StringRef S) { +static const char *GetStableCStr(std::set<std::string> &SavedStrings, + StringRef S) { return SavedStrings.insert(S).first->c_str(); } @@ -101,12 +103,12 @@ static void ApplyOneQAOverride(raw_ostream &OS, if (Edit[0] == '^') { const char *Str = - SaveStringInSet(SavedStrings, Edit.substr(1)); + GetStableCStr(SavedStrings, Edit.substr(1)); OS << "### Adding argument " << Str << " at beginning\n"; Args.insert(Args.begin() + 1, Str); } else if (Edit[0] == '+') { const char *Str = - SaveStringInSet(SavedStrings, Edit.substr(1)); + GetStableCStr(SavedStrings, Edit.substr(1)); OS << "### Adding argument " << Str << " at end\n"; Args.push_back(Str); } else if (Edit[0] == 's' && Edit[1] == '/' && Edit.endswith("/") && @@ -116,11 +118,14 @@ static void ApplyOneQAOverride(raw_ostream &OS, ReplPattern = ReplPattern.slice(0, ReplPattern.size()-1); for (unsigned i = 1, e = Args.size(); i != e; ++i) { + // Ignore end-of-line response file markers + if (Args[i] == nullptr) + continue; std::string Repl = llvm::Regex(MatchPattern).sub(ReplPattern, Args[i]); if (Repl != Args[i]) { OS << "### Replacing '" << Args[i] << "' with '" << Repl << "'\n"; - Args[i] = SaveStringInSet(SavedStrings, Repl); + Args[i] = GetStableCStr(SavedStrings, Repl); } } } else if (Edit[0] == 'x' || Edit[0] == 'X') { @@ -142,6 +147,9 @@ static void ApplyOneQAOverride(raw_ostream &OS, } else if (Edit[0] == 'O') { for (unsigned i = 1; i < Args.size();) { const char *A = Args[i]; + // Ignore end-of-line response file markers + if (A == nullptr) + continue; if (A[0] == '-' && A[1] == 'O' && (A[2] == '\0' || (A[3] == '\0' && (A[2] == 's' || A[2] == 'z' || @@ -152,7 +160,7 @@ static void ApplyOneQAOverride(raw_ostream &OS, ++i; } OS << "### Adding argument " << Edit << " at end\n"; - Args.push_back(SaveStringInSet(SavedStrings, '-' + Edit.str())); + Args.push_back(GetStableCStr(SavedStrings, '-' + Edit.str())); } else { OS << "### Unrecognized edit: " << Edit << "\n"; } @@ -187,97 +195,98 @@ static void ApplyQAOverride(SmallVectorImpl<const char*> &Args, } } -extern int cc1_main(const char **ArgBegin, const char **ArgEnd, - const char *Argv0, void *MainAddr); -extern int cc1as_main(const char **ArgBegin, const char **ArgEnd, - const char *Argv0, void *MainAddr); +extern int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, + void *MainAddr); +extern int cc1as_main(ArrayRef<const char *> Argv, const char *Argv0, + void *MainAddr); + +struct DriverSuffix { + const char *Suffix; + const char *ModeFlag; +}; + +static const DriverSuffix *FindDriverSuffix(StringRef ProgName) { + // A list of known driver suffixes. Suffixes are compared against the + // program name in order. If there is a match, the frontend type if updated as + // necessary by applying the ModeFlag. + static const DriverSuffix DriverSuffixes[] = { + {"clang", nullptr}, + {"clang++", "--driver-mode=g++"}, + {"clang-c++", "--driver-mode=g++"}, + {"clang-cc", nullptr}, + {"clang-cpp", "--driver-mode=cpp"}, + {"clang-g++", "--driver-mode=g++"}, + {"clang-gcc", nullptr}, + {"clang-cl", "--driver-mode=cl"}, + {"cc", nullptr}, + {"cpp", "--driver-mode=cpp"}, + {"cl", "--driver-mode=cl"}, + {"++", "--driver-mode=g++"}, + }; + + for (size_t i = 0; i < llvm::array_lengthof(DriverSuffixes); ++i) + if (ProgName.endswith(DriverSuffixes[i].Suffix)) + return &DriverSuffixes[i]; + return nullptr; +} static void ParseProgName(SmallVectorImpl<const char *> &ArgVector, - std::set<std::string> &SavedStrings, - Driver &TheDriver) -{ - // Try to infer frontend type and default target from the program name. - - // suffixes[] contains the list of known driver suffixes. - // Suffixes are compared against the program name in order. - // If there is a match, the frontend type is updated as necessary (CPP/C++). - // If there is no match, a second round is done after stripping the last - // hyphen and everything following it. This allows using something like - // "clang++-2.9". - - // If there is a match in either the first or second round, - // the function tries to identify a target as prefix. E.g. - // "x86_64-linux-clang" as interpreted as suffix "clang" with - // target prefix "x86_64-linux". If such a target prefix is found, - // is gets added via -target as implicit first argument. - static const struct { - const char *Suffix; - const char *ModeFlag; - } suffixes [] = { - { "clang", nullptr }, - { "clang++", "--driver-mode=g++" }, - { "clang-c++", "--driver-mode=g++" }, - { "clang-cc", nullptr }, - { "clang-cpp", "--driver-mode=cpp" }, - { "clang-g++", "--driver-mode=g++" }, - { "clang-gcc", nullptr }, - { "clang-cl", "--driver-mode=cl" }, - { "cc", nullptr }, - { "cpp", "--driver-mode=cpp" }, - { "cl" , "--driver-mode=cl" }, - { "++", "--driver-mode=g++" }, - }; - std::string ProgName(llvm::sys::path::stem(ArgVector[0])); + std::set<std::string> &SavedStrings) { + // Try to infer frontend type and default target from the program name by + // comparing it against DriverSuffixes in order. + + // If there is a match, the function tries to identify a target as prefix. + // E.g. "x86_64-linux-clang" as interpreted as suffix "clang" with target + // prefix "x86_64-linux". If such a target prefix is found, is gets added via + // -target as implicit first argument. + + std::string ProgName =llvm::sys::path::stem(ArgVector[0]); #ifdef LLVM_ON_WIN32 // Transform to lowercase for case insensitive file systems. - std::transform(ProgName.begin(), ProgName.end(), ProgName.begin(), - toLowercase); + ProgName = StringRef(ProgName).lower(); #endif - StringRef ProgNameRef(ProgName); - StringRef Prefix; - - for (int Components = 2; Components; --Components) { - bool FoundMatch = false; - size_t i; - - for (i = 0; i < sizeof(suffixes) / sizeof(suffixes[0]); ++i) { - if (ProgNameRef.endswith(suffixes[i].Suffix)) { - FoundMatch = true; - SmallVectorImpl<const char *>::iterator it = ArgVector.begin(); - if (it != ArgVector.end()) - ++it; - if (suffixes[i].ModeFlag) - ArgVector.insert(it, suffixes[i].ModeFlag); - break; - } - } - if (FoundMatch) { - StringRef::size_type LastComponent = ProgNameRef.rfind('-', - ProgNameRef.size() - strlen(suffixes[i].Suffix)); - if (LastComponent != StringRef::npos) - Prefix = ProgNameRef.slice(0, LastComponent); - break; - } + StringRef ProgNameRef = ProgName; + const DriverSuffix *DS = FindDriverSuffix(ProgNameRef); - StringRef::size_type LastComponent = ProgNameRef.rfind('-'); - if (LastComponent == StringRef::npos) - break; - ProgNameRef = ProgNameRef.slice(0, LastComponent); + if (!DS) { + // Try again after stripping any trailing version number: + // clang++3.5 -> clang++ + ProgNameRef = ProgNameRef.rtrim("0123456789."); + DS = FindDriverSuffix(ProgNameRef); } - if (Prefix.empty()) - return; - - std::string IgnoredError; - if (llvm::TargetRegistry::lookupTarget(Prefix, IgnoredError)) { - SmallVectorImpl<const char *>::iterator it = ArgVector.begin(); - if (it != ArgVector.end()) - ++it; - const char* Strings[] = - { SaveStringInSet(SavedStrings, std::string("-target")), - SaveStringInSet(SavedStrings, Prefix) }; - ArgVector.insert(it, Strings, Strings + llvm::array_lengthof(Strings)); + if (!DS) { + // Try again after stripping trailing -component. + // clang++-tot -> clang++ + ProgNameRef = ProgNameRef.slice(0, ProgNameRef.rfind('-')); + DS = FindDriverSuffix(ProgNameRef); + } + + if (DS) { + if (const char *Flag = DS->ModeFlag) { + // Add Flag to the arguments. + auto it = ArgVector.begin(); + if (it != ArgVector.end()) + ++it; + ArgVector.insert(it, Flag); + } + + StringRef::size_type LastComponent = ProgNameRef.rfind( + '-', ProgNameRef.size() - strlen(DS->Suffix)); + if (LastComponent == StringRef::npos) + return; + + // Infer target from the prefix. + StringRef Prefix = ProgNameRef.slice(0, LastComponent); + std::string IgnoredError; + if (llvm::TargetRegistry::lookupTarget(Prefix, IgnoredError)) { + auto it = ArgVector.begin(); + if (it != ArgVector.end()) + ++it; + const char *arr[] = { "-target", GetStableCStr(SavedStrings, Prefix) }; + ArgVector.insert(it, std::begin(arr), std::end(arr)); + } } } @@ -286,21 +295,97 @@ namespace { public: StringSetSaver(std::set<std::string> &Storage) : Storage(Storage) {} const char *SaveString(const char *Str) override { - return SaveStringInSet(Storage, Str); + return GetStableCStr(Storage, Str); } private: std::set<std::string> &Storage; }; } +static void SetBackdoorDriverOutputsFromEnvVars(Driver &TheDriver) { + // Handle CC_PRINT_OPTIONS and CC_PRINT_OPTIONS_FILE. + TheDriver.CCPrintOptions = !!::getenv("CC_PRINT_OPTIONS"); + if (TheDriver.CCPrintOptions) + TheDriver.CCPrintOptionsFilename = ::getenv("CC_PRINT_OPTIONS_FILE"); + + // Handle CC_PRINT_HEADERS and CC_PRINT_HEADERS_FILE. + TheDriver.CCPrintHeaders = !!::getenv("CC_PRINT_HEADERS"); + if (TheDriver.CCPrintHeaders) + TheDriver.CCPrintHeadersFilename = ::getenv("CC_PRINT_HEADERS_FILE"); + + // Handle CC_LOG_DIAGNOSTICS and CC_LOG_DIAGNOSTICS_FILE. + TheDriver.CCLogDiagnostics = !!::getenv("CC_LOG_DIAGNOSTICS"); + if (TheDriver.CCLogDiagnostics) + TheDriver.CCLogDiagnosticsFilename = ::getenv("CC_LOG_DIAGNOSTICS_FILE"); +} + +static void FixupDiagPrefixExeName(TextDiagnosticPrinter *DiagClient, + const std::string &Path) { + // If the clang binary happens to be named cl.exe for compatibility reasons, + // use clang-cl.exe as the prefix to avoid confusion between clang and MSVC. + StringRef ExeBasename(llvm::sys::path::filename(Path)); + if (ExeBasename.equals_lower("cl.exe")) + ExeBasename = "clang-cl.exe"; + DiagClient->setPrefix(ExeBasename); +} + +// This lets us create the DiagnosticsEngine with a properly-filled-out +// DiagnosticOptions instance. +static DiagnosticOptions * +CreateAndPopulateDiagOpts(SmallVectorImpl<const char *> &argv) { + auto *DiagOpts = new DiagnosticOptions; + std::unique_ptr<OptTable> Opts(createDriverOptTable()); + unsigned MissingArgIndex, MissingArgCount; + std::unique_ptr<InputArgList> Args(Opts->ParseArgs( + argv.begin() + 1, argv.end(), MissingArgIndex, MissingArgCount)); + // We ignore MissingArgCount and the return value of ParseDiagnosticArgs. + // Any errors that would be diagnosed here will also be diagnosed later, + // when the DiagnosticsEngine actually exists. + (void) ParseDiagnosticArgs(*DiagOpts, *Args); + return DiagOpts; +} + +static void SetInstallDir(SmallVectorImpl<const char *> &argv, + Driver &TheDriver) { + // Attempt to find the original path used to invoke the driver, to determine + // the installed path. We do this manually, because we want to support that + // path being a symlink. + SmallString<128> InstalledPath(argv[0]); + + // Do a PATH lookup, if there are no directory components. + if (llvm::sys::path::filename(InstalledPath) == InstalledPath) + if (llvm::ErrorOr<std::string> Tmp = llvm::sys::findProgramByName( + llvm::sys::path::filename(InstalledPath.str()))) + InstalledPath = *Tmp; + llvm::sys::fs::make_absolute(InstalledPath); + InstalledPath = llvm::sys::path::parent_path(InstalledPath); + if (llvm::sys::fs::exists(InstalledPath.c_str())) + TheDriver.setInstalledDir(InstalledPath); +} + +static int ExecuteCC1Tool(ArrayRef<const char *> argv, StringRef Tool) { + void *GetExecutablePathVP = (void *)(intptr_t) GetExecutablePath; + if (Tool == "") + return cc1_main(argv.slice(2), argv[0], GetExecutablePathVP); + if (Tool == "as") + return cc1as_main(argv.slice(2), argv[0], GetExecutablePathVP); + + // Reject unknown tools. + llvm::errs() << "error: unknown integrated tool '" << Tool << "'\n"; + return 1; +} + int main(int argc_, const char **argv_) { llvm::sys::PrintStackTraceOnErrorSignal(); llvm::PrettyStackTraceProgram X(argc_, argv_); + if (llvm::sys::Process::FixupStandardFileDescriptors()) + return 1; + SmallVector<const char *, 256> argv; llvm::SpecificBumpPtrAllocator<char> ArgAllocator; std::error_code EC = llvm::sys::Process::GetArgumentVector( - argv, ArrayRef<const char *>(argv_, argc_), ArgAllocator); + argv, llvm::makeArrayRef(argv_, argc_), ArgAllocator); if (EC) { llvm::errs() << "error: couldn't get arguments: " << EC.message() << '\n'; return 1; @@ -308,26 +393,33 @@ int main(int argc_, const char **argv_) { std::set<std::string> SavedStrings; StringSetSaver Saver(SavedStrings); - llvm::cl::ExpandResponseFiles(Saver, llvm::cl::TokenizeGNUCommandLine, argv); - - // Handle -cc1 integrated tools. - if (argv.size() > 1 && StringRef(argv[1]).startswith("-cc1")) { - StringRef Tool = argv[1] + 4; - - if (Tool == "") - return cc1_main(argv.data()+2, argv.data()+argv.size(), argv[0], - (void*) (intptr_t) GetExecutablePath); - if (Tool == "as") - return cc1as_main(argv.data()+2, argv.data()+argv.size(), argv[0], - (void*) (intptr_t) GetExecutablePath); - // Reject unknown tools. - llvm::errs() << "error: unknown integrated tool '" << Tool << "'\n"; - return 1; + // Determines whether we want nullptr markers in argv to indicate response + // files end-of-lines. We only use this for the /LINK driver argument. + bool MarkEOLs = true; + if (argv.size() > 1 && StringRef(argv[1]).startswith("-cc1")) + MarkEOLs = false; + llvm::cl::ExpandResponseFiles(Saver, llvm::cl::TokenizeGNUCommandLine, argv, + MarkEOLs); + + // Handle -cc1 integrated tools, even if -cc1 was expanded from a response + // file. + auto FirstArg = std::find_if(argv.begin() + 1, argv.end(), + [](const char *A) { return A != nullptr; }); + if (FirstArg != argv.end() && StringRef(*FirstArg).startswith("-cc1")) { + // If -cc1 came from a response file, remove the EOL sentinels. + if (MarkEOLs) { + auto newEnd = std::remove(argv.begin(), argv.end(), nullptr); + argv.resize(newEnd - argv.begin()); + } + return ExecuteCC1Tool(argv, argv[1] + 4); } bool CanonicalPrefixes = true; for (int i = 1, size = argv.size(); i < size; ++i) { + // Skip end-of-line response file markers + if (argv[i] == nullptr) + continue; if (StringRef(argv[i]) == "-no-canonical-prefixes") { CanonicalPrefixes = false; break; @@ -343,73 +435,34 @@ int main(int argc_, const char **argv_) { std::string Path = GetExecutablePath(argv[0], CanonicalPrefixes); - IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions; - { - std::unique_ptr<OptTable> Opts(createDriverOptTable()); - unsigned MissingArgIndex, MissingArgCount; - std::unique_ptr<InputArgList> Args(Opts->ParseArgs( - argv.begin() + 1, argv.end(), MissingArgIndex, MissingArgCount)); - // We ignore MissingArgCount and the return value of ParseDiagnosticArgs. - // Any errors that would be diagnosed here will also be diagnosed later, - // when the DiagnosticsEngine actually exists. - (void) ParseDiagnosticArgs(*DiagOpts, *Args); - } - // Now we can create the DiagnosticsEngine with a properly-filled-out - // DiagnosticOptions instance. + IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = + CreateAndPopulateDiagOpts(argv); + TextDiagnosticPrinter *DiagClient = new TextDiagnosticPrinter(llvm::errs(), &*DiagOpts); - - // If the clang binary happens to be named cl.exe for compatibility reasons, - // use clang-cl.exe as the prefix to avoid confusion between clang and MSVC. - StringRef ExeBasename(llvm::sys::path::filename(Path)); - if (ExeBasename.equals_lower("cl.exe")) - ExeBasename = "clang-cl.exe"; - DiagClient->setPrefix(ExeBasename); + FixupDiagPrefixExeName(DiagClient, Path); IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagClient); - ProcessWarningOptions(Diags, *DiagOpts, /*ReportDiags=*/false); - - Driver TheDriver(Path, llvm::sys::getDefaultTargetTriple(), Diags); - // Attempt to find the original path used to invoke the driver, to determine - // the installed path. We do this manually, because we want to support that - // path being a symlink. - { - SmallString<128> InstalledPath(argv[0]); - - // Do a PATH lookup, if there are no directory components. - if (llvm::sys::path::filename(InstalledPath) == InstalledPath) { - std::string Tmp = llvm::sys::FindProgramByName( - llvm::sys::path::filename(InstalledPath.str())); - if (!Tmp.empty()) - InstalledPath = Tmp; - } - llvm::sys::fs::make_absolute(InstalledPath); - InstalledPath = llvm::sys::path::parent_path(InstalledPath); - bool exists; - if (!llvm::sys::fs::exists(InstalledPath.str(), exists) && exists) - TheDriver.setInstalledDir(InstalledPath); + if (!DiagOpts->DiagnosticSerializationFile.empty()) { + auto SerializedConsumer = + clang::serialized_diags::create(DiagOpts->DiagnosticSerializationFile, + &*DiagOpts, /*MergeChildRecords=*/true); + Diags.setClient(new ChainedDiagnosticConsumer( + Diags.takeClient(), std::move(SerializedConsumer))); } - llvm::InitializeAllTargets(); - ParseProgName(argv, SavedStrings, TheDriver); + ProcessWarningOptions(Diags, *DiagOpts, /*ReportDiags=*/false); - // Handle CC_PRINT_OPTIONS and CC_PRINT_OPTIONS_FILE. - TheDriver.CCPrintOptions = !!::getenv("CC_PRINT_OPTIONS"); - if (TheDriver.CCPrintOptions) - TheDriver.CCPrintOptionsFilename = ::getenv("CC_PRINT_OPTIONS_FILE"); + Driver TheDriver(Path, llvm::sys::getDefaultTargetTriple(), Diags); + SetInstallDir(argv, TheDriver); - // Handle CC_PRINT_HEADERS and CC_PRINT_HEADERS_FILE. - TheDriver.CCPrintHeaders = !!::getenv("CC_PRINT_HEADERS"); - if (TheDriver.CCPrintHeaders) - TheDriver.CCPrintHeadersFilename = ::getenv("CC_PRINT_HEADERS_FILE"); + llvm::InitializeAllTargets(); + ParseProgName(argv, SavedStrings); - // Handle CC_LOG_DIAGNOSTICS and CC_LOG_DIAGNOSTICS_FILE. - TheDriver.CCLogDiagnostics = !!::getenv("CC_LOG_DIAGNOSTICS"); - if (TheDriver.CCLogDiagnostics) - TheDriver.CCLogDiagnosticsFilename = ::getenv("CC_LOG_DIAGNOSTICS_FILE"); + SetBackdoorDriverOutputsFromEnvVars(TheDriver); std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(argv)); int Res = 0; @@ -420,14 +473,17 @@ int main(int argc_, const char **argv_) { // Force a crash to test the diagnostics. if (::getenv("FORCE_CLANG_DIAGNOSTICS_CRASH")) { Diags.Report(diag::err_drv_force_crash) << "FORCE_CLANG_DIAGNOSTICS_CRASH"; - const Command *FailingCommand = nullptr; - FailingCommands.push_back(std::make_pair(-1, FailingCommand)); + + // Pretend that every command failed. + FailingCommands.clear(); + for (const auto &J : C->getJobs()) + if (const Command *C = dyn_cast<Command>(&J)) + FailingCommands.push_back(std::make_pair(-1, C)); } - for (SmallVectorImpl< std::pair<int, const Command *> >::iterator it = - FailingCommands.begin(), ie = FailingCommands.end(); it != ie; ++it) { - int CommandRes = it->first; - const Command *FailingCommand = it->second; + for (const auto &P : FailingCommands) { + int CommandRes = P.first; + const Command *FailingCommand = P.second; if (!Res) Res = CommandRes; @@ -440,15 +496,17 @@ int main(int argc_, const char **argv_) { DiagnoseCrash |= CommandRes == 3; #endif if (DiagnoseCrash) { - TheDriver.generateCompilationDiagnostics(*C, FailingCommand); + TheDriver.generateCompilationDiagnostics(*C, *FailingCommand); break; } } + Diags.getClient()->finish(); + // If any timers were active but haven't been destroyed yet, print their // results now. This happens in -disable-free mode. llvm::TimerGroup::printAll(llvm::errs()); - + llvm::llvm_shutdown(); #ifdef LLVM_ON_WIN32 |