16#include "llvm/ADT/ArrayRef.h"
17#include "llvm/ADT/SmallString.h"
18#include "llvm/ADT/SmallVector.h"
19#include "llvm/ADT/StringExtras.h"
20#include "llvm/ADT/StringRef.h"
21#include "llvm/ADT/StringSet.h"
22#include "llvm/ADT/StringSwitch.h"
23#include "llvm/Support/CrashRecoveryContext.h"
24#include "llvm/Support/FileSystem.h"
25#include "llvm/Support/Path.h"
26#include "llvm/Support/PrettyStackTrace.h"
27#include "llvm/Support/Program.h"
28#include "llvm/Support/raw_ostream.h"
33#include <system_error>
37using namespace driver;
41 const llvm::opt::ArgStringList &Arguments,
43 const char *PrependArg)
44 : Source(Source), Creator(Creator), ResponseSupport(ResponseSupport),
45 Executable(Executable), PrependArg(PrependArg), Arguments(Arguments) {
46 for (
const auto &II : Inputs)
49 for (
const auto &II : Outputs)
51 OutputFilenames.push_back(II.getFilename());
57static bool skipArgs(
const char *Flag,
bool HaveCrashVFS,
int &SkipNum,
62 bool ShouldSkip = llvm::StringSwitch<bool>(Flag)
63 .Cases(
"-MF",
"-MT",
"-MQ",
"-serialize-diagnostic-file",
true)
64 .Cases(
"-o",
"-dependency-file",
true)
65 .Cases(
"-fdebug-compilation-dir",
"-diagnostic-log-file",
true)
66 .Cases(
"-dwarf-debug-flags",
"-ivfsoverlay",
true)
72 IsInclude = llvm::StringSwitch<bool>(Flag)
73 .Cases(
"-include",
"-header-include-file",
true)
74 .Cases(
"-idirafter",
"-internal-isystem",
"-iwithprefix",
true)
75 .Cases(
"-internal-externc-isystem",
"-iprefix",
true)
76 .Cases(
"-iwithprefixbefore",
"-isystem",
"-iquote",
true)
77 .Cases(
"-isysroot",
"-I",
"-F",
"-resource-dir",
true)
78 .Cases(
"-iframework",
"-include-pch",
true)
86 ShouldSkip = llvm::StringSwitch<bool>(Flag)
87 .Cases(
"-M",
"-MM",
"-MG",
"-MP",
"-MD",
true)
97 StringRef FlagRef(Flag);
98 IsInclude = FlagRef.starts_with(
"-F") || FlagRef.starts_with(
"-I");
100 return !HaveCrashVFS;
101 if (FlagRef.starts_with(
"-fmodules-cache-path="))
108void Command::writeResponseFile(raw_ostream &OS)
const {
111 for (
const auto *Arg : InputFileList) {
120 for (
const auto *Arg : Arguments) {
123 for (; *Arg !=
'\0'; Arg++) {
124 if (*Arg ==
'\"' || *Arg ==
'\\') {
134void Command::buildArgvForResponseFile(
140 Out.push_back(Executable);
141 Out.push_back(ResponseFileFlag.c_str());
145 llvm::StringSet<> Inputs;
146 for (
const auto *InputName : InputFileList)
147 Inputs.insert(InputName);
148 Out.push_back(Executable);
151 Out.push_back(PrependArg);
155 bool FirstInput =
true;
156 for (
const auto *Arg : Arguments) {
157 if (Inputs.count(Arg) == 0) {
159 }
else if (FirstInput) {
162 Out.push_back(ResponseFile);
172 using namespace llvm;
176 if (path::is_absolute(InInc))
178 std::error_code EC = fs::current_path(OutInc);
181 path::append(OutInc, InInc);
187 StringRef FlagRef(Args[Idx + NumArgs - 1]);
188 assert((FlagRef.starts_with(
"-F") || FlagRef.starts_with(
"-I")) &&
189 "Expecting -I or -F");
190 StringRef Inc = FlagRef.slice(2, StringRef::npos);
191 if (getAbsPath(Inc, NewInc)) {
194 IncFlags.push_back(std::move(NewArg));
199 assert(NumArgs == 2 &&
"Not expecting more than two arguments");
200 StringRef Inc(Args[Idx + NumArgs - 1]);
201 if (!getAbsPath(Inc, NewInc))
204 IncFlags.push_back(std::move(NewInc));
211 llvm::sys::printArg(OS, Executable,
true);
215 if (ResponseFile !=
nullptr) {
216 buildArgvForResponseFile(ArgsRespFile);
218 }
else if (PrependArg) {
220 llvm::sys::printArg(OS, PrependArg,
true);
223 bool HaveCrashVFS = CrashInfo && !CrashInfo->
VFSPath.empty();
224 for (
size_t i = 0, e = Args.size(); i < e; ++i) {
225 const char *
const Arg = Args[i];
229 bool IsInclude =
false;
230 if (
skipArgs(Arg, HaveCrashVFS, NumArgs, IsInclude)) {
236 if (HaveCrashVFS && IsInclude) {
239 if (!NewIncFlags.empty()) {
240 for (
auto &F : NewIncFlags) {
242 llvm::sys::printArg(OS, F.c_str(), Quote);
253 (i == 0 || StringRef(Args[i - 1]) !=
"-main-file-name")) {
257 llvm::sys::printArg(OS,
ShortName.str(), Quote);
263 llvm::sys::printArg(OS, Arg, Quote);
266 if (CrashInfo && HaveCrashVFS) {
268 llvm::sys::printArg(OS,
"-ivfsoverlay", Quote);
270 llvm::sys::printArg(OS, CrashInfo->
VFSPath.str(), Quote);
278 llvm::sys::path::parent_path(CrashInfo->
VFSPath));
279 llvm::sys::path::append(RelModCacheDir,
"repro-modules");
281 std::string ModCachePath =
"-fmodules-cache-path=";
282 ModCachePath.append(RelModCacheDir.c_str());
285 llvm::sys::printArg(OS, ModCachePath, Quote);
288 if (ResponseFile !=
nullptr) {
289 OS <<
"\n Arguments passed via response file:\n";
290 writeResponseFile(OS);
295 OS <<
" (end of response file)";
308 Environment.reserve(NewEnvironment.size() + 1);
309 Environment.assign(NewEnvironment.begin(), NewEnvironment.end());
310 Environment.push_back(
nullptr);
314 const std::vector<std::optional<std::string>> &Redirects) {
315 RedirectFiles = Redirects;
321 llvm::outs() << llvm::sys::path::filename(Arg.getFilename()) <<
"\n";
322 llvm::outs().flush();
327 std::string *ErrMsg,
bool *ExecutionFailed)
const {
331 if (ResponseFile ==
nullptr) {
332 Argv.push_back(Executable);
334 Argv.push_back(PrependArg);
335 Argv.append(Arguments.begin(), Arguments.end());
336 Argv.push_back(
nullptr);
339 std::string RespContents;
340 llvm::raw_string_ostream SS(RespContents);
343 writeResponseFile(SS);
344 buildArgvForResponseFile(Argv);
345 Argv.push_back(
nullptr);
349 if (std::error_code EC = writeFileWithEncoding(
352 *ErrMsg = EC.message();
354 *ExecutionFailed =
true;
361 std::optional<ArrayRef<StringRef>>
Env;
362 std::vector<StringRef> ArgvVectorStorage;
363 if (!Environment.empty()) {
364 assert(Environment.back() ==
nullptr &&
365 "Environment vector should be null-terminated by now");
366 ArgvVectorStorage = llvm::toStringRefArray(Environment.data());
370 auto Args = llvm::toStringRefArray(Argv.data());
373 if (!RedirectFiles.empty()) {
374 std::vector<std::optional<StringRef>> RedirectFilesOptional;
375 for (
const auto &Ele : RedirectFiles)
377 RedirectFilesOptional.push_back(std::optional<StringRef>(*Ele));
379 RedirectFilesOptional.push_back(std::nullopt);
381 return llvm::sys::ExecuteAndWait(Executable, Args,
Env,
384 ErrMsg, ExecutionFailed, &ProcStat);
387 return llvm::sys::ExecuteAndWait(Executable, Args,
Env, Redirects,
389 ErrMsg, ExecutionFailed, &ProcStat);
394 const char *Executable,
395 const llvm::opt::ArgStringList &Arguments,
397 const char *PrependArg)
398 :
Command(Source, Creator, ResponseSupport, Executable, Arguments, Inputs,
399 Outputs, PrependArg) {
406 OS <<
" (in-process)\n";
411 std::string *ErrMsg,
bool *ExecutionFailed)
const {
423 Argv.push_back(
nullptr);
430 *ExecutionFailed =
false;
432 llvm::CrashRecoveryContext CRC;
433 CRC.DumpStackAndCleanupOnFailure =
true;
435 const void *PrettyState = llvm::SavePrettyStackState();
440 if (!CRC.RunSafely([&]() { R = D.CC1Main(Argv); })) {
441 llvm::RestorePrettyStackState(PrettyState);
450 "The CC1Command doesn't support changing the environment vars!");
455 for (
const auto &Job : *
this)
456 Job.Print(OS, Terminator, Quote, CrashInfo);
static void rewriteIncludes(const llvm::ArrayRef< const char * > &Args, size_t Idx, size_t NumArgs, llvm::SmallVectorImpl< llvm::SmallString< 128 > > &IncFlags)
Rewrite relative include-like flag paths to absolute ones.
static bool skipArgs(const char *Flag, bool HaveCrashVFS, int &SkipNum, bool &IsInclude)
Check if the compiler flag in question should be skipped when emitting a reproducer.
Forward-declares and imports various common LLVM datatypes that clang wants to use unqualified.
Action - Represent an abstract compilation step to perform.
void setEnvironment(llvm::ArrayRef< const char * > NewEnvironment) override
Sets the environment to be used by the new process.
void Print(llvm::raw_ostream &OS, const char *Terminator, bool Quote, CrashReportInfo *CrashInfo=nullptr) const override
CC1Command(const Action &Source, const Tool &Creator, ResponseFileSupport ResponseSupport, const char *Executable, const llvm::opt::ArgStringList &Arguments, ArrayRef< InputInfo > Inputs, ArrayRef< InputInfo > Outputs=std::nullopt, const char *PrependArg=nullptr)
int Execute(ArrayRef< std::optional< StringRef > > Redirects, std::string *ErrMsg, bool *ExecutionFailed) const override
Command - An executable path/name and argument vector to execute.
const Tool & getCreator() const
getCreator - Return the Tool which caused the creation of this job.
const llvm::opt::ArgStringList & getArguments() const
void setResponseFile(const char *FileName)
Set to pass arguments via a response file when launching the command.
void setRedirectFiles(const std::vector< std::optional< std::string > > &Redirects)
bool PrintInputFilenames
Whether to print the input filenames when executing.
const char * getExecutable() const
virtual void Print(llvm::raw_ostream &OS, const char *Terminator, bool Quote, CrashReportInfo *CrashInfo=nullptr) const
bool InProcess
Whether the command will be executed in this process or not.
virtual void setEnvironment(llvm::ArrayRef< const char * > NewEnvironment)
Sets the environment to be used by the new process.
Command(const Action &Source, const Tool &Creator, ResponseFileSupport ResponseSupport, const char *Executable, const llvm::opt::ArgStringList &Arguments, ArrayRef< InputInfo > Inputs, ArrayRef< InputInfo > Outputs=std::nullopt, const char *PrependArg=nullptr)
void PrintFileNames() const
Optionally print the filenames to be compiled.
virtual int Execute(ArrayRef< std::optional< StringRef > > Redirects, std::string *ErrMsg, bool *ExecutionFailed) const
Driver - Encapsulate logic for constructing compilation processes from a set of gcc-driver-like comma...
void clear()
Clear the job list.
void Print(llvm::raw_ostream &OS, const char *Terminator, bool Quote, CrashReportInfo *CrashInfo=nullptr) const
The JSON file list parser is used to communicate input to InstallAPI.
Diagnostic wrappers for TextAPI types for error reporting.
ResponseFileKind ResponseKind
The level of support for response files.
llvm::sys::WindowsEncodingMethod ResponseEncoding
The encoding to use when writing response files on Windows.
const char * ResponseFlag
What prefix to use for the command-line argument when passing a response file.