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/Job.h" 11 #include "InputInfo.h" 12 #include "clang/Driver/Driver.h" 13 #include "clang/Driver/DriverDiagnostic.h" 14 #include "clang/Driver/Tool.h" 15 #include "clang/Driver/ToolChain.h" 16 #include "llvm/ADT/ArrayRef.h" 17 #include "llvm/ADT/STLExtras.h" 18 #include "llvm/ADT/SmallString.h" 19 #include "llvm/ADT/StringRef.h" 20 #include "llvm/ADT/StringSet.h" 21 #include "llvm/ADT/StringSwitch.h" 22 #include "llvm/Support/Path.h" 23 #include "llvm/Support/Program.h" 24 #include "llvm/Support/raw_ostream.h" 25 #include <cassert> 26 using namespace clang::driver; 27 using llvm::raw_ostream; 28 using llvm::StringRef; 29 using llvm::ArrayRef; 30 31 Command::Command(const Action &Source, const Tool &Creator, 32 const char *Executable, const ArgStringList &Arguments, 33 ArrayRef<InputInfo> Inputs) 34 : Source(Source), Creator(Creator), Executable(Executable), 35 Arguments(Arguments), ResponseFile(nullptr) { 36 for (const auto &II : Inputs) 37 if (II.isFilename()) 38 InputFilenames.push_back(II.getFilename()); 39 } 40 41 static int skipArgs(const char *Flag, bool HaveCrashVFS) { 42 // These flags are all of the form -Flag <Arg> and are treated as two 43 // arguments. Therefore, we need to skip the flag and the next argument. 44 bool Res = llvm::StringSwitch<bool>(Flag) 45 .Cases("-MF", "-MT", "-MQ", "-serialize-diagnostic-file", true) 46 .Cases("-o", "-coverage-file", "-dependency-file", true) 47 .Cases("-fdebug-compilation-dir", "-idirafter", true) 48 .Cases("-include", "-include-pch", "-internal-isystem", true) 49 .Cases("-internal-externc-isystem", "-iprefix", "-iwithprefix", true) 50 .Cases("-iwithprefixbefore", "-isystem", "-iquote", true) 51 .Cases("-dwarf-debug-flags", "-ivfsoverlay", true) 52 .Cases("-header-include-file", "-diagnostic-log-file", true) 53 // Some include flags shouldn't be skipped if we have a crash VFS 54 .Cases("-isysroot", "-I", "-F", "-resource-dir", !HaveCrashVFS) 55 .Default(false); 56 57 // Match found. 58 if (Res) 59 return 2; 60 61 // The remaining flags are treated as a single argument. 62 63 // These flags are all of the form -Flag and have no second argument. 64 Res = llvm::StringSwitch<bool>(Flag) 65 .Cases("-M", "-MM", "-MG", "-MP", "-MD", true) 66 .Case("-MMD", true) 67 .Default(false); 68 69 // Match found. 70 if (Res) 71 return 1; 72 73 // These flags are treated as a single argument (e.g., -F<Dir>). 74 StringRef FlagRef(Flag); 75 if ((!HaveCrashVFS && 76 (FlagRef.startswith("-F") || FlagRef.startswith("-I"))) || 77 FlagRef.startswith("-fmodules-cache-path=")) 78 return 1; 79 80 return 0; 81 } 82 83 void Command::printArg(raw_ostream &OS, StringRef Arg, bool Quote) { 84 const bool Escape = Arg.find_first_of("\"\\$") != StringRef::npos; 85 86 if (!Quote && !Escape) { 87 OS << Arg; 88 return; 89 } 90 91 // Quote and escape. This isn't really complete, but good enough. 92 OS << '"'; 93 for (const char c : Arg) { 94 if (c == '"' || c == '\\' || c == '$') 95 OS << '\\'; 96 OS << c; 97 } 98 OS << '"'; 99 } 100 101 void Command::writeResponseFile(raw_ostream &OS) const { 102 // In a file list, we only write the set of inputs to the response file 103 if (Creator.getResponseFilesSupport() == Tool::RF_FileList) { 104 for (const char *Arg : InputFileList) { 105 OS << Arg << '\n'; 106 } 107 return; 108 } 109 110 // In regular response files, we send all arguments to the response file. 111 // Wrapping all arguments in double quotes ensures that both Unix tools and 112 // Windows tools understand the response file. 113 for (const char *Arg : Arguments) { 114 OS << '"'; 115 116 for (; *Arg != '\0'; Arg++) { 117 if (*Arg == '\"' || *Arg == '\\') { 118 OS << '\\'; 119 } 120 OS << *Arg; 121 } 122 123 OS << "\" "; 124 } 125 } 126 127 void Command::buildArgvForResponseFile( 128 llvm::SmallVectorImpl<const char *> &Out) const { 129 // When not a file list, all arguments are sent to the response file. 130 // This leaves us to set the argv to a single parameter, requesting the tool 131 // to read the response file. 132 if (Creator.getResponseFilesSupport() != Tool::RF_FileList) { 133 Out.push_back(Executable); 134 Out.push_back(ResponseFileFlag.c_str()); 135 return; 136 } 137 138 llvm::StringSet<> Inputs; 139 for (const char *InputName : InputFileList) 140 Inputs.insert(InputName); 141 Out.push_back(Executable); 142 // In a file list, build args vector ignoring parameters that will go in the 143 // response file (elements of the InputFileList vector) 144 bool FirstInput = true; 145 for (const char *Arg : Arguments) { 146 if (Inputs.count(Arg) == 0) { 147 Out.push_back(Arg); 148 } else if (FirstInput) { 149 FirstInput = false; 150 Out.push_back(Creator.getResponseFileFlag()); 151 Out.push_back(ResponseFile); 152 } 153 } 154 } 155 156 void Command::Print(raw_ostream &OS, const char *Terminator, bool Quote, 157 CrashReportInfo *CrashInfo) const { 158 // Always quote the exe. 159 OS << ' '; 160 printArg(OS, Executable, /*Quote=*/true); 161 162 llvm::ArrayRef<const char *> Args = Arguments; 163 llvm::SmallVector<const char *, 128> ArgsRespFile; 164 if (ResponseFile != nullptr) { 165 buildArgvForResponseFile(ArgsRespFile); 166 Args = ArrayRef<const char *>(ArgsRespFile).slice(1); // no executable name 167 } 168 169 bool HaveCrashVFS = CrashInfo && !CrashInfo->VFSPath.empty(); 170 for (size_t i = 0, e = Args.size(); i < e; ++i) { 171 const char *const Arg = Args[i]; 172 173 if (CrashInfo) { 174 if (int Skip = skipArgs(Arg, HaveCrashVFS)) { 175 i += Skip - 1; 176 continue; 177 } 178 auto Found = std::find_if(InputFilenames.begin(), InputFilenames.end(), 179 [&Arg](StringRef IF) { return IF == Arg; }); 180 if (Found != InputFilenames.end() && 181 (i == 0 || StringRef(Args[i - 1]) != "-main-file-name")) { 182 // Replace the input file name with the crashinfo's file name. 183 OS << ' '; 184 StringRef ShortName = llvm::sys::path::filename(CrashInfo->Filename); 185 printArg(OS, ShortName.str().c_str(), Quote); 186 continue; 187 } 188 } 189 190 OS << ' '; 191 printArg(OS, Arg, Quote); 192 } 193 194 if (CrashInfo && HaveCrashVFS) { 195 OS << ' '; 196 printArg(OS, "-ivfsoverlay", Quote); 197 OS << ' '; 198 printArg(OS, CrashInfo->VFSPath.str().c_str(), Quote); 199 200 // Insert -fmodules-cache-path and use the relative module directory 201 // <name>.cache/vfs/modules where we already dumped the modules. 202 SmallString<128> RelModCacheDir = llvm::sys::path::parent_path( 203 llvm::sys::path::parent_path(CrashInfo->VFSPath)); 204 llvm::sys::path::append(RelModCacheDir, "modules"); 205 206 std::string ModCachePath = "-fmodules-cache-path="; 207 ModCachePath.append(RelModCacheDir.c_str()); 208 209 OS << ' '; 210 printArg(OS, ModCachePath.c_str(), Quote); 211 } 212 213 if (ResponseFile != nullptr) { 214 OS << "\n Arguments passed via response file:\n"; 215 writeResponseFile(OS); 216 // Avoiding duplicated newline terminator, since FileLists are 217 // newline-separated. 218 if (Creator.getResponseFilesSupport() != Tool::RF_FileList) 219 OS << "\n"; 220 OS << " (end of response file)"; 221 } 222 223 OS << Terminator; 224 } 225 226 void Command::setResponseFile(const char *FileName) { 227 ResponseFile = FileName; 228 ResponseFileFlag = Creator.getResponseFileFlag(); 229 ResponseFileFlag += FileName; 230 } 231 232 int Command::Execute(const StringRef **Redirects, std::string *ErrMsg, 233 bool *ExecutionFailed) const { 234 SmallVector<const char*, 128> Argv; 235 236 if (ResponseFile == nullptr) { 237 Argv.push_back(Executable); 238 Argv.append(Arguments.begin(), Arguments.end()); 239 Argv.push_back(nullptr); 240 241 return llvm::sys::ExecuteAndWait(Executable, Argv.data(), /*env*/ nullptr, 242 Redirects, /*secondsToWait*/ 0, 243 /*memoryLimit*/ 0, ErrMsg, 244 ExecutionFailed); 245 } 246 247 // We need to put arguments in a response file (command is too large) 248 // Open stream to store the response file contents 249 std::string RespContents; 250 llvm::raw_string_ostream SS(RespContents); 251 252 // Write file contents and build the Argv vector 253 writeResponseFile(SS); 254 buildArgvForResponseFile(Argv); 255 Argv.push_back(nullptr); 256 SS.flush(); 257 258 // Save the response file in the appropriate encoding 259 if (std::error_code EC = writeFileWithEncoding( 260 ResponseFile, RespContents, Creator.getResponseFileEncoding())) { 261 if (ErrMsg) 262 *ErrMsg = EC.message(); 263 if (ExecutionFailed) 264 *ExecutionFailed = true; 265 return -1; 266 } 267 268 return llvm::sys::ExecuteAndWait(Executable, Argv.data(), /*env*/ nullptr, 269 Redirects, /*secondsToWait*/ 0, 270 /*memoryLimit*/ 0, ErrMsg, ExecutionFailed); 271 } 272 273 FallbackCommand::FallbackCommand(const Action &Source_, const Tool &Creator_, 274 const char *Executable_, 275 const ArgStringList &Arguments_, 276 ArrayRef<InputInfo> Inputs, 277 std::unique_ptr<Command> Fallback_) 278 : Command(Source_, Creator_, Executable_, Arguments_, Inputs), 279 Fallback(std::move(Fallback_)) {} 280 281 void FallbackCommand::Print(raw_ostream &OS, const char *Terminator, 282 bool Quote, CrashReportInfo *CrashInfo) const { 283 Command::Print(OS, "", Quote, CrashInfo); 284 OS << " ||"; 285 Fallback->Print(OS, Terminator, Quote, CrashInfo); 286 } 287 288 static bool ShouldFallback(int ExitCode) { 289 // FIXME: We really just want to fall back for internal errors, such 290 // as when some symbol cannot be mangled, when we should be able to 291 // parse something but can't, etc. 292 return ExitCode != 0; 293 } 294 295 int FallbackCommand::Execute(const StringRef **Redirects, std::string *ErrMsg, 296 bool *ExecutionFailed) const { 297 int PrimaryStatus = Command::Execute(Redirects, ErrMsg, ExecutionFailed); 298 if (!ShouldFallback(PrimaryStatus)) 299 return PrimaryStatus; 300 301 // Clear ExecutionFailed and ErrMsg before falling back. 302 if (ErrMsg) 303 ErrMsg->clear(); 304 if (ExecutionFailed) 305 *ExecutionFailed = false; 306 307 const Driver &D = getCreator().getToolChain().getDriver(); 308 D.Diag(diag::warn_drv_invoking_fallback) << Fallback->getExecutable(); 309 310 int SecondaryStatus = Fallback->Execute(Redirects, ErrMsg, ExecutionFailed); 311 return SecondaryStatus; 312 } 313 314 ForceSuccessCommand::ForceSuccessCommand(const Action &Source_, 315 const Tool &Creator_, 316 const char *Executable_, 317 const ArgStringList &Arguments_, 318 ArrayRef<InputInfo> Inputs) 319 : Command(Source_, Creator_, Executable_, Arguments_, Inputs) {} 320 321 void ForceSuccessCommand::Print(raw_ostream &OS, const char *Terminator, 322 bool Quote, CrashReportInfo *CrashInfo) const { 323 Command::Print(OS, "", Quote, CrashInfo); 324 OS << " || (exit 0)" << Terminator; 325 } 326 327 int ForceSuccessCommand::Execute(const StringRef **Redirects, 328 std::string *ErrMsg, 329 bool *ExecutionFailed) const { 330 int Status = Command::Execute(Redirects, ErrMsg, ExecutionFailed); 331 (void)Status; 332 if (ExecutionFailed) 333 *ExecutionFailed = false; 334 return 0; 335 } 336 337 void JobList::Print(raw_ostream &OS, const char *Terminator, bool Quote, 338 CrashReportInfo *CrashInfo) const { 339 for (const auto &Job : *this) 340 Job.Print(OS, Terminator, Quote, CrashInfo); 341 } 342 343 void JobList::clear() { Jobs.clear(); } 344