clang: lib/Driver/Job.cpp Source File (original) (raw)
1
2
3
4
5
6
7
8
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"
28#include
29#include
30#include
31#include <system_error>
32#include
33
34using namespace clang;
35using namespace driver;
36
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)
45 if (II.isFilename())
47 for (const auto &II : Outputs)
48 if (II.isFilename())
49 OutputFilenames.push_back(II.getFilename());
50}
51
52
53
54
55static bool skipArgs(const char *Flag, bool HaveCrashVFS, int &SkipNum,
56 bool &IsInclude) {
57 SkipNum = 2;
58
59
60 bool ShouldSkip = llvm::StringSwitch(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)
65 .Default(false);
66 if (ShouldSkip)
67 return true;
68
69
70 IsInclude = llvm::StringSwitch(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)
77 .Default(false);
78 if (IsInclude)
79 return !HaveCrashVFS;
80
81
82
83
84 ShouldSkip = llvm::StringSwitch(Flag)
85 .Cases("-M", "-MM", "-MG", "-MP", "-MD", true)
86 .Case("-MMD", true)
87 .Default(false);
88
89
90 SkipNum = 1;
91 if (ShouldSkip)
92 return true;
93
94
95 StringRef FlagRef(Flag);
96 IsInclude = FlagRef.starts_with("-F") || FlagRef.starts_with("-I");
97 if (IsInclude)
98 return !HaveCrashVFS;
99 if (FlagRef.starts_with("-fmodules-cache-path="))
100 return true;
101
102 SkipNum = 0;
103 return false;
104}
105
106void Command::writeResponseFile(raw_ostream &OS) const {
107
109 for (const auto *Arg : InputFileList) {
110 OS << Arg << '\n';
111 }
112 return;
113 }
114
115
116
117
118 for (const auto *Arg : Arguments) {
119 OS << '"';
120
121 for (; *Arg != '\0'; Arg++) {
122 if (*Arg == '\"' || *Arg == '\\') {
123 OS << '\\';
124 }
125 OS << *Arg;
126 }
127
128 OS << "\" ";
129 }
130}
131
132void Command::buildArgvForResponseFile(
134
135
136
138 Out.push_back(Executable);
139 Out.push_back(ResponseFileFlag.c_str());
140 return;
141 }
142
143 llvm::StringSet<> Inputs;
144 for (const auto *InputName : InputFileList)
145 Inputs.insert(InputName);
146 Out.push_back(Executable);
147
148 if (PrependArg)
149 Out.push_back(PrependArg);
150
151
152
153 bool FirstInput = true;
154 for (const auto *Arg : Arguments) {
155 if (Inputs.count(Arg) == 0) {
156 Out.push_back(Arg);
157 } else if (FirstInput) {
158 FirstInput = false;
160 Out.push_back(ResponseFile);
161 }
162 }
163}
164
165
166static void
168 size_t NumArgs,
170 using namespace llvm;
171 using namespace sys;
172
174 if (path::is_absolute(InInc))
175 return false;
176 std::error_code EC = fs::current_path(OutInc);
177 if (EC)
178 return false;
179 path::append(OutInc, InInc);
180 return true;
181 };
182
184 if (NumArgs == 1) {
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)) {
191 NewArg += NewInc;
192 IncFlags.push_back(std::move(NewArg));
193 }
194 return;
195 }
196
197 assert(NumArgs == 2 && "Not expecting more than two arguments");
198 StringRef Inc(Args[Idx + NumArgs - 1]);
199 if (!getAbsPath(Inc, NewInc))
200 return;
202 IncFlags.push_back(std::move(NewInc));
203}
204
205void Command::Print(raw_ostream &OS, const char *Terminator, bool Quote,
207
208 OS << ' ';
209 llvm::sys::printArg(OS, Executable, true);
210
213 if (ResponseFile != nullptr) {
214 buildArgvForResponseFile(ArgsRespFile);
216 } else if (PrependArg) {
217 OS << ' ';
218 llvm::sys::printArg(OS, PrependArg, true);
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
234 if (HaveCrashVFS && IsInclude) {
237 if (!NewIncFlags.empty()) {
238 for (auto &F : NewIncFlags) {
239 OS << ' ';
240 llvm::sys::printArg(OS, F.c_str(), Quote);
241 }
242 i += NumArgs - 1;
243 continue;
244 }
245 }
246
249 });
251 (i == 0 || StringRef(Args[i - 1]) != "-main-file-name")) {
252
253 OS << ' ';
254 StringRef ShortName = llvm::sys::path::filename(CrashInfo->Filename);
255 llvm::sys::printArg(OS, ShortName.str(), Quote);
256 continue;
257 }
258 }
259
260 OS << ' ';
261 llvm::sys::printArg(OS, Arg, Quote);
262 }
263
264 if (CrashInfo && HaveCrashVFS) {
265 OS << ' ';
266 llvm::sys::printArg(OS, "-ivfsoverlay", Quote);
267 OS << ' ';
268 llvm::sys::printArg(OS, CrashInfo->VFSPath.str(), Quote);
269
270
271
272
273
274
275 SmallString<128> RelModCacheDir = llvm::sys::path::parent_path(
276 llvm::sys::path::parent_path(CrashInfo->VFSPath));
277 llvm::sys::path::append(RelModCacheDir, "repro-modules");
278
279 std::string ModCachePath = "-fmodules-cache-path=";
280 ModCachePath.append(RelModCacheDir.c_str());
281
282 OS << ' ';
283 llvm::sys::printArg(OS, ModCachePath, Quote);
284 }
285
286 if (ResponseFile != nullptr) {
287 OS << "\n Arguments passed via response file:\n";
288 writeResponseFile(OS);
289
290
292 OS << "\n";
293 OS << " (end of response file)";
294 }
295
296 OS << Terminator;
297}
298
301 ResponseFileFlag = ResponseSupport.ResponseFlag;
302 ResponseFileFlag += FileName;
303}
304
306 Environment.reserve(NewEnvironment.size() + 1);
307 Environment.assign(NewEnvironment.begin(), NewEnvironment.end());
308 Environment.push_back(nullptr);
309}
310
312 const std::vector<std::optionalstd::string> &Redirects) {
313 RedirectFiles = Redirects;
314}
315
319 llvm::outs() << llvm::sys::path::filename(Arg.getFilename()) << "\n";
320 llvm::outs().flush();
321 }
322}
323
325 std::string *ErrMsg, bool *ExecutionFailed) const {
327
329 if (ResponseFile == nullptr) {
330 Argv.push_back(Executable);
331 if (PrependArg)
332 Argv.push_back(PrependArg);
333 Argv.append(Arguments.begin(), Arguments.end());
334 Argv.push_back(nullptr);
335 } else {
336
337 std::string RespContents;
338 llvm::raw_string_ostream SS(RespContents);
339
340
341 writeResponseFile(SS);
342 buildArgvForResponseFile(Argv);
343 Argv.push_back(nullptr);
344
345
346 if (std::error_code EC = writeFileWithEncoding(
347 ResponseFile, RespContents, ResponseSupport.ResponseEncoding)) {
348 if (ErrMsg)
349 *ErrMsg = EC.message();
350 if (ExecutionFailed)
351 *ExecutionFailed = true;
352
353
354 return -1;
355 }
356 }
357
358 std::optional<ArrayRef> Env;
359 std::vector 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());
365 }
366
367 auto Args = llvm::toStringRefArray(Argv.data());
368
369
370 if (!RedirectFiles.empty()) {
371 std::vector<std::optional> RedirectFilesOptional;
372 for (const auto &Ele : RedirectFiles)
373 if (Ele)
374 RedirectFilesOptional.push_back(std::optional(*Ele));
375 else
376 RedirectFilesOptional.push_back(std::nullopt);
377
378 return llvm::sys::ExecuteAndWait(Executable, Args, Env,
379 ArrayRef(RedirectFilesOptional),
380 0, 0,
381 ErrMsg, ExecutionFailed, &ProcStat);
382 }
383
384 return llvm::sys::ExecuteAndWait(Executable, Args, Env, Redirects,
385 0, 0,
386 ErrMsg, ExecutionFailed, &ProcStat);
387}
388
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) {
398}
399
403 OS << " (in-process)\n";
405}
406
408 std::string *ErrMsg, bool *ExecutionFailed) const {
409
410
411
414
416
420 Argv.push_back(nullptr);
421 Argv.pop_back();
422
423
424
425
426 if (ExecutionFailed)
427 *ExecutionFailed = false;
428
429 llvm::CrashRecoveryContext CRC;
430 CRC.DumpStackAndCleanupOnFailure = true;
431
432 const void *PrettyState = llvm::SavePrettyStackState();
434
435 int R = 0;
436
437 if (!CRC.RunSafely([&]() { R = D.CC1Main(Argv); })) {
438 llvm::RestorePrettyStackState(PrettyState);
439 return CRC.RetCode;
440 }
441 return R;
442}
443
445
446 llvm_unreachable(
447 "The CC1Command doesn't support changing the environment vars!");
448}
449
450void JobList::Print(raw_ostream &OS, const char *Terminator, bool Quote,
452 for (const auto &Job : *this)
453 Job.Print(OS, Terminator, Quote, CrashInfo);
454}
455
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...
InputInfo - Wrapper for information about an input source.
const char * getFilename() const
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.