Path: blob/main/contrib/llvm-project/clang/tools/driver/driver.cpp
35236 views
//===-- driver.cpp - Clang GCC-Compatible Driver --------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This is the entry point to the clang driver; it is a thin wrapper9// for functionality in the Driver clang library.10//11//===----------------------------------------------------------------------===//1213#include "clang/Driver/Driver.h"14#include "clang/Basic/DiagnosticOptions.h"15#include "clang/Basic/HeaderInclude.h"16#include "clang/Basic/Stack.h"17#include "clang/Config/config.h"18#include "clang/Driver/Compilation.h"19#include "clang/Driver/DriverDiagnostic.h"20#include "clang/Driver/Options.h"21#include "clang/Driver/ToolChain.h"22#include "clang/Frontend/ChainedDiagnosticConsumer.h"23#include "clang/Frontend/CompilerInvocation.h"24#include "clang/Frontend/SerializedDiagnosticPrinter.h"25#include "clang/Frontend/TextDiagnosticPrinter.h"26#include "clang/Frontend/Utils.h"27#include "llvm/ADT/ArrayRef.h"28#include "llvm/ADT/SmallString.h"29#include "llvm/ADT/SmallVector.h"30#include "llvm/ADT/StringSet.h"31#include "llvm/Option/ArgList.h"32#include "llvm/Option/OptTable.h"33#include "llvm/Option/Option.h"34#include "llvm/Support/BuryPointer.h"35#include "llvm/Support/CommandLine.h"36#include "llvm/Support/CrashRecoveryContext.h"37#include "llvm/Support/ErrorHandling.h"38#include "llvm/Support/FileSystem.h"39#include "llvm/Support/LLVMDriver.h"40#include "llvm/Support/Path.h"41#include "llvm/Support/PrettyStackTrace.h"42#include "llvm/Support/Process.h"43#include "llvm/Support/Program.h"44#include "llvm/Support/Signals.h"45#include "llvm/Support/StringSaver.h"46#include "llvm/Support/TargetSelect.h"47#include "llvm/Support/Timer.h"48#include "llvm/Support/raw_ostream.h"49#include "llvm/TargetParser/Host.h"50#include <memory>51#include <optional>52#include <set>53#include <system_error>54using namespace clang;55using namespace clang::driver;56using namespace llvm::opt;5758std::string GetExecutablePath(const char *Argv0, bool CanonicalPrefixes) {59if (!CanonicalPrefixes) {60SmallString<128> ExecutablePath(Argv0);61// Do a PATH lookup if Argv0 isn't a valid path.62if (!llvm::sys::fs::exists(ExecutablePath))63if (llvm::ErrorOr<std::string> P =64llvm::sys::findProgramByName(ExecutablePath))65ExecutablePath = *P;66return std::string(ExecutablePath);67}6869// This just needs to be some symbol in the binary; C++ doesn't70// allow taking the address of ::main however.71void *P = (void*) (intptr_t) GetExecutablePath;72return llvm::sys::fs::getMainExecutable(Argv0, P);73}7475static const char *GetStableCStr(llvm::StringSet<> &SavedStrings, StringRef S) {76return SavedStrings.insert(S).first->getKeyData();77}7879extern int cc1_main(ArrayRef<const char *> Argv, const char *Argv0,80void *MainAddr);81extern int cc1as_main(ArrayRef<const char *> Argv, const char *Argv0,82void *MainAddr);83extern int cc1gen_reproducer_main(ArrayRef<const char *> Argv,84const char *Argv0, void *MainAddr,85const llvm::ToolContext &);8687static void insertTargetAndModeArgs(const ParsedClangName &NameParts,88SmallVectorImpl<const char *> &ArgVector,89llvm::StringSet<> &SavedStrings) {90// Put target and mode arguments at the start of argument list so that91// arguments specified in command line could override them. Avoid putting92// them at index 0, as an option like '-cc1' must remain the first.93int InsertionPoint = 0;94if (ArgVector.size() > 0)95++InsertionPoint;9697if (NameParts.DriverMode) {98// Add the mode flag to the arguments.99ArgVector.insert(ArgVector.begin() + InsertionPoint,100GetStableCStr(SavedStrings, NameParts.DriverMode));101}102103if (NameParts.TargetIsValid) {104const char *arr[] = {"-target", GetStableCStr(SavedStrings,105NameParts.TargetPrefix)};106ArgVector.insert(ArgVector.begin() + InsertionPoint,107std::begin(arr), std::end(arr));108}109}110111static void getCLEnvVarOptions(std::string &EnvValue, llvm::StringSaver &Saver,112SmallVectorImpl<const char *> &Opts) {113llvm::cl::TokenizeWindowsCommandLine(EnvValue, Saver, Opts);114// The first instance of '#' should be replaced with '=' in each option.115for (const char *Opt : Opts)116if (char *NumberSignPtr = const_cast<char *>(::strchr(Opt, '#')))117*NumberSignPtr = '=';118}119120template <class T>121static T checkEnvVar(const char *EnvOptSet, const char *EnvOptFile,122std::string &OptFile) {123const char *Str = ::getenv(EnvOptSet);124if (!Str)125return T{};126127T OptVal = Str;128if (const char *Var = ::getenv(EnvOptFile))129OptFile = Var;130return OptVal;131}132133static bool SetBackdoorDriverOutputsFromEnvVars(Driver &TheDriver) {134TheDriver.CCPrintOptions =135checkEnvVar<bool>("CC_PRINT_OPTIONS", "CC_PRINT_OPTIONS_FILE",136TheDriver.CCPrintOptionsFilename);137if (checkEnvVar<bool>("CC_PRINT_HEADERS", "CC_PRINT_HEADERS_FILE",138TheDriver.CCPrintHeadersFilename)) {139TheDriver.CCPrintHeadersFormat = HIFMT_Textual;140TheDriver.CCPrintHeadersFiltering = HIFIL_None;141} else {142std::string EnvVar = checkEnvVar<std::string>(143"CC_PRINT_HEADERS_FORMAT", "CC_PRINT_HEADERS_FILE",144TheDriver.CCPrintHeadersFilename);145if (!EnvVar.empty()) {146TheDriver.CCPrintHeadersFormat =147stringToHeaderIncludeFormatKind(EnvVar.c_str());148if (!TheDriver.CCPrintHeadersFormat) {149TheDriver.Diag(clang::diag::err_drv_print_header_env_var)150<< 0 << EnvVar;151return false;152}153154const char *FilteringStr = ::getenv("CC_PRINT_HEADERS_FILTERING");155HeaderIncludeFilteringKind Filtering;156if (!stringToHeaderIncludeFiltering(FilteringStr, Filtering)) {157TheDriver.Diag(clang::diag::err_drv_print_header_env_var)158<< 1 << FilteringStr;159return false;160}161162if ((TheDriver.CCPrintHeadersFormat == HIFMT_Textual &&163Filtering != HIFIL_None) ||164(TheDriver.CCPrintHeadersFormat == HIFMT_JSON &&165Filtering != HIFIL_Only_Direct_System)) {166TheDriver.Diag(clang::diag::err_drv_print_header_env_var_combination)167<< EnvVar << FilteringStr;168return false;169}170TheDriver.CCPrintHeadersFiltering = Filtering;171}172}173174TheDriver.CCLogDiagnostics =175checkEnvVar<bool>("CC_LOG_DIAGNOSTICS", "CC_LOG_DIAGNOSTICS_FILE",176TheDriver.CCLogDiagnosticsFilename);177TheDriver.CCPrintProcessStats =178checkEnvVar<bool>("CC_PRINT_PROC_STAT", "CC_PRINT_PROC_STAT_FILE",179TheDriver.CCPrintStatReportFilename);180TheDriver.CCPrintInternalStats =181checkEnvVar<bool>("CC_PRINT_INTERNAL_STAT", "CC_PRINT_INTERNAL_STAT_FILE",182TheDriver.CCPrintInternalStatReportFilename);183184return true;185}186187static void FixupDiagPrefixExeName(TextDiagnosticPrinter *DiagClient,188const std::string &Path) {189// If the clang binary happens to be named cl.exe for compatibility reasons,190// use clang-cl.exe as the prefix to avoid confusion between clang and MSVC.191StringRef ExeBasename(llvm::sys::path::stem(Path));192if (ExeBasename.equals_insensitive("cl"))193ExeBasename = "clang-cl";194DiagClient->setPrefix(std::string(ExeBasename));195}196197static int ExecuteCC1Tool(SmallVectorImpl<const char *> &ArgV,198const llvm::ToolContext &ToolContext) {199// If we call the cc1 tool from the clangDriver library (through200// Driver::CC1Main), we need to clean up the options usage count. The options201// are currently global, and they might have been used previously by the202// driver.203llvm::cl::ResetAllOptionOccurrences();204205llvm::BumpPtrAllocator A;206llvm::cl::ExpansionContext ECtx(A, llvm::cl::TokenizeGNUCommandLine);207if (llvm::Error Err = ECtx.expandResponseFiles(ArgV)) {208llvm::errs() << toString(std::move(Err)) << '\n';209return 1;210}211StringRef Tool = ArgV[1];212void *GetExecutablePathVP = (void *)(intptr_t)GetExecutablePath;213if (Tool == "-cc1")214return cc1_main(ArrayRef(ArgV).slice(1), ArgV[0], GetExecutablePathVP);215if (Tool == "-cc1as")216return cc1as_main(ArrayRef(ArgV).slice(2), ArgV[0], GetExecutablePathVP);217if (Tool == "-cc1gen-reproducer")218return cc1gen_reproducer_main(ArrayRef(ArgV).slice(2), ArgV[0],219GetExecutablePathVP, ToolContext);220// Reject unknown tools.221llvm::errs()222<< "error: unknown integrated tool '" << Tool << "'. "223<< "Valid tools include '-cc1', '-cc1as' and '-cc1gen-reproducer'.\n";224return 1;225}226227int clang_main(int Argc, char **Argv, const llvm::ToolContext &ToolContext) {228noteBottomOfStack();229llvm::setBugReportMsg("PLEASE submit a bug report to " BUG_REPORT_URL230" and include the crash backtrace, preprocessed "231"source, and associated run script.\n");232SmallVector<const char *, 256> Args(Argv, Argv + Argc);233234if (llvm::sys::Process::FixupStandardFileDescriptors())235return 1;236237llvm::InitializeAllTargets();238239llvm::BumpPtrAllocator A;240llvm::StringSaver Saver(A);241242const char *ProgName =243ToolContext.NeedsPrependArg ? ToolContext.PrependArg : ToolContext.Path;244245bool ClangCLMode =246IsClangCL(getDriverMode(ProgName, llvm::ArrayRef(Args).slice(1)));247248if (llvm::Error Err = expandResponseFiles(Args, ClangCLMode, A)) {249llvm::errs() << toString(std::move(Err)) << '\n';250return 1;251}252253// Handle -cc1 integrated tools.254if (Args.size() >= 2 && StringRef(Args[1]).starts_with("-cc1"))255return ExecuteCC1Tool(Args, ToolContext);256257// Handle options that need handling before the real command line parsing in258// Driver::BuildCompilation()259bool CanonicalPrefixes = true;260for (int i = 1, size = Args.size(); i < size; ++i) {261// Skip end-of-line response file markers262if (Args[i] == nullptr)263continue;264if (StringRef(Args[i]) == "-canonical-prefixes")265CanonicalPrefixes = true;266else if (StringRef(Args[i]) == "-no-canonical-prefixes")267CanonicalPrefixes = false;268}269270// Handle CL and _CL_ which permits additional command line options to be271// prepended or appended.272if (ClangCLMode) {273// Arguments in "CL" are prepended.274std::optional<std::string> OptCL = llvm::sys::Process::GetEnv("CL");275if (OptCL) {276SmallVector<const char *, 8> PrependedOpts;277getCLEnvVarOptions(*OptCL, Saver, PrependedOpts);278279// Insert right after the program name to prepend to the argument list.280Args.insert(Args.begin() + 1, PrependedOpts.begin(), PrependedOpts.end());281}282// Arguments in "_CL_" are appended.283std::optional<std::string> Opt_CL_ = llvm::sys::Process::GetEnv("_CL_");284if (Opt_CL_) {285SmallVector<const char *, 8> AppendedOpts;286getCLEnvVarOptions(*Opt_CL_, Saver, AppendedOpts);287288// Insert at the end of the argument list to append.289Args.append(AppendedOpts.begin(), AppendedOpts.end());290}291}292293llvm::StringSet<> SavedStrings;294// Handle CCC_OVERRIDE_OPTIONS, used for editing a command line behind the295// scenes.296if (const char *OverrideStr = ::getenv("CCC_OVERRIDE_OPTIONS")) {297// FIXME: Driver shouldn't take extra initial argument.298driver::applyOverrideOptions(Args, OverrideStr, SavedStrings,299&llvm::errs());300}301302std::string Path = GetExecutablePath(ToolContext.Path, CanonicalPrefixes);303304// Whether the cc1 tool should be called inside the current process, or if we305// should spawn a new clang subprocess (old behavior).306// Not having an additional process saves some execution time of Windows,307// and makes debugging and profiling easier.308bool UseNewCC1Process = CLANG_SPAWN_CC1;309for (const char *Arg : Args)310UseNewCC1Process = llvm::StringSwitch<bool>(Arg)311.Case("-fno-integrated-cc1", true)312.Case("-fintegrated-cc1", false)313.Default(UseNewCC1Process);314315IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts =316CreateAndPopulateDiagOpts(Args);317318TextDiagnosticPrinter *DiagClient319= new TextDiagnosticPrinter(llvm::errs(), &*DiagOpts);320FixupDiagPrefixExeName(DiagClient, ProgName);321322IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());323324DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagClient);325326if (!DiagOpts->DiagnosticSerializationFile.empty()) {327auto SerializedConsumer =328clang::serialized_diags::create(DiagOpts->DiagnosticSerializationFile,329&*DiagOpts, /*MergeChildRecords=*/true);330Diags.setClient(new ChainedDiagnosticConsumer(331Diags.takeClient(), std::move(SerializedConsumer)));332}333334ProcessWarningOptions(Diags, *DiagOpts, /*ReportDiags=*/false);335336Driver TheDriver(Path, llvm::sys::getDefaultTargetTriple(), Diags);337auto TargetAndMode = ToolChain::getTargetAndModeFromProgramName(ProgName);338TheDriver.setTargetAndMode(TargetAndMode);339// If -canonical-prefixes is set, GetExecutablePath will have resolved Path340// to the llvm driver binary, not clang. In this case, we need to use341// PrependArg which should be clang-*. Checking just CanonicalPrefixes is342// safe even in the normal case because PrependArg will be null so343// setPrependArg will be a no-op.344if (ToolContext.NeedsPrependArg || CanonicalPrefixes)345TheDriver.setPrependArg(ToolContext.PrependArg);346347insertTargetAndModeArgs(TargetAndMode, Args, SavedStrings);348349if (!SetBackdoorDriverOutputsFromEnvVars(TheDriver))350return 1;351352if (!UseNewCC1Process) {353TheDriver.CC1Main = [ToolContext](SmallVectorImpl<const char *> &ArgV) {354return ExecuteCC1Tool(ArgV, ToolContext);355};356// Ensure the CC1Command actually catches cc1 crashes357llvm::CrashRecoveryContext::Enable();358}359360std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(Args));361362Driver::ReproLevel ReproLevel = Driver::ReproLevel::OnCrash;363if (Arg *A = C->getArgs().getLastArg(options::OPT_gen_reproducer_eq)) {364auto Level =365llvm::StringSwitch<std::optional<Driver::ReproLevel>>(A->getValue())366.Case("off", Driver::ReproLevel::Off)367.Case("crash", Driver::ReproLevel::OnCrash)368.Case("error", Driver::ReproLevel::OnError)369.Case("always", Driver::ReproLevel::Always)370.Default(std::nullopt);371if (!Level) {372llvm::errs() << "Unknown value for " << A->getSpelling() << ": '"373<< A->getValue() << "'\n";374return 1;375}376ReproLevel = *Level;377}378if (!!::getenv("FORCE_CLANG_DIAGNOSTICS_CRASH"))379ReproLevel = Driver::ReproLevel::Always;380381int Res = 1;382bool IsCrash = false;383Driver::CommandStatus CommandStatus = Driver::CommandStatus::Ok;384// Pretend the first command failed if ReproStatus is Always.385const Command *FailingCommand = nullptr;386if (!C->getJobs().empty())387FailingCommand = &*C->getJobs().begin();388if (C && !C->containsError()) {389SmallVector<std::pair<int, const Command *>, 4> FailingCommands;390Res = TheDriver.ExecuteCompilation(*C, FailingCommands);391392for (const auto &P : FailingCommands) {393int CommandRes = P.first;394FailingCommand = P.second;395if (!Res)396Res = CommandRes;397398// If result status is < 0, then the driver command signalled an error.399// If result status is 70, then the driver command reported a fatal error.400// On Windows, abort will return an exit code of 3. In these cases,401// generate additional diagnostic information if possible.402IsCrash = CommandRes < 0 || CommandRes == 70;403#ifdef _WIN32404IsCrash |= CommandRes == 3;405#endif406#if LLVM_ON_UNIX407// When running in integrated-cc1 mode, the CrashRecoveryContext returns408// the same codes as if the program crashed. See section "Exit Status for409// Commands":410// https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xcu_chap02.html411IsCrash |= CommandRes > 128;412#endif413CommandStatus =414IsCrash ? Driver::CommandStatus::Crash : Driver::CommandStatus::Error;415if (IsCrash)416break;417}418}419420// Print the bug report message that would be printed if we did actually421// crash, but only if we're crashing due to FORCE_CLANG_DIAGNOSTICS_CRASH.422if (::getenv("FORCE_CLANG_DIAGNOSTICS_CRASH"))423llvm::dbgs() << llvm::getBugReportMsg();424if (FailingCommand != nullptr &&425TheDriver.maybeGenerateCompilationDiagnostics(CommandStatus, ReproLevel,426*C, *FailingCommand))427Res = 1;428429Diags.getClient()->finish();430431if (!UseNewCC1Process && IsCrash) {432// When crashing in -fintegrated-cc1 mode, bury the timer pointers, because433// the internal linked list might point to already released stack frames.434llvm::BuryPointer(llvm::TimerGroup::aquireDefaultGroup());435} else {436// If any timers were active but haven't been destroyed yet, print their437// results now. This happens in -disable-free mode.438llvm::TimerGroup::printAll(llvm::errs());439llvm::TimerGroup::clearAll();440}441442#ifdef _WIN32443// Exit status should not be negative on Win32, unless abnormal termination.444// Once abnormal termination was caught, negative status should not be445// propagated.446if (Res < 0)447Res = 1;448#endif449450// If we have multiple failing commands, we return the result of the first451// failing command.452return Res;453}454455456