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/FileSystem.h" 23 #include "llvm/Support/Path.h" 24 #include "llvm/Support/Program.h" 25 #include "llvm/Support/raw_ostream.h" 26 #include <cassert> 27 using namespace clang::driver; 28 using llvm::raw_ostream; 29 using llvm::StringRef; 30 using llvm::ArrayRef; 31 32 Command::Command(const Action &Source, const Tool &Creator, 33 const char *Executable, const ArgStringList &Arguments, 34 ArrayRef<InputInfo> Inputs) 35 : Source(Source), Creator(Creator), Executable(Executable), 36 Arguments(Arguments), ResponseFile(nullptr) { 37 for (const auto &II : Inputs) 38 if (II.isFilename()) 39 InputFilenames.push_back(II.getFilename()); 40 } 41 42 /// @brief Check if the compiler flag in question should be skipped when 43 /// emitting a reproducer. Also track how many arguments it has and if the 44 /// option is some kind of include path. 45 static bool skipArgs(const char *Flag, bool HaveCrashVFS, int &SkipNum, 46 bool &IsInclude) { 47 SkipNum = 2; 48 // These flags are all of the form -Flag <Arg> and are treated as two 49 // arguments. Therefore, we need to skip the flag and the next argument. 50 bool ShouldSkip = llvm::StringSwitch<bool>(Flag) 51 .Cases("-MF", "-MT", "-MQ", "-serialize-diagnostic-file", true) 52 .Cases("-o", "-dependency-file", true) 53 .Cases("-fdebug-compilation-dir", "-diagnostic-log-file", true) 54 .Cases("-dwarf-debug-flags", "-ivfsoverlay", true) 55 .Default(false); 56 if (ShouldSkip) 57 return true; 58 59 // Some include flags shouldn't be skipped if we have a crash VFS 60 IsInclude = llvm::StringSwitch<bool>(Flag) 61 .Cases("-include", "-header-include-file", true) 62 .Cases("-idirafter", "-internal-isystem", "-iwithprefix", true) 63 .Cases("-internal-externc-isystem", "-iprefix", true) 64 .Cases("-iwithprefixbefore", "-isystem", "-iquote", true) 65 .Cases("-isysroot", "-I", "-F", "-resource-dir", true) 66 .Cases("-iframework", "-include-pch", true) 67 .Default(false); 68 if (IsInclude) 69 return HaveCrashVFS ? false : true; 70 71 // The remaining flags are treated as a single argument. 72 73 // These flags are all of the form -Flag and have no second argument. 74 ShouldSkip = llvm::StringSwitch<bool>(Flag) 75 .Cases("-M", "-MM", "-MG", "-MP", "-MD", true) 76 .Case("-MMD", true) 77 .Default(false); 78 79 // Match found. 80 SkipNum = 1; 81 if (ShouldSkip) 82 return true; 83 84 // These flags are treated as a single argument (e.g., -F<Dir>). 85 StringRef FlagRef(Flag); 86 IsInclude = FlagRef.startswith("-F") || FlagRef.startswith("-I"); 87 if (IsInclude) 88 return HaveCrashVFS ? false : true; 89 if (FlagRef.startswith("-fmodules-cache-path=")) 90 return true; 91 92 SkipNum = 0; 93 return false; 94 } 95 96 void Command::printArg(raw_ostream &OS, StringRef Arg, bool Quote) { 97 const bool Escape = Arg.find_first_of("\"\\$") != StringRef::npos; 98 99 if (!Quote && !Escape) { 100 OS << Arg; 101 return; 102 } 103 104 // Quote and escape. This isn't really complete, but good enough. 105 OS << '"'; 106 for (const char c : Arg) { 107 if (c == '"' || c == '\\' || c == '$') 108 OS << '\\'; 109 OS << c; 110 } 111 OS << '"'; 112 } 113 114 void Command::writeResponseFile(raw_ostream &OS) const { 115 // In a file list, we only write the set of inputs to the response file 116 if (Creator.getResponseFilesSupport() == Tool::RF_FileList) { 117 for (const char *Arg : InputFileList) { 118 OS << Arg << '\n'; 119 } 120 return; 121 } 122 123 // In regular response files, we send all arguments to the response file. 124 // Wrapping all arguments in double quotes ensures that both Unix tools and 125 // Windows tools understand the response file. 126 for (const char *Arg : Arguments) { 127 OS << '"'; 128 129 for (; *Arg != '\0'; Arg++) { 130 if (*Arg == '\"' || *Arg == '\\') { 131 OS << '\\'; 132 } 133 OS << *Arg; 134 } 135 136 OS << "\" "; 137 } 138 } 139 140 void Command::buildArgvForResponseFile( 141 llvm::SmallVectorImpl<const char *> &Out) const { 142 // When not a file list, all arguments are sent to the response file. 143 // This leaves us to set the argv to a single parameter, requesting the tool 144 // to read the response file. 145 if (Creator.getResponseFilesSupport() != Tool::RF_FileList) { 146 Out.push_back(Executable); 147 Out.push_back(ResponseFileFlag.c_str()); 148 return; 149 } 150 151 llvm::StringSet<> Inputs; 152 for (const char *InputName : InputFileList) 153 Inputs.insert(InputName); 154 Out.push_back(Executable); 155 // In a file list, build args vector ignoring parameters that will go in the 156 // response file (elements of the InputFileList vector) 157 bool FirstInput = true; 158 for (const char *Arg : Arguments) { 159 if (Inputs.count(Arg) == 0) { 160 Out.push_back(Arg); 161 } else if (FirstInput) { 162 FirstInput = false; 163 Out.push_back(Creator.getResponseFileFlag()); 164 Out.push_back(ResponseFile); 165 } 166 } 167 } 168 169 /// @brief Rewrite relative include-like flag paths to absolute ones. 170 static void 171 rewriteIncludes(const llvm::ArrayRef<const char *> &Args, size_t Idx, 172 size_t NumArgs, 173 llvm::SmallVectorImpl<llvm::SmallString<128>> &IncFlags) { 174 using namespace llvm; 175 using namespace sys; 176 auto getAbsPath = [](StringRef InInc, SmallVectorImpl<char> &OutInc) -> bool { 177 if (path::is_absolute(InInc)) // Nothing to do here... 178 return false; 179 std::error_code EC = fs::current_path(OutInc); 180 if (EC) 181 return false; 182 path::append(OutInc, InInc); 183 return true; 184 }; 185 186 SmallString<128> NewInc; 187 if (NumArgs == 1) { 188 StringRef FlagRef(Args[Idx + NumArgs - 1]); 189 assert((FlagRef.startswith("-F") || FlagRef.startswith("-I")) && 190 "Expecting -I or -F"); 191 StringRef Inc = FlagRef.slice(2, StringRef::npos); 192 if (getAbsPath(Inc, NewInc)) { 193 SmallString<128> NewArg(FlagRef.slice(0, 2)); 194 NewArg += NewInc; 195 IncFlags.push_back(std::move(NewArg)); 196 } 197 return; 198 } 199 200 assert(NumArgs == 2 && "Not expecting more than two arguments"); 201 StringRef Inc(Args[Idx + NumArgs - 1]); 202 if (!getAbsPath(Inc, NewInc)) 203 return; 204 IncFlags.push_back(SmallString<128>(Args[Idx])); 205 IncFlags.push_back(std::move(NewInc)); 206 } 207 208 void Command::Print(raw_ostream &OS, const char *Terminator, bool Quote, 209 CrashReportInfo *CrashInfo) const { 210 // Always quote the exe. 211 OS << ' '; 212 printArg(OS, Executable, /*Quote=*/true); 213 214 llvm::ArrayRef<const char *> Args = Arguments; 215 llvm::SmallVector<const char *, 128> ArgsRespFile; 216 if (ResponseFile != nullptr) { 217 buildArgvForResponseFile(ArgsRespFile); 218 Args = ArrayRef<const char *>(ArgsRespFile).slice(1); // no executable name 219 } 220 221 bool HaveCrashVFS = CrashInfo && !CrashInfo->VFSPath.empty(); 222 for (size_t i = 0, e = Args.size(); i < e; ++i) { 223 const char *const Arg = Args[i]; 224 225 if (CrashInfo) { 226 int NumArgs = 0; 227 bool IsInclude = false; 228 if (skipArgs(Arg, HaveCrashVFS, NumArgs, IsInclude)) { 229 i += NumArgs - 1; 230 continue; 231 } 232 233 // Relative includes need to be expanded to absolute paths. 234 if (HaveCrashVFS && IsInclude) { 235 SmallVector<SmallString<128>, 2> NewIncFlags; 236 rewriteIncludes(Args, i, NumArgs, NewIncFlags); 237 if (!NewIncFlags.empty()) { 238 for (auto &F : NewIncFlags) { 239 OS << ' '; 240 printArg(OS, F.c_str(), Quote); 241 } 242 i += NumArgs - 1; 243 continue; 244 } 245 } 246 247 auto Found = std::find_if(InputFilenames.begin(), InputFilenames.end(), 248 [&Arg](StringRef IF) { return IF == Arg; }); 249 if (Found != InputFilenames.end() && 250 (i == 0 || StringRef(Args[i - 1]) != "-main-file-name")) { 251 // Replace the input file name with the crashinfo's file name. 252 OS << ' '; 253 StringRef ShortName = llvm::sys::path::filename(CrashInfo->Filename); 254 printArg(OS, ShortName.str(), Quote); 255 continue; 256 } 257 } 258 259 OS << ' '; 260 printArg(OS, Arg, Quote); 261 } 262 263 if (CrashInfo && HaveCrashVFS) { 264 OS << ' '; 265 printArg(OS, "-ivfsoverlay", Quote); 266 OS << ' '; 267 printArg(OS, CrashInfo->VFSPath.str(), Quote); 268 269 // The leftover modules from the crash are stored in 270 // <name>.cache/vfs/modules 271 // Leave it untouched for pcm inspection and provide a clean/empty dir 272 // path to contain the future generated module cache: 273 // <name>.cache/vfs/repro-modules 274 SmallString<128> RelModCacheDir = llvm::sys::path::parent_path( 275 llvm::sys::path::parent_path(CrashInfo->VFSPath)); 276 llvm::sys::path::append(RelModCacheDir, "repro-modules"); 277 278 std::string ModCachePath = "-fmodules-cache-path="; 279 ModCachePath.append(RelModCacheDir.c_str()); 280 281 OS << ' '; 282 printArg(OS, ModCachePath, Quote); 283 } 284 285 if (ResponseFile != nullptr) { 286 OS << "\n Arguments passed via response file:\n"; 287 writeResponseFile(OS); 288 // Avoiding duplicated newline terminator, since FileLists are 289 // newline-separated. 290 if (Creator.getResponseFilesSupport() != Tool::RF_FileList) 291 OS << "\n"; 292 OS << " (end of response file)"; 293 } 294 295 OS << Terminator; 296 } 297 298 void Command::setResponseFile(const char *FileName) { 299 ResponseFile = FileName; 300 ResponseFileFlag = Creator.getResponseFileFlag(); 301 ResponseFileFlag += FileName; 302 } 303 304 void Command::setEnvironment(llvm::ArrayRef<const char *> NewEnvironment) { 305 Environment.reserve(NewEnvironment.size() + 1); 306 Environment.assign(NewEnvironment.begin(), NewEnvironment.end()); 307 Environment.push_back(nullptr); 308 } 309 310 int Command::Execute(const StringRef **Redirects, std::string *ErrMsg, 311 bool *ExecutionFailed) const { 312 SmallVector<const char*, 128> Argv; 313 314 const char **Envp; 315 if (Environment.empty()) { 316 Envp = nullptr; 317 } else { 318 assert(Environment.back() == nullptr && 319 "Environment vector should be null-terminated by now"); 320 Envp = const_cast<const char **>(Environment.data()); 321 } 322 323 if (ResponseFile == nullptr) { 324 Argv.push_back(Executable); 325 Argv.append(Arguments.begin(), Arguments.end()); 326 Argv.push_back(nullptr); 327 328 return llvm::sys::ExecuteAndWait( 329 Executable, Argv.data(), Envp, Redirects, /*secondsToWait*/ 0, 330 /*memoryLimit*/ 0, ErrMsg, ExecutionFailed); 331 } 332 333 // We need to put arguments in a response file (command is too large) 334 // Open stream to store the response file contents 335 std::string RespContents; 336 llvm::raw_string_ostream SS(RespContents); 337 338 // Write file contents and build the Argv vector 339 writeResponseFile(SS); 340 buildArgvForResponseFile(Argv); 341 Argv.push_back(nullptr); 342 SS.flush(); 343 344 // Save the response file in the appropriate encoding 345 if (std::error_code EC = writeFileWithEncoding( 346 ResponseFile, RespContents, Creator.getResponseFileEncoding())) { 347 if (ErrMsg) 348 *ErrMsg = EC.message(); 349 if (ExecutionFailed) 350 *ExecutionFailed = true; 351 return -1; 352 } 353 354 return llvm::sys::ExecuteAndWait(Executable, Argv.data(), Envp, Redirects, 355 /*secondsToWait*/ 0, 356 /*memoryLimit*/ 0, ErrMsg, ExecutionFailed); 357 } 358 359 FallbackCommand::FallbackCommand(const Action &Source_, const Tool &Creator_, 360 const char *Executable_, 361 const ArgStringList &Arguments_, 362 ArrayRef<InputInfo> Inputs, 363 std::unique_ptr<Command> Fallback_) 364 : Command(Source_, Creator_, Executable_, Arguments_, Inputs), 365 Fallback(std::move(Fallback_)) {} 366 367 void FallbackCommand::Print(raw_ostream &OS, const char *Terminator, 368 bool Quote, CrashReportInfo *CrashInfo) const { 369 Command::Print(OS, "", Quote, CrashInfo); 370 OS << " ||"; 371 Fallback->Print(OS, Terminator, Quote, CrashInfo); 372 } 373 374 static bool ShouldFallback(int ExitCode) { 375 // FIXME: We really just want to fall back for internal errors, such 376 // as when some symbol cannot be mangled, when we should be able to 377 // parse something but can't, etc. 378 return ExitCode != 0; 379 } 380 381 int FallbackCommand::Execute(const StringRef **Redirects, std::string *ErrMsg, 382 bool *ExecutionFailed) const { 383 int PrimaryStatus = Command::Execute(Redirects, ErrMsg, ExecutionFailed); 384 if (!ShouldFallback(PrimaryStatus)) 385 return PrimaryStatus; 386 387 // Clear ExecutionFailed and ErrMsg before falling back. 388 if (ErrMsg) 389 ErrMsg->clear(); 390 if (ExecutionFailed) 391 *ExecutionFailed = false; 392 393 const Driver &D = getCreator().getToolChain().getDriver(); 394 D.Diag(diag::warn_drv_invoking_fallback) << Fallback->getExecutable(); 395 396 int SecondaryStatus = Fallback->Execute(Redirects, ErrMsg, ExecutionFailed); 397 return SecondaryStatus; 398 } 399 400 ForceSuccessCommand::ForceSuccessCommand(const Action &Source_, 401 const Tool &Creator_, 402 const char *Executable_, 403 const ArgStringList &Arguments_, 404 ArrayRef<InputInfo> Inputs) 405 : Command(Source_, Creator_, Executable_, Arguments_, Inputs) {} 406 407 void ForceSuccessCommand::Print(raw_ostream &OS, const char *Terminator, 408 bool Quote, CrashReportInfo *CrashInfo) const { 409 Command::Print(OS, "", Quote, CrashInfo); 410 OS << " || (exit 0)" << Terminator; 411 } 412 413 int ForceSuccessCommand::Execute(const StringRef **Redirects, 414 std::string *ErrMsg, 415 bool *ExecutionFailed) const { 416 int Status = Command::Execute(Redirects, ErrMsg, ExecutionFailed); 417 (void)Status; 418 if (ExecutionFailed) 419 *ExecutionFailed = false; 420 return 0; 421 } 422 423 void JobList::Print(raw_ostream &OS, const char *Terminator, bool Quote, 424 CrashReportInfo *CrashInfo) const { 425 for (const auto &Job : *this) 426 Job.Print(OS, Terminator, Quote, CrashInfo); 427 } 428 429 void JobList::clear() { Jobs.clear(); } 430