1//===-- driver.cpp - Clang GCC-Compatible Driver --------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This is the entry point to the clang driver; it is a thin wrapper
10// for functionality in the Driver clang library.
11//
12//===----------------------------------------------------------------------===//
13
14#include "clang/Driver/Driver.h"
15#include "clang/Basic/DiagnosticOptions.h"
16#include "clang/Basic/HeaderInclude.h"
17#include "clang/Basic/Stack.h"
18#include "clang/Config/config.h"
19#include "clang/Driver/Compilation.h"
20#include "clang/Driver/DriverDiagnostic.h"
21#include "clang/Driver/Options.h"
22#include "clang/Driver/ToolChain.h"
23#include "clang/Frontend/ChainedDiagnosticConsumer.h"
24#include "clang/Frontend/CompilerInvocation.h"
25#include "clang/Frontend/SerializedDiagnosticPrinter.h"
26#include "clang/Frontend/TextDiagnosticPrinter.h"
27#include "clang/Frontend/Utils.h"
28#include "llvm/ADT/ArrayRef.h"
29#include "llvm/ADT/SmallString.h"
30#include "llvm/ADT/SmallVector.h"
31#include "llvm/Option/ArgList.h"
32#include "llvm/Option/OptTable.h"
33#include "llvm/Option/Option.h"
34#include "llvm/Support/BuryPointer.h"
35#include "llvm/Support/CommandLine.h"
36#include "llvm/Support/CrashRecoveryContext.h"
37#include "llvm/Support/ErrorHandling.h"
38#include "llvm/Support/FileSystem.h"
39#include "llvm/Support/LLVMDriver.h"
40#include "llvm/Support/Path.h"
41#include "llvm/Support/PrettyStackTrace.h"
42#include "llvm/Support/Process.h"
43#include "llvm/Support/Program.h"
44#include "llvm/Support/Regex.h"
45#include "llvm/Support/Signals.h"
46#include "llvm/Support/StringSaver.h"
47#include "llvm/Support/TargetSelect.h"
48#include "llvm/Support/Timer.h"
49#include "llvm/Support/raw_ostream.h"
50#include "llvm/TargetParser/Host.h"
51#include <memory>
52#include <optional>
53#include <set>
54#include <system_error>
55using namespace clang;
56using namespace clang::driver;
57using namespace llvm::opt;
58
59std::string GetExecutablePath(const char *Argv0, bool CanonicalPrefixes) {
60 if (!CanonicalPrefixes) {
61 SmallString<128> ExecutablePath(Argv0);
62 // Do a PATH lookup if Argv0 isn't a valid path.
63 if (!llvm::sys::fs::exists(Path: ExecutablePath))
64 if (llvm::ErrorOr<std::string> P =
65 llvm::sys::findProgramByName(Name: ExecutablePath))
66 ExecutablePath = *P;
67 return std::string(ExecutablePath);
68 }
69
70 // This just needs to be some symbol in the binary; C++ doesn't
71 // allow taking the address of ::main however.
72 void *P = (void*) (intptr_t) GetExecutablePath;
73 return llvm::sys::fs::getMainExecutable(argv0: Argv0, MainExecAddr: P);
74}
75
76static const char *GetStableCStr(std::set<std::string> &SavedStrings,
77 StringRef S) {
78 return SavedStrings.insert(x: std::string(S)).first->c_str();
79}
80
81/// ApplyOneQAOverride - Apply a list of edits to the input argument lists.
82///
83/// The input string is a space separated list of edits to perform,
84/// they are applied in order to the input argument lists. Edits
85/// should be one of the following forms:
86///
87/// '#': Silence information about the changes to the command line arguments.
88///
89/// '^': Add FOO as a new argument at the beginning of the command line.
90///
91/// '+': Add FOO as a new argument at the end of the command line.
92///
93/// 's/XXX/YYY/': Substitute the regular expression XXX with YYY in the command
94/// line.
95///
96/// 'xOPTION': Removes all instances of the literal argument OPTION.
97///
98/// 'XOPTION': Removes all instances of the literal argument OPTION,
99/// and the following argument.
100///
101/// 'Ox': Removes all flags matching 'O' or 'O[sz0-9]' and adds 'Ox'
102/// at the end of the command line.
103///
104/// \param OS - The stream to write edit information to.
105/// \param Args - The vector of command line arguments.
106/// \param Edit - The override command to perform.
107/// \param SavedStrings - Set to use for storing string representations.
108static void ApplyOneQAOverride(raw_ostream &OS,
109 SmallVectorImpl<const char*> &Args,
110 StringRef Edit,
111 std::set<std::string> &SavedStrings) {
112 // This does not need to be efficient.
113
114 if (Edit[0] == '^') {
115 const char *Str =
116 GetStableCStr(SavedStrings, S: Edit.substr(Start: 1));
117 OS << "### Adding argument " << Str << " at beginning\n";
118 Args.insert(I: Args.begin() + 1, Elt: Str);
119 } else if (Edit[0] == '+') {
120 const char *Str =
121 GetStableCStr(SavedStrings, S: Edit.substr(Start: 1));
122 OS << "### Adding argument " << Str << " at end\n";
123 Args.push_back(Elt: Str);
124 } else if (Edit[0] == 's' && Edit[1] == '/' && Edit.ends_with(Suffix: "/") &&
125 Edit.slice(Start: 2, End: Edit.size() - 1).contains(C: '/')) {
126 StringRef MatchPattern = Edit.substr(Start: 2).split(Separator: '/').first;
127 StringRef ReplPattern = Edit.substr(Start: 2).split(Separator: '/').second;
128 ReplPattern = ReplPattern.slice(Start: 0, End: ReplPattern.size()-1);
129
130 for (unsigned i = 1, e = Args.size(); i != e; ++i) {
131 // Ignore end-of-line response file markers
132 if (Args[i] == nullptr)
133 continue;
134 std::string Repl = llvm::Regex(MatchPattern).sub(Repl: ReplPattern, String: Args[i]);
135
136 if (Repl != Args[i]) {
137 OS << "### Replacing '" << Args[i] << "' with '" << Repl << "'\n";
138 Args[i] = GetStableCStr(SavedStrings, S: Repl);
139 }
140 }
141 } else if (Edit[0] == 'x' || Edit[0] == 'X') {
142 auto Option = Edit.substr(Start: 1);
143 for (unsigned i = 1; i < Args.size();) {
144 if (Option == Args[i]) {
145 OS << "### Deleting argument " << Args[i] << '\n';
146 Args.erase(CI: Args.begin() + i);
147 if (Edit[0] == 'X') {
148 if (i < Args.size()) {
149 OS << "### Deleting argument " << Args[i] << '\n';
150 Args.erase(CI: Args.begin() + i);
151 } else
152 OS << "### Invalid X edit, end of command line!\n";
153 }
154 } else
155 ++i;
156 }
157 } else if (Edit[0] == 'O') {
158 for (unsigned i = 1; i < Args.size();) {
159 const char *A = Args[i];
160 // Ignore end-of-line response file markers
161 if (A == nullptr)
162 continue;
163 if (A[0] == '-' && A[1] == 'O' &&
164 (A[2] == '\0' ||
165 (A[3] == '\0' && (A[2] == 's' || A[2] == 'z' ||
166 ('0' <= A[2] && A[2] <= '9'))))) {
167 OS << "### Deleting argument " << Args[i] << '\n';
168 Args.erase(CI: Args.begin() + i);
169 } else
170 ++i;
171 }
172 OS << "### Adding argument " << Edit << " at end\n";
173 Args.push_back(Elt: GetStableCStr(SavedStrings, S: '-' + Edit.str()));
174 } else {
175 OS << "### Unrecognized edit: " << Edit << "\n";
176 }
177}
178
179/// ApplyQAOverride - Apply a space separated list of edits to the
180/// input argument lists. See ApplyOneQAOverride.
181static void ApplyQAOverride(SmallVectorImpl<const char*> &Args,
182 const char *OverrideStr,
183 std::set<std::string> &SavedStrings) {
184 raw_ostream *OS = &llvm::errs();
185
186 if (OverrideStr[0] == '#') {
187 ++OverrideStr;
188 OS = &llvm::nulls();
189 }
190
191 *OS << "### CCC_OVERRIDE_OPTIONS: " << OverrideStr << "\n";
192
193 // This does not need to be efficient.
194
195 const char *S = OverrideStr;
196 while (*S) {
197 const char *End = ::strchr(s: S, c: ' ');
198 if (!End)
199 End = S + strlen(s: S);
200 if (End != S)
201 ApplyOneQAOverride(OS&: *OS, Args, Edit: std::string(S, End), SavedStrings);
202 S = End;
203 if (*S != '\0')
204 ++S;
205 }
206}
207
208extern int cc1_main(ArrayRef<const char *> Argv, const char *Argv0,
209 void *MainAddr);
210extern int cc1as_main(ArrayRef<const char *> Argv, const char *Argv0,
211 void *MainAddr);
212extern int cc1gen_reproducer_main(ArrayRef<const char *> Argv,
213 const char *Argv0, void *MainAddr,
214 const llvm::ToolContext &);
215
216static void insertTargetAndModeArgs(const ParsedClangName &NameParts,
217 SmallVectorImpl<const char *> &ArgVector,
218 std::set<std::string> &SavedStrings) {
219 // Put target and mode arguments at the start of argument list so that
220 // arguments specified in command line could override them. Avoid putting
221 // them at index 0, as an option like '-cc1' must remain the first.
222 int InsertionPoint = 0;
223 if (ArgVector.size() > 0)
224 ++InsertionPoint;
225
226 if (NameParts.DriverMode) {
227 // Add the mode flag to the arguments.
228 ArgVector.insert(I: ArgVector.begin() + InsertionPoint,
229 Elt: GetStableCStr(SavedStrings, S: NameParts.DriverMode));
230 }
231
232 if (NameParts.TargetIsValid) {
233 const char *arr[] = {"-target", GetStableCStr(SavedStrings,
234 S: NameParts.TargetPrefix)};
235 ArgVector.insert(I: ArgVector.begin() + InsertionPoint,
236 From: std::begin(arr&: arr), To: std::end(arr&: arr));
237 }
238}
239
240static void getCLEnvVarOptions(std::string &EnvValue, llvm::StringSaver &Saver,
241 SmallVectorImpl<const char *> &Opts) {
242 llvm::cl::TokenizeWindowsCommandLine(Source: EnvValue, Saver, NewArgv&: Opts);
243 // The first instance of '#' should be replaced with '=' in each option.
244 for (const char *Opt : Opts)
245 if (char *NumberSignPtr = const_cast<char *>(::strchr(s: Opt, c: '#')))
246 *NumberSignPtr = '=';
247}
248
249template <class T>
250static T checkEnvVar(const char *EnvOptSet, const char *EnvOptFile,
251 std::string &OptFile) {
252 const char *Str = ::getenv(name: EnvOptSet);
253 if (!Str)
254 return T{};
255
256 T OptVal = Str;
257 if (const char *Var = ::getenv(name: EnvOptFile))
258 OptFile = Var;
259 return OptVal;
260}
261
262static bool SetBackdoorDriverOutputsFromEnvVars(Driver &TheDriver) {
263 TheDriver.CCPrintOptions =
264 checkEnvVar<bool>(EnvOptSet: "CC_PRINT_OPTIONS", EnvOptFile: "CC_PRINT_OPTIONS_FILE",
265 OptFile&: TheDriver.CCPrintOptionsFilename);
266 if (checkEnvVar<bool>(EnvOptSet: "CC_PRINT_HEADERS", EnvOptFile: "CC_PRINT_HEADERS_FILE",
267 OptFile&: TheDriver.CCPrintHeadersFilename)) {
268 TheDriver.CCPrintHeadersFormat = HIFMT_Textual;
269 TheDriver.CCPrintHeadersFiltering = HIFIL_None;
270 } else {
271 std::string EnvVar = checkEnvVar<std::string>(
272 EnvOptSet: "CC_PRINT_HEADERS_FORMAT", EnvOptFile: "CC_PRINT_HEADERS_FILE",
273 OptFile&: TheDriver.CCPrintHeadersFilename);
274 if (!EnvVar.empty()) {
275 TheDriver.CCPrintHeadersFormat =
276 stringToHeaderIncludeFormatKind(Str: EnvVar.c_str());
277 if (!TheDriver.CCPrintHeadersFormat) {
278 TheDriver.Diag(clang::diag::DiagID: err_drv_print_header_env_var)
279 << 0 << EnvVar;
280 return false;
281 }
282
283 const char *FilteringStr = ::getenv(name: "CC_PRINT_HEADERS_FILTERING");
284 HeaderIncludeFilteringKind Filtering;
285 if (!stringToHeaderIncludeFiltering(Str: FilteringStr, Kind&: Filtering)) {
286 TheDriver.Diag(clang::diag::err_drv_print_header_env_var)
287 << 1 << FilteringStr;
288 return false;
289 }
290
291 if ((TheDriver.CCPrintHeadersFormat == HIFMT_Textual &&
292 Filtering != HIFIL_None) ||
293 (TheDriver.CCPrintHeadersFormat == HIFMT_JSON &&
294 Filtering != HIFIL_Only_Direct_System)) {
295 TheDriver.Diag(clang::diag::err_drv_print_header_env_var_combination)
296 << EnvVar << FilteringStr;
297 return false;
298 }
299 TheDriver.CCPrintHeadersFiltering = Filtering;
300 }
301 }
302
303 TheDriver.CCLogDiagnostics =
304 checkEnvVar<bool>(EnvOptSet: "CC_LOG_DIAGNOSTICS", EnvOptFile: "CC_LOG_DIAGNOSTICS_FILE",
305 OptFile&: TheDriver.CCLogDiagnosticsFilename);
306 TheDriver.CCPrintProcessStats =
307 checkEnvVar<bool>(EnvOptSet: "CC_PRINT_PROC_STAT", EnvOptFile: "CC_PRINT_PROC_STAT_FILE",
308 OptFile&: TheDriver.CCPrintStatReportFilename);
309 TheDriver.CCPrintInternalStats =
310 checkEnvVar<bool>(EnvOptSet: "CC_PRINT_INTERNAL_STAT", EnvOptFile: "CC_PRINT_INTERNAL_STAT_FILE",
311 OptFile&: TheDriver.CCPrintInternalStatReportFilename);
312
313 return true;
314}
315
316static void FixupDiagPrefixExeName(TextDiagnosticPrinter *DiagClient,
317 const std::string &Path) {
318 // If the clang binary happens to be named cl.exe for compatibility reasons,
319 // use clang-cl.exe as the prefix to avoid confusion between clang and MSVC.
320 StringRef ExeBasename(llvm::sys::path::stem(path: Path));
321 if (ExeBasename.equals_insensitive(RHS: "cl"))
322 ExeBasename = "clang-cl";
323 DiagClient->setPrefix(std::string(ExeBasename));
324}
325
326static void SetInstallDir(SmallVectorImpl<const char *> &argv,
327 Driver &TheDriver, bool CanonicalPrefixes) {
328 // Attempt to find the original path used to invoke the driver, to determine
329 // the installed path. We do this manually, because we want to support that
330 // path being a symlink.
331 SmallString<128> InstalledPath(argv[0]);
332
333 // Do a PATH lookup, if there are no directory components.
334 if (llvm::sys::path::filename(path: InstalledPath) == InstalledPath)
335 if (llvm::ErrorOr<std::string> Tmp = llvm::sys::findProgramByName(
336 Name: llvm::sys::path::filename(path: InstalledPath.str())))
337 InstalledPath = *Tmp;
338
339 // FIXME: We don't actually canonicalize this, we just make it absolute.
340 if (CanonicalPrefixes)
341 llvm::sys::fs::make_absolute(path&: InstalledPath);
342
343 StringRef InstalledPathParent(llvm::sys::path::parent_path(path: InstalledPath));
344 if (llvm::sys::fs::exists(Path: InstalledPathParent))
345 TheDriver.setInstalledDir(InstalledPathParent);
346}
347
348static int ExecuteCC1Tool(SmallVectorImpl<const char *> &ArgV,
349 const llvm::ToolContext &ToolContext) {
350 // If we call the cc1 tool from the clangDriver library (through
351 // Driver::CC1Main), we need to clean up the options usage count. The options
352 // are currently global, and they might have been used previously by the
353 // driver.
354 llvm::cl::ResetAllOptionOccurrences();
355
356 llvm::BumpPtrAllocator A;
357 llvm::cl::ExpansionContext ECtx(A, llvm::cl::TokenizeGNUCommandLine);
358 if (llvm::Error Err = ECtx.expandResponseFiles(Argv&: ArgV)) {
359 llvm::errs() << toString(E: std::move(Err)) << '\n';
360 return 1;
361 }
362 StringRef Tool = ArgV[1];
363 void *GetExecutablePathVP = (void *)(intptr_t)GetExecutablePath;
364 if (Tool == "-cc1")
365 return cc1_main(Argv: ArrayRef(ArgV).slice(N: 1), Argv0: ArgV[0], MainAddr: GetExecutablePathVP);
366 if (Tool == "-cc1as")
367 return cc1as_main(Argv: ArrayRef(ArgV).slice(N: 2), Argv0: ArgV[0], MainAddr: GetExecutablePathVP);
368 if (Tool == "-cc1gen-reproducer")
369 return cc1gen_reproducer_main(Argv: ArrayRef(ArgV).slice(N: 2), Argv0: ArgV[0],
370 MainAddr: GetExecutablePathVP, ToolContext);
371 // Reject unknown tools.
372 llvm::errs() << "error: unknown integrated tool '" << Tool << "'. "
373 << "Valid tools include '-cc1' and '-cc1as'.\n";
374 return 1;
375}
376
377int clang_main(int Argc, char **Argv, const llvm::ToolContext &ToolContext) {
378 noteBottomOfStack();
379 llvm::setBugReportMsg("PLEASE submit a bug report to " BUG_REPORT_URL
380 " and include the crash backtrace, preprocessed "
381 "source, and associated run script.\n");
382 SmallVector<const char *, 256> Args(Argv, Argv + Argc);
383
384 if (llvm::sys::Process::FixupStandardFileDescriptors())
385 return 1;
386
387 llvm::InitializeAllTargets();
388
389 llvm::BumpPtrAllocator A;
390 llvm::StringSaver Saver(A);
391
392 const char *ProgName =
393 ToolContext.NeedsPrependArg ? ToolContext.PrependArg : ToolContext.Path;
394
395 bool ClangCLMode =
396 IsClangCL(DriverMode: getDriverMode(ProgName, Args: llvm::ArrayRef(Args).slice(N: 1)));
397
398 if (llvm::Error Err = expandResponseFiles(Args, ClangCLMode, Alloc&: A)) {
399 llvm::errs() << toString(E: std::move(Err)) << '\n';
400 return 1;
401 }
402
403 // Handle -cc1 integrated tools.
404 if (Args.size() >= 2 && StringRef(Args[1]).starts_with(Prefix: "-cc1"))
405 return ExecuteCC1Tool(ArgV&: Args, ToolContext);
406
407 // Handle options that need handling before the real command line parsing in
408 // Driver::BuildCompilation()
409 bool CanonicalPrefixes = true;
410 for (int i = 1, size = Args.size(); i < size; ++i) {
411 // Skip end-of-line response file markers
412 if (Args[i] == nullptr)
413 continue;
414 if (StringRef(Args[i]) == "-canonical-prefixes")
415 CanonicalPrefixes = true;
416 else if (StringRef(Args[i]) == "-no-canonical-prefixes")
417 CanonicalPrefixes = false;
418 }
419
420 // Handle CL and _CL_ which permits additional command line options to be
421 // prepended or appended.
422 if (ClangCLMode) {
423 // Arguments in "CL" are prepended.
424 std::optional<std::string> OptCL = llvm::sys::Process::GetEnv(name: "CL");
425 if (OptCL) {
426 SmallVector<const char *, 8> PrependedOpts;
427 getCLEnvVarOptions(EnvValue&: *OptCL, Saver, Opts&: PrependedOpts);
428
429 // Insert right after the program name to prepend to the argument list.
430 Args.insert(I: Args.begin() + 1, From: PrependedOpts.begin(), To: PrependedOpts.end());
431 }
432 // Arguments in "_CL_" are appended.
433 std::optional<std::string> Opt_CL_ = llvm::sys::Process::GetEnv(name: "_CL_");
434 if (Opt_CL_) {
435 SmallVector<const char *, 8> AppendedOpts;
436 getCLEnvVarOptions(EnvValue&: *Opt_CL_, Saver, Opts&: AppendedOpts);
437
438 // Insert at the end of the argument list to append.
439 Args.append(in_start: AppendedOpts.begin(), in_end: AppendedOpts.end());
440 }
441 }
442
443 std::set<std::string> SavedStrings;
444 // Handle CCC_OVERRIDE_OPTIONS, used for editing a command line behind the
445 // scenes.
446 if (const char *OverrideStr = ::getenv(name: "CCC_OVERRIDE_OPTIONS")) {
447 // FIXME: Driver shouldn't take extra initial argument.
448 ApplyQAOverride(Args, OverrideStr, SavedStrings);
449 }
450
451 std::string Path = GetExecutablePath(Argv0: ToolContext.Path, CanonicalPrefixes);
452
453 // Whether the cc1 tool should be called inside the current process, or if we
454 // should spawn a new clang subprocess (old behavior).
455 // Not having an additional process saves some execution time of Windows,
456 // and makes debugging and profiling easier.
457 bool UseNewCC1Process = CLANG_SPAWN_CC1;
458 for (const char *Arg : Args)
459 UseNewCC1Process = llvm::StringSwitch<bool>(Arg)
460 .Case(S: "-fno-integrated-cc1", Value: true)
461 .Case(S: "-fintegrated-cc1", Value: false)
462 .Default(Value: UseNewCC1Process);
463
464 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts =
465 CreateAndPopulateDiagOpts(Argv: Args);
466
467 TextDiagnosticPrinter *DiagClient
468 = new TextDiagnosticPrinter(llvm::errs(), &*DiagOpts);
469 FixupDiagPrefixExeName(DiagClient, Path: ProgName);
470
471 IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
472
473 DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagClient);
474
475 if (!DiagOpts->DiagnosticSerializationFile.empty()) {
476 auto SerializedConsumer =
477 clang::serialized_diags::create(OutputFile: DiagOpts->DiagnosticSerializationFile,
478 Diags: &*DiagOpts, /*MergeChildRecords=*/true);
479 Diags.setClient(client: new ChainedDiagnosticConsumer(
480 Diags.takeClient(), std::move(SerializedConsumer)));
481 }
482
483 ProcessWarningOptions(Diags, Opts: *DiagOpts, /*ReportDiags=*/false);
484
485 Driver TheDriver(Path, llvm::sys::getDefaultTargetTriple(), Diags);
486 SetInstallDir(argv&: Args, TheDriver, CanonicalPrefixes);
487 auto TargetAndMode = ToolChain::getTargetAndModeFromProgramName(ProgName);
488 TheDriver.setTargetAndMode(TargetAndMode);
489 // If -canonical-prefixes is set, GetExecutablePath will have resolved Path
490 // to the llvm driver binary, not clang. In this case, we need to use
491 // PrependArg which should be clang-*. Checking just CanonicalPrefixes is
492 // safe even in the normal case because PrependArg will be null so
493 // setPrependArg will be a no-op.
494 if (ToolContext.NeedsPrependArg || CanonicalPrefixes)
495 TheDriver.setPrependArg(ToolContext.PrependArg);
496
497 insertTargetAndModeArgs(NameParts: TargetAndMode, ArgVector&: Args, SavedStrings);
498
499 if (!SetBackdoorDriverOutputsFromEnvVars(TheDriver))
500 return 1;
501
502 if (!UseNewCC1Process) {
503 TheDriver.CC1Main = [ToolContext](SmallVectorImpl<const char *> &ArgV) {
504 return ExecuteCC1Tool(ArgV, ToolContext);
505 };
506 // Ensure the CC1Command actually catches cc1 crashes
507 llvm::CrashRecoveryContext::Enable();
508 }
509
510 std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(Args));
511
512 Driver::ReproLevel ReproLevel = Driver::ReproLevel::OnCrash;
513 if (Arg *A = C->getArgs().getLastArg(options::OPT_gen_reproducer_eq)) {
514 auto Level =
515 llvm::StringSwitch<std::optional<Driver::ReproLevel>>(A->getValue())
516 .Case(S: "off", Value: Driver::ReproLevel::Off)
517 .Case(S: "crash", Value: Driver::ReproLevel::OnCrash)
518 .Case(S: "error", Value: Driver::ReproLevel::OnError)
519 .Case(S: "always", Value: Driver::ReproLevel::Always)
520 .Default(Value: std::nullopt);
521 if (!Level) {
522 llvm::errs() << "Unknown value for " << A->getSpelling() << ": '"
523 << A->getValue() << "'\n";
524 return 1;
525 }
526 ReproLevel = *Level;
527 }
528 if (!!::getenv(name: "FORCE_CLANG_DIAGNOSTICS_CRASH"))
529 ReproLevel = Driver::ReproLevel::Always;
530
531 int Res = 1;
532 bool IsCrash = false;
533 Driver::CommandStatus CommandStatus = Driver::CommandStatus::Ok;
534 // Pretend the first command failed if ReproStatus is Always.
535 const Command *FailingCommand = nullptr;
536 if (!C->getJobs().empty())
537 FailingCommand = &*C->getJobs().begin();
538 if (C && !C->containsError()) {
539 SmallVector<std::pair<int, const Command *>, 4> FailingCommands;
540 Res = TheDriver.ExecuteCompilation(C&: *C, FailingCommands);
541
542 for (const auto &P : FailingCommands) {
543 int CommandRes = P.first;
544 FailingCommand = P.second;
545 if (!Res)
546 Res = CommandRes;
547
548 // If result status is < 0, then the driver command signalled an error.
549 // If result status is 70, then the driver command reported a fatal error.
550 // On Windows, abort will return an exit code of 3. In these cases,
551 // generate additional diagnostic information if possible.
552 IsCrash = CommandRes < 0 || CommandRes == 70;
553#ifdef _WIN32
554 IsCrash |= CommandRes == 3;
555#endif
556#if LLVM_ON_UNIX
557 // When running in integrated-cc1 mode, the CrashRecoveryContext returns
558 // the same codes as if the program crashed. See section "Exit Status for
559 // Commands":
560 // https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xcu_chap02.html
561 IsCrash |= CommandRes > 128;
562#endif
563 CommandStatus =
564 IsCrash ? Driver::CommandStatus::Crash : Driver::CommandStatus::Error;
565 if (IsCrash)
566 break;
567 }
568 }
569
570 // Print the bug report message that would be printed if we did actually
571 // crash, but only if we're crashing due to FORCE_CLANG_DIAGNOSTICS_CRASH.
572 if (::getenv(name: "FORCE_CLANG_DIAGNOSTICS_CRASH"))
573 llvm::dbgs() << llvm::getBugReportMsg();
574 if (FailingCommand != nullptr &&
575 TheDriver.maybeGenerateCompilationDiagnostics(CS: CommandStatus, Level: ReproLevel,
576 C&: *C, FailingCommand: *FailingCommand))
577 Res = 1;
578
579 Diags.getClient()->finish();
580
581 if (!UseNewCC1Process && IsCrash) {
582 // When crashing in -fintegrated-cc1 mode, bury the timer pointers, because
583 // the internal linked list might point to already released stack frames.
584 llvm::BuryPointer(Ptr: llvm::TimerGroup::aquireDefaultGroup());
585 } else {
586 // If any timers were active but haven't been destroyed yet, print their
587 // results now. This happens in -disable-free mode.
588 llvm::TimerGroup::printAll(OS&: llvm::errs());
589 llvm::TimerGroup::clearAll();
590 }
591
592#ifdef _WIN32
593 // Exit status should not be negative on Win32, unless abnormal termination.
594 // Once abnormal termination was caught, negative status should not be
595 // propagated.
596 if (Res < 0)
597 Res = 1;
598#endif
599
600 // If we have multiple failing commands, we return the result of the first
601 // failing command.
602 return Res;
603}
604

source code of clang/tools/driver/driver.cpp