15#include "llvm/ADT/ArrayRef.h"
16#include "llvm/ADT/SmallString.h"
17#include "llvm/ADT/SmallVector.h"
18#include "llvm/ADT/StringExtras.h"
19#include "llvm/ADT/StringRef.h"
20#include "llvm/ADT/StringSet.h"
21#include "llvm/ADT/StringSwitch.h"
22#include "llvm/Support/CrashRecoveryContext.h"
23#include "llvm/Support/FileSystem.h"
24#include "llvm/Support/Path.h"
25#include "llvm/Support/PrettyStackTrace.h"
26#include "llvm/Support/Program.h"
27#include "llvm/Support/raw_ostream.h"
31#include <system_error>
35using namespace driver;
39 const llvm::opt::ArgStringList &Arguments,
41 const char *PrependArg)
42 : Source(Source), Creator(Creator), ResponseSupport(ResponseSupport),
43 Executable(Executable), PrependArg(PrependArg), Arguments(Arguments) {
44 for (
const auto &II : Inputs)
47 for (
const auto &II : Outputs)
49 OutputFilenames.push_back(II.getFilename());
55static bool skipArgs(
const char *Flag,
bool HaveCrashVFS,
int &SkipNum,
60 bool ShouldSkip = llvm::StringSwitch<bool>(Flag)
61 .Cases(
"-MF",
"-MT",
"-MQ",
"-serialize-diagnostic-file",
true)
62 .Cases(
"-o",
"-dependency-file",
true)
63 .Cases(
"-fdebug-compilation-dir",
"-diagnostic-log-file",
true)
64 .Cases(
"-dwarf-debug-flags",
"-ivfsoverlay",
true)
71 llvm::StringSwitch<bool>(Flag)
72 .Cases(
"-include",
"-header-include-file",
true)
73 .Cases(
"-idirafter",
"-internal-isystem",
"-iwithprefix",
true)
74 .Cases(
"-internal-externc-isystem",
"-iprefix",
true)
75 .Cases(
"-iwithprefixbefore",
"-isystem",
"-iquote",
true)
76 .Cases(
"-isysroot",
"-I",
"-F",
"-resource-dir",
true)
77 .Cases(
"-internal-iframework",
"-iframework",
"-include-pch",
true)
85 ShouldSkip = llvm::StringSwitch<bool>(Flag)
86 .Cases(
"-M",
"-MM",
"-MG",
"-MP",
"-MD",
true)
96 StringRef FlagRef(Flag);
97 IsInclude = FlagRef.starts_with(
"-F") || FlagRef.starts_with(
"-I");
100 if (FlagRef.starts_with(
"-fmodules-cache-path="))
107void Command::writeResponseFile(raw_ostream &OS)
const {
110 for (
const auto *Arg : InputFileList) {
119 for (
const auto *Arg : Arguments) {
122 for (; *Arg !=
'\0'; Arg++) {
123 if (*Arg ==
'\"' || *Arg ==
'\\') {
133void Command::buildArgvForResponseFile(
139 Out.push_back(Executable);
140 Out.push_back(ResponseFileFlag.c_str());
144 llvm::StringSet<> Inputs(llvm::from_range, InputFileList);
145 Out.push_back(Executable);
148 Out.push_back(PrependArg);
152 bool FirstInput =
true;
153 for (
const auto *Arg : Arguments) {
154 if (Inputs.count(Arg) == 0) {
156 }
else if (FirstInput) {
159 Out.push_back(ResponseFile);
169 using namespace llvm;
173 if (path::is_absolute(InInc))
175 std::error_code EC = fs::current_path(OutInc);
178 path::append(OutInc, InInc);
184 StringRef FlagRef(Args[Idx + NumArgs - 1]);
185 assert((FlagRef.starts_with(
"-F") || FlagRef.starts_with(
"-I")) &&
186 "Expecting -I or -F");
187 StringRef Inc = FlagRef.substr(2);
188 if (getAbsPath(Inc, NewInc)) {
191 IncFlags.push_back(std::move(NewArg));
196 assert(NumArgs == 2 &&
"Not expecting more than two arguments");
197 StringRef Inc(Args[Idx + NumArgs - 1]);
198 if (!getAbsPath(Inc, NewInc))
201 IncFlags.push_back(std::move(NewInc));
208 llvm::sys::printArg(OS, Executable,
true);
212 if (ResponseFile !=
nullptr) {
213 buildArgvForResponseFile(ArgsRespFile);
215 }
else if (PrependArg) {
217 llvm::sys::printArg(OS, PrependArg,
true);
220 bool HaveCrashVFS = CrashInfo && !CrashInfo->
VFSPath.empty();
221 for (
size_t i = 0, e = Args.size(); i < e; ++i) {
222 const char *
const Arg = Args[i];
226 bool IsInclude =
false;
227 if (
skipArgs(Arg, HaveCrashVFS, NumArgs, IsInclude)) {
233 if (HaveCrashVFS && IsInclude) {
236 if (!NewIncFlags.empty()) {
237 for (
auto &F : NewIncFlags) {
239 llvm::sys::printArg(OS, F.c_str(), Quote);
250 (i == 0 || StringRef(Args[i - 1]) !=
"-main-file-name")) {
254 llvm::sys::printArg(OS,
ShortName.str(), Quote);
260 llvm::sys::printArg(OS, Arg, Quote);
263 if (CrashInfo && HaveCrashVFS) {
265 llvm::sys::printArg(OS,
"-ivfsoverlay", Quote);
267 llvm::sys::printArg(OS, CrashInfo->
VFSPath.str(), Quote);
275 llvm::sys::path::parent_path(CrashInfo->
VFSPath));
276 llvm::sys::path::append(RelModCacheDir,
"repro-modules");
278 std::string ModCachePath =
"-fmodules-cache-path=";
279 ModCachePath.append(RelModCacheDir.c_str());
282 llvm::sys::printArg(OS, ModCachePath, Quote);
285 if (ResponseFile !=
nullptr) {
286 OS <<
"\n Arguments passed via response file:\n";
287 writeResponseFile(OS);
292 OS <<
" (end of response file)";
305 Environment.reserve(NewEnvironment.size() + 1);
306 Environment.assign(NewEnvironment.begin(), NewEnvironment.end());
307 Environment.push_back(
nullptr);
311 const std::vector<std::optional<std::string>> &Redirects) {
312 RedirectFiles = Redirects;
318 llvm::outs() << llvm::sys::path::filename(Arg.getFilename()) <<
"\n";
319 llvm::outs().flush();
324 std::string *ErrMsg,
bool *ExecutionFailed)
const {
328 if (ResponseFile ==
nullptr) {
329 Argv.push_back(Executable);
331 Argv.push_back(PrependArg);
332 Argv.append(Arguments.begin(), Arguments.end());
333 Argv.push_back(
nullptr);
336 std::string RespContents;
337 llvm::raw_string_ostream SS(RespContents);
340 writeResponseFile(SS);
341 buildArgvForResponseFile(Argv);
342 Argv.push_back(
nullptr);
345 if (std::error_code EC = writeFileWithEncoding(
348 *ErrMsg = EC.message();
350 *ExecutionFailed =
true;
357 std::optional<ArrayRef<StringRef>>
Env;
358 std::vector<StringRef> ArgvVectorStorage;
359 if (!Environment.empty()) {
360 assert(Environment.back() ==
nullptr &&
361 "Environment vector should be null-terminated by now");
362 ArgvVectorStorage = llvm::toStringRefArray(Environment.data());
366 auto Args = llvm::toStringRefArray(Argv.data());
369 if (!RedirectFiles.empty()) {
370 std::vector<std::optional<StringRef>> RedirectFilesOptional;
371 for (
const auto &Ele : RedirectFiles)
373 RedirectFilesOptional.push_back(std::optional<StringRef>(*Ele));
375 RedirectFilesOptional.push_back(std::nullopt);
377 return llvm::sys::ExecuteAndWait(Executable, Args,
Env,
380 ErrMsg, ExecutionFailed, &ProcStat);
383 return llvm::sys::ExecuteAndWait(Executable, Args,
Env, Redirects,
385 ErrMsg, ExecutionFailed, &ProcStat);
390 const char *Executable,
391 const llvm::opt::ArgStringList &Arguments,
393 const char *PrependArg)
394 :
Command(Source, Creator, ResponseSupport, Executable, Arguments, Inputs,
395 Outputs, PrependArg) {
402 OS <<
" (in-process)\n";
407 std::string *ErrMsg,
bool *ExecutionFailed)
const {
419 Argv.push_back(
nullptr);
426 *ExecutionFailed =
false;
428 llvm::CrashRecoveryContext CRC;
429 CRC.DumpStackAndCleanupOnFailure =
true;
431 const void *PrettyState = llvm::SavePrettyStackState();
436 if (!CRC.RunSafely([&]() { R = D.CC1Main(Argv); })) {
437 llvm::RestorePrettyStackState(PrettyState);
446 "The CC1Command doesn't support changing the environment vars!");
451 for (
const auto &Job : *
this)
452 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
int Execute(ArrayRef< std::optional< StringRef > > Redirects, std::string *ErrMsg, bool *ExecutionFailed) 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={}, const char *PrependArg=nullptr)
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)
Command(const Action &Source, const Tool &Creator, ResponseFileSupport ResponseSupport, const char *Executable, const llvm::opt::ArgStringList &Arguments, ArrayRef< InputInfo > Inputs, ArrayRef< InputInfo > Outputs={}, const char *PrependArg=nullptr)
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.
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.