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)
70 IsInclude = llvm::StringSwitch<bool>(Flag)
71 .Cases(
"-include",
"-header-include-file",
true)
72 .Cases(
"-idirafter",
"-internal-isystem",
"-iwithprefix",
true)
73 .Cases(
"-internal-externc-isystem",
"-iprefix",
true)
74 .Cases(
"-iwithprefixbefore",
"-isystem",
"-iquote",
true)
75 .Cases(
"-isysroot",
"-I",
"-F",
"-resource-dir",
true)
76 .Cases(
"-iframework",
"-include-pch",
true)
84 ShouldSkip = llvm::StringSwitch<bool>(Flag)
85 .Cases(
"-M",
"-MM",
"-MG",
"-MP",
"-MD",
true)
95 StringRef FlagRef(Flag);
96 IsInclude = FlagRef.starts_with(
"-F") || FlagRef.starts_with(
"-I");
99 if (FlagRef.starts_with(
"-fmodules-cache-path="))
106void Command::writeResponseFile(raw_ostream &OS)
const {
109 for (
const auto *Arg : InputFileList) {
118 for (
const auto *Arg : Arguments) {
121 for (; *Arg !=
'\0'; Arg++) {
122 if (*Arg ==
'\"' || *Arg ==
'\\') {
132void Command::buildArgvForResponseFile(
138 Out.push_back(Executable);
139 Out.push_back(ResponseFileFlag.c_str());
143 llvm::StringSet<> Inputs;
144 for (
const auto *InputName : InputFileList)
145 Inputs.insert(InputName);
146 Out.push_back(Executable);
149 Out.push_back(PrependArg);
153 bool FirstInput =
true;
154 for (
const auto *Arg : Arguments) {
155 if (Inputs.count(Arg) == 0) {
157 }
else if (FirstInput) {
160 Out.push_back(ResponseFile);
170 using namespace llvm;
174 if (path::is_absolute(InInc))
176 std::error_code EC = fs::current_path(OutInc);
179 path::append(OutInc, InInc);
185 StringRef FlagRef(Args[Idx + NumArgs - 1]);
186 assert((FlagRef.starts_with(
"-F") || FlagRef.starts_with(
"-I")) &&
187 "Expecting -I or -F");
188 StringRef Inc = FlagRef.slice(2, StringRef::npos);
189 if (getAbsPath(Inc, NewInc)) {
192 IncFlags.push_back(std::move(NewArg));
197 assert(NumArgs == 2 &&
"Not expecting more than two arguments");
198 StringRef Inc(Args[Idx + NumArgs - 1]);
199 if (!getAbsPath(Inc, NewInc))
202 IncFlags.push_back(std::move(NewInc));
209 llvm::sys::printArg(OS, Executable,
true);
213 if (ResponseFile !=
nullptr) {
214 buildArgvForResponseFile(ArgsRespFile);
216 }
else if (PrependArg) {
218 llvm::sys::printArg(OS, PrependArg,
true);
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];
227 bool IsInclude =
false;
228 if (
skipArgs(Arg, HaveCrashVFS, NumArgs, IsInclude)) {
234 if (HaveCrashVFS && IsInclude) {
237 if (!NewIncFlags.empty()) {
238 for (
auto &F : NewIncFlags) {
240 llvm::sys::printArg(OS, F.c_str(), Quote);
251 (i == 0 || StringRef(Args[i - 1]) !=
"-main-file-name")) {
255 llvm::sys::printArg(OS,
ShortName.str(), Quote);
261 llvm::sys::printArg(OS, Arg, Quote);
264 if (CrashInfo && HaveCrashVFS) {
266 llvm::sys::printArg(OS,
"-ivfsoverlay", Quote);
268 llvm::sys::printArg(OS, CrashInfo->
VFSPath.str(), Quote);
276 llvm::sys::path::parent_path(CrashInfo->
VFSPath));
277 llvm::sys::path::append(RelModCacheDir,
"repro-modules");
279 std::string ModCachePath =
"-fmodules-cache-path=";
280 ModCachePath.append(RelModCacheDir.c_str());
283 llvm::sys::printArg(OS, ModCachePath, Quote);
286 if (ResponseFile !=
nullptr) {
287 OS <<
"\n Arguments passed via response file:\n";
288 writeResponseFile(OS);
293 OS <<
" (end of response file)";
306 Environment.reserve(NewEnvironment.size() + 1);
307 Environment.assign(NewEnvironment.begin(), NewEnvironment.end());
308 Environment.push_back(
nullptr);
312 const std::vector<std::optional<std::string>> &Redirects) {
313 RedirectFiles = Redirects;
319 llvm::outs() << llvm::sys::path::filename(Arg.getFilename()) <<
"\n";
320 llvm::outs().flush();
325 std::string *ErrMsg,
bool *ExecutionFailed)
const {
329 if (ResponseFile ==
nullptr) {
330 Argv.push_back(Executable);
332 Argv.push_back(PrependArg);
333 Argv.append(Arguments.begin(), Arguments.end());
334 Argv.push_back(
nullptr);
337 std::string RespContents;
338 llvm::raw_string_ostream SS(RespContents);
341 writeResponseFile(SS);
342 buildArgvForResponseFile(Argv);
343 Argv.push_back(
nullptr);
346 if (std::error_code EC = writeFileWithEncoding(
349 *ErrMsg = EC.message();
351 *ExecutionFailed =
true;
358 std::optional<ArrayRef<StringRef>>
Env;
359 std::vector<StringRef> ArgvVectorStorage;
360 if (!Environment.empty()) {
361 assert(Environment.back() ==
nullptr &&
362 "Environment vector should be null-terminated by now");
363 ArgvVectorStorage = llvm::toStringRefArray(Environment.data());
367 auto Args = llvm::toStringRefArray(Argv.data());
370 if (!RedirectFiles.empty()) {
371 std::vector<std::optional<StringRef>> RedirectFilesOptional;
372 for (
const auto &Ele : RedirectFiles)
374 RedirectFilesOptional.push_back(std::optional<StringRef>(*Ele));
376 RedirectFilesOptional.push_back(std::nullopt);
378 return llvm::sys::ExecuteAndWait(Executable, Args,
Env,
381 ErrMsg, ExecutionFailed, &ProcStat);
384 return llvm::sys::ExecuteAndWait(Executable, Args,
Env, Redirects,
386 ErrMsg, ExecutionFailed, &ProcStat);
391 const char *Executable,
392 const llvm::opt::ArgStringList &Arguments,
394 const char *PrependArg)
395 :
Command(Source, Creator, ResponseSupport, Executable, Arguments, Inputs,
396 Outputs, PrependArg) {
403 OS <<
" (in-process)\n";
408 std::string *ErrMsg,
bool *ExecutionFailed)
const {
420 Argv.push_back(
nullptr);
427 *ExecutionFailed =
false;
429 llvm::CrashRecoveryContext CRC;
430 CRC.DumpStackAndCleanupOnFailure =
true;
432 const void *PrettyState = llvm::SavePrettyStackState();
437 if (!CRC.RunSafely([&]() { R = D.CC1Main(Argv); })) {
438 llvm::RestorePrettyStackState(PrettyState);
447 "The CC1Command doesn't support changing the environment vars!");
452 for (
const auto &Job : *
this)
453 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.