1//===--- Job.cpp - Command to Execute -------------------------------------===// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9 10#include "clang/Driver/Driver.h" 11#include "clang/Driver/DriverDiagnostic.h" 12#include "clang/Driver/Job.h" 13#include "clang/Driver/Tool.h" 14#include "clang/Driver/ToolChain.h" 15#include "llvm/ADT/STLExtras.h" 16#include "llvm/ADT/StringRef.h" 17#include "llvm/ADT/StringSwitch.h" 18#include "llvm/Support/Program.h" 19#include "llvm/Support/raw_ostream.h" 20#include <cassert> 21using namespace clang::driver; 22using llvm::raw_ostream; 23using llvm::StringRef; 24 25Job::~Job() {} 26 27Command::Command(const Action &_Source, const Tool &_Creator, 28 const char *_Executable, 29 const ArgStringList &_Arguments) 30 : Job(CommandClass), Source(_Source), Creator(_Creator), 31 Executable(_Executable), Arguments(_Arguments) {} 32 33static int skipArgs(const char *Flag) { 34 // These flags are all of the form -Flag <Arg> and are treated as two 35 // arguments. Therefore, we need to skip the flag and the next argument. 36 bool Res = llvm::StringSwitch<bool>(Flag) 37 .Cases("-I", "-MF", "-MT", "-MQ", true) 38 .Cases("-o", "-coverage-file", "-dependency-file", true) 39 .Cases("-fdebug-compilation-dir", "-idirafter", true) 40 .Cases("-include", "-include-pch", "-internal-isystem", true) 41 .Cases("-internal-externc-isystem", "-iprefix", "-iwithprefix", true) 42 .Cases("-iwithprefixbefore", "-isysroot", "-isystem", "-iquote", true) 43 .Cases("-resource-dir", "-serialize-diagnostic-file", true) 44 .Cases("-dwarf-debug-flags", "-ivfsoverlay", true) 45 .Default(false); 46 47 // Match found. 48 if (Res) 49 return 2; 50 51 // The remaining flags are treated as a single argument. 52 53 // These flags are all of the form -Flag and have no second argument. 54 Res = llvm::StringSwitch<bool>(Flag) 55 .Cases("-M", "-MM", "-MG", "-MP", "-MD", true) 56 .Case("-MMD", true) 57 .Default(false); 58 59 // Match found. 60 if (Res) 61 return 1; 62 63 // These flags are treated as a single argument (e.g., -F<Dir>). 64 StringRef FlagRef(Flag); 65 if (FlagRef.startswith("-F") || FlagRef.startswith("-I") || 66 FlagRef.startswith("-fmodules-cache-path=")) 67 return 1; 68 69 return 0; 70} 71 72static bool quoteNextArg(const char *flag) { 73 return llvm::StringSwitch<bool>(flag) 74 .Case("-D", true) 75 .Default(false); 76} 77 78static void PrintArg(raw_ostream &OS, const char *Arg, bool Quote) { 79 const bool Escape = std::strpbrk(Arg, "\"\\$"); 80 81 if (!Quote && !Escape) { 82 OS << Arg; 83 return; 84 } 85 86 // Quote and escape. This isn't really complete, but good enough. 87 OS << '"'; 88 while (const char c = *Arg++) { 89 if (c == '"' || c == '\\' || c == '$') 90 OS << '\\'; 91 OS << c; 92 } 93 OS << '"'; 94} 95 96void Command::Print(raw_ostream &OS, const char *Terminator, bool Quote, 97 bool CrashReport) const { 98 OS << " \"" << Executable << '"'; 99 100 for (size_t i = 0, e = Arguments.size(); i < e; ++i) { 101 const char *const Arg = Arguments[i]; 102 103 if (CrashReport) { 104 if (int Skip = skipArgs(Arg)) { 105 i += Skip - 1; 106 continue; 107 } 108 } 109 110 OS << ' '; 111 PrintArg(OS, Arg, Quote); 112 113 if (CrashReport && quoteNextArg(Arg) && i + 1 < e) { 114 OS << ' '; 115 PrintArg(OS, Arguments[++i], true); 116 } 117 } 118 OS << Terminator; 119} 120 121int Command::Execute(const StringRef **Redirects, std::string *ErrMsg, 122 bool *ExecutionFailed) const { 123 SmallVector<const char*, 128> Argv; 124 Argv.push_back(Executable); 125 for (size_t i = 0, e = Arguments.size(); i != e; ++i) 126 Argv.push_back(Arguments[i]); 127 Argv.push_back(nullptr); 128 129 return llvm::sys::ExecuteAndWait(Executable, Argv.data(), /*env*/ nullptr, 130 Redirects, /*secondsToWait*/ 0, 131 /*memoryLimit*/ 0, ErrMsg, ExecutionFailed); 132} 133 134FallbackCommand::FallbackCommand(const Action &Source_, const Tool &Creator_, 135 const char *Executable_, 136 const ArgStringList &Arguments_, 137 Command *Fallback_) 138 : Command(Source_, Creator_, Executable_, Arguments_), Fallback(Fallback_) { 139} 140 141void FallbackCommand::Print(raw_ostream &OS, const char *Terminator, 142 bool Quote, bool CrashReport) const { 143 Command::Print(OS, "", Quote, CrashReport); 144 OS << " ||"; 145 Fallback->Print(OS, Terminator, Quote, CrashReport); 146} 147 148static bool ShouldFallback(int ExitCode) { 149 // FIXME: We really just want to fall back for internal errors, such 150 // as when some symbol cannot be mangled, when we should be able to 151 // parse something but can't, etc. 152 return ExitCode != 0; 153} 154 155int FallbackCommand::Execute(const StringRef **Redirects, std::string *ErrMsg, 156 bool *ExecutionFailed) const { 157 int PrimaryStatus = Command::Execute(Redirects, ErrMsg, ExecutionFailed); 158 if (!ShouldFallback(PrimaryStatus)) 159 return PrimaryStatus; 160 161 // Clear ExecutionFailed and ErrMsg before falling back. 162 if (ErrMsg) 163 ErrMsg->clear(); 164 if (ExecutionFailed) 165 *ExecutionFailed = false; 166 167 const Driver &D = getCreator().getToolChain().getDriver(); 168 D.Diag(diag::warn_drv_invoking_fallback) << Fallback->getExecutable(); 169 170 int SecondaryStatus = Fallback->Execute(Redirects, ErrMsg, ExecutionFailed); 171 return SecondaryStatus; 172} 173 174JobList::JobList() : Job(JobListClass) {} 175 176JobList::~JobList() { 177 for (iterator it = begin(), ie = end(); it != ie; ++it) 178 delete *it; 179} 180 181void JobList::Print(raw_ostream &OS, const char *Terminator, bool Quote, 182 bool CrashReport) const { 183 for (const_iterator it = begin(), ie = end(); it != ie; ++it) 184 (*it)->Print(OS, Terminator, Quote, CrashReport); 185} 186 187void JobList::clear() { 188 DeleteContainerPointers(Jobs); 189} 190