Home | History | Annotate | Line # | Download | only in driver
driver.cpp revision 1.1
      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/Stack.h"
     17 #include "clang/Driver/Compilation.h"
     18 #include "clang/Driver/DriverDiagnostic.h"
     19 #include "clang/Driver/Options.h"
     20 #include "clang/Driver/ToolChain.h"
     21 #include "clang/Frontend/ChainedDiagnosticConsumer.h"
     22 #include "clang/Frontend/CompilerInvocation.h"
     23 #include "clang/Frontend/SerializedDiagnosticPrinter.h"
     24 #include "clang/Frontend/TextDiagnosticPrinter.h"
     25 #include "clang/Frontend/Utils.h"
     26 #include "llvm/ADT/ArrayRef.h"
     27 #include "llvm/ADT/SmallString.h"
     28 #include "llvm/ADT/SmallVector.h"
     29 #include "llvm/Option/ArgList.h"
     30 #include "llvm/Option/OptTable.h"
     31 #include "llvm/Option/Option.h"
     32 #include "llvm/Support/CommandLine.h"
     33 #include "llvm/Support/ErrorHandling.h"
     34 #include "llvm/Support/FileSystem.h"
     35 #include "llvm/Support/Host.h"
     36 #include "llvm/Support/InitLLVM.h"
     37 #include "llvm/Support/Path.h"
     38 #include "llvm/Support/Process.h"
     39 #include "llvm/Support/Program.h"
     40 #include "llvm/Support/Regex.h"
     41 #include "llvm/Support/Signals.h"
     42 #include "llvm/Support/StringSaver.h"
     43 #include "llvm/Support/TargetSelect.h"
     44 #include "llvm/Support/Timer.h"
     45 #include "llvm/Support/raw_ostream.h"
     46 #include <memory>
     47 #include <set>
     48 #include <system_error>
     49 using namespace clang;
     50 using namespace clang::driver;
     51 using namespace llvm::opt;
     52 
     53 std::string GetExecutablePath(const char *Argv0, bool CanonicalPrefixes) {
     54   if (!CanonicalPrefixes) {
     55     SmallString<128> ExecutablePath(Argv0);
     56     // Do a PATH lookup if Argv0 isn't a valid path.
     57     if (!llvm::sys::fs::exists(ExecutablePath))
     58       if (llvm::ErrorOr<std::string> P =
     59               llvm::sys::findProgramByName(ExecutablePath))
     60         ExecutablePath = *P;
     61     return ExecutablePath.str();
     62   }
     63 
     64   // This just needs to be some symbol in the binary; C++ doesn't
     65   // allow taking the address of ::main however.
     66   void *P = (void*) (intptr_t) GetExecutablePath;
     67   return llvm::sys::fs::getMainExecutable(Argv0, P);
     68 }
     69 
     70 static const char *GetStableCStr(std::set<std::string> &SavedStrings,
     71                                  StringRef S) {
     72   return SavedStrings.insert(S).first->c_str();
     73 }
     74 
     75 /// ApplyQAOverride - Apply a list of edits to the input argument lists.
     76 ///
     77 /// The input string is a space separate list of edits to perform,
     78 /// they are applied in order to the input argument lists. Edits
     79 /// should be one of the following forms:
     80 ///
     81 ///  '#': Silence information about the changes to the command line arguments.
     82 ///
     83 ///  '^': Add FOO as a new argument at the beginning of the command line.
     84 ///
     85 ///  '+': Add FOO as a new argument at the end of the command line.
     86 ///
     87 ///  's/XXX/YYY/': Substitute the regular expression XXX with YYY in the command
     88 ///  line.
     89 ///
     90 ///  'xOPTION': Removes all instances of the literal argument OPTION.
     91 ///
     92 ///  'XOPTION': Removes all instances of the literal argument OPTION,
     93 ///  and the following argument.
     94 ///
     95 ///  'Ox': Removes all flags matching 'O' or 'O[sz0-9]' and adds 'Ox'
     96 ///  at the end of the command line.
     97 ///
     98 /// \param OS - The stream to write edit information to.
     99 /// \param Args - The vector of command line arguments.
    100 /// \param Edit - The override command to perform.
    101 /// \param SavedStrings - Set to use for storing string representations.
    102 static void ApplyOneQAOverride(raw_ostream &OS,
    103                                SmallVectorImpl<const char*> &Args,
    104                                StringRef Edit,
    105                                std::set<std::string> &SavedStrings) {
    106   // This does not need to be efficient.
    107 
    108   if (Edit[0] == '^') {
    109     const char *Str =
    110       GetStableCStr(SavedStrings, Edit.substr(1));
    111     OS << "### Adding argument " << Str << " at beginning\n";
    112     Args.insert(Args.begin() + 1, Str);
    113   } else if (Edit[0] == '+') {
    114     const char *Str =
    115       GetStableCStr(SavedStrings, Edit.substr(1));
    116     OS << "### Adding argument " << Str << " at end\n";
    117     Args.push_back(Str);
    118   } else if (Edit[0] == 's' && Edit[1] == '/' && Edit.endswith("/") &&
    119              Edit.slice(2, Edit.size()-1).find('/') != StringRef::npos) {
    120     StringRef MatchPattern = Edit.substr(2).split('/').first;
    121     StringRef ReplPattern = Edit.substr(2).split('/').second;
    122     ReplPattern = ReplPattern.slice(0, ReplPattern.size()-1);
    123 
    124     for (unsigned i = 1, e = Args.size(); i != e; ++i) {
    125       // Ignore end-of-line response file markers
    126       if (Args[i] == nullptr)
    127         continue;
    128       std::string Repl = llvm::Regex(MatchPattern).sub(ReplPattern, Args[i]);
    129 
    130       if (Repl != Args[i]) {
    131         OS << "### Replacing '" << Args[i] << "' with '" << Repl << "'\n";
    132         Args[i] = GetStableCStr(SavedStrings, Repl);
    133       }
    134     }
    135   } else if (Edit[0] == 'x' || Edit[0] == 'X') {
    136     auto Option = Edit.substr(1);
    137     for (unsigned i = 1; i < Args.size();) {
    138       if (Option == Args[i]) {
    139         OS << "### Deleting argument " << Args[i] << '\n';
    140         Args.erase(Args.begin() + i);
    141         if (Edit[0] == 'X') {
    142           if (i < Args.size()) {
    143             OS << "### Deleting argument " << Args[i] << '\n';
    144             Args.erase(Args.begin() + i);
    145           } else
    146             OS << "### Invalid X edit, end of command line!\n";
    147         }
    148       } else
    149         ++i;
    150     }
    151   } else if (Edit[0] == 'O') {
    152     for (unsigned i = 1; i < Args.size();) {
    153       const char *A = Args[i];
    154       // Ignore end-of-line response file markers
    155       if (A == nullptr)
    156         continue;
    157       if (A[0] == '-' && A[1] == 'O' &&
    158           (A[2] == '\0' ||
    159            (A[3] == '\0' && (A[2] == 's' || A[2] == 'z' ||
    160                              ('0' <= A[2] && A[2] <= '9'))))) {
    161         OS << "### Deleting argument " << Args[i] << '\n';
    162         Args.erase(Args.begin() + i);
    163       } else
    164         ++i;
    165     }
    166     OS << "### Adding argument " << Edit << " at end\n";
    167     Args.push_back(GetStableCStr(SavedStrings, '-' + Edit.str()));
    168   } else {
    169     OS << "### Unrecognized edit: " << Edit << "\n";
    170   }
    171 }
    172 
    173 /// ApplyQAOverride - Apply a comma separate list of edits to the
    174 /// input argument lists. See ApplyOneQAOverride.
    175 static void ApplyQAOverride(SmallVectorImpl<const char*> &Args,
    176                             const char *OverrideStr,
    177                             std::set<std::string> &SavedStrings) {
    178   raw_ostream *OS = &llvm::errs();
    179 
    180   if (OverrideStr[0] == '#') {
    181     ++OverrideStr;
    182     OS = &llvm::nulls();
    183   }
    184 
    185   *OS << "### CCC_OVERRIDE_OPTIONS: " << OverrideStr << "\n";
    186 
    187   // This does not need to be efficient.
    188 
    189   const char *S = OverrideStr;
    190   while (*S) {
    191     const char *End = ::strchr(S, ' ');
    192     if (!End)
    193       End = S + strlen(S);
    194     if (End != S)
    195       ApplyOneQAOverride(*OS, Args, std::string(S, End), SavedStrings);
    196     S = End;
    197     if (*S != '\0')
    198       ++S;
    199   }
    200 }
    201 
    202 extern int cc1_main(ArrayRef<const char *> Argv, const char *Argv0,
    203                     void *MainAddr);
    204 extern int cc1as_main(ArrayRef<const char *> Argv, const char *Argv0,
    205                       void *MainAddr);
    206 extern int cc1gen_reproducer_main(ArrayRef<const char *> Argv,
    207                                   const char *Argv0, void *MainAddr);
    208 
    209 static void insertTargetAndModeArgs(const ParsedClangName &NameParts,
    210                                     SmallVectorImpl<const char *> &ArgVector,
    211                                     std::set<std::string> &SavedStrings) {
    212   // Put target and mode arguments at the start of argument list so that
    213   // arguments specified in command line could override them. Avoid putting
    214   // them at index 0, as an option like '-cc1' must remain the first.
    215   int InsertionPoint = 0;
    216   if (ArgVector.size() > 0)
    217     ++InsertionPoint;
    218 
    219   if (NameParts.DriverMode) {
    220     // Add the mode flag to the arguments.
    221     ArgVector.insert(ArgVector.begin() + InsertionPoint,
    222                      GetStableCStr(SavedStrings, NameParts.DriverMode));
    223   }
    224 
    225   if (NameParts.TargetIsValid) {
    226     const char *arr[] = {"-target", GetStableCStr(SavedStrings,
    227                                                   NameParts.TargetPrefix)};
    228     ArgVector.insert(ArgVector.begin() + InsertionPoint,
    229                      std::begin(arr), std::end(arr));
    230   }
    231 }
    232 
    233 static void getCLEnvVarOptions(std::string &EnvValue, llvm::StringSaver &Saver,
    234                                SmallVectorImpl<const char *> &Opts) {
    235   llvm::cl::TokenizeWindowsCommandLine(EnvValue, Saver, Opts);
    236   // The first instance of '#' should be replaced with '=' in each option.
    237   for (const char *Opt : Opts)
    238     if (char *NumberSignPtr = const_cast<char *>(::strchr(Opt, '#')))
    239       *NumberSignPtr = '=';
    240 }
    241 
    242 static void SetBackdoorDriverOutputsFromEnvVars(Driver &TheDriver) {
    243   // Handle CC_PRINT_OPTIONS and CC_PRINT_OPTIONS_FILE.
    244   TheDriver.CCPrintOptions = !!::getenv("CC_PRINT_OPTIONS");
    245   if (TheDriver.CCPrintOptions)
    246     TheDriver.CCPrintOptionsFilename = ::getenv("CC_PRINT_OPTIONS_FILE");
    247 
    248   // Handle CC_PRINT_HEADERS and CC_PRINT_HEADERS_FILE.
    249   TheDriver.CCPrintHeaders = !!::getenv("CC_PRINT_HEADERS");
    250   if (TheDriver.CCPrintHeaders)
    251     TheDriver.CCPrintHeadersFilename = ::getenv("CC_PRINT_HEADERS_FILE");
    252 
    253   // Handle CC_LOG_DIAGNOSTICS and CC_LOG_DIAGNOSTICS_FILE.
    254   TheDriver.CCLogDiagnostics = !!::getenv("CC_LOG_DIAGNOSTICS");
    255   if (TheDriver.CCLogDiagnostics)
    256     TheDriver.CCLogDiagnosticsFilename = ::getenv("CC_LOG_DIAGNOSTICS_FILE");
    257 }
    258 
    259 static void FixupDiagPrefixExeName(TextDiagnosticPrinter *DiagClient,
    260                                    const std::string &Path) {
    261   // If the clang binary happens to be named cl.exe for compatibility reasons,
    262   // use clang-cl.exe as the prefix to avoid confusion between clang and MSVC.
    263   StringRef ExeBasename(llvm::sys::path::stem(Path));
    264   if (ExeBasename.equals_lower("cl"))
    265     ExeBasename = "clang-cl";
    266   DiagClient->setPrefix(ExeBasename);
    267 }
    268 
    269 // This lets us create the DiagnosticsEngine with a properly-filled-out
    270 // DiagnosticOptions instance.
    271 static DiagnosticOptions *
    272 CreateAndPopulateDiagOpts(ArrayRef<const char *> argv) {
    273   auto *DiagOpts = new DiagnosticOptions;
    274   unsigned MissingArgIndex, MissingArgCount;
    275   InputArgList Args = getDriverOptTable().ParseArgs(
    276       argv.slice(1), MissingArgIndex, MissingArgCount);
    277   // We ignore MissingArgCount and the return value of ParseDiagnosticArgs.
    278   // Any errors that would be diagnosed here will also be diagnosed later,
    279   // when the DiagnosticsEngine actually exists.
    280   (void)ParseDiagnosticArgs(*DiagOpts, Args);
    281   return DiagOpts;
    282 }
    283 
    284 static void SetInstallDir(SmallVectorImpl<const char *> &argv,
    285                           Driver &TheDriver, bool CanonicalPrefixes) {
    286   // Attempt to find the original path used to invoke the driver, to determine
    287   // the installed path. We do this manually, because we want to support that
    288   // path being a symlink.
    289   SmallString<128> InstalledPath(argv[0]);
    290 
    291   // Do a PATH lookup, if there are no directory components.
    292   if (llvm::sys::path::filename(InstalledPath) == InstalledPath)
    293     if (llvm::ErrorOr<std::string> Tmp = llvm::sys::findProgramByName(
    294             llvm::sys::path::filename(InstalledPath.str())))
    295       InstalledPath = *Tmp;
    296 
    297   // FIXME: We don't actually canonicalize this, we just make it absolute.
    298   if (CanonicalPrefixes)
    299     llvm::sys::fs::make_absolute(InstalledPath);
    300 
    301   StringRef InstalledPathParent(llvm::sys::path::parent_path(InstalledPath));
    302   if (llvm::sys::fs::exists(InstalledPathParent))
    303     TheDriver.setInstalledDir(InstalledPathParent);
    304 }
    305 
    306 static int ExecuteCC1Tool(ArrayRef<const char *> argv, StringRef Tool) {
    307   void *GetExecutablePathVP = (void *)(intptr_t) GetExecutablePath;
    308   if (Tool == "")
    309     return cc1_main(argv.slice(2), argv[0], GetExecutablePathVP);
    310   if (Tool == "as")
    311     return cc1as_main(argv.slice(2), argv[0], GetExecutablePathVP);
    312   if (Tool == "gen-reproducer")
    313     return cc1gen_reproducer_main(argv.slice(2), argv[0], GetExecutablePathVP);
    314 
    315   // Reject unknown tools.
    316   llvm::errs() << "error: unknown integrated tool '" << Tool << "'. "
    317                << "Valid tools include '-cc1' and '-cc1as'.\n";
    318   return 1;
    319 }
    320 
    321 int main(int argc_, const char **argv_) {
    322   noteBottomOfStack();
    323   llvm::InitLLVM X(argc_, argv_);
    324   SmallVector<const char *, 256> argv(argv_, argv_ + argc_);
    325 
    326   if (llvm::sys::Process::FixupStandardFileDescriptors())
    327     return 1;
    328 
    329   llvm::InitializeAllTargets();
    330   auto TargetAndMode = ToolChain::getTargetAndModeFromProgramName(argv[0]);
    331 
    332   llvm::BumpPtrAllocator A;
    333   llvm::StringSaver Saver(A);
    334 
    335   // Parse response files using the GNU syntax, unless we're in CL mode. There
    336   // are two ways to put clang in CL compatibility mode: argv[0] is either
    337   // clang-cl or cl, or --driver-mode=cl is on the command line. The normal
    338   // command line parsing can't happen until after response file parsing, so we
    339   // have to manually search for a --driver-mode=cl argument the hard way.
    340   // Finally, our -cc1 tools don't care which tokenization mode we use because
    341   // response files written by clang will tokenize the same way in either mode.
    342   bool ClangCLMode = false;
    343   if (StringRef(TargetAndMode.DriverMode).equals("--driver-mode=cl") ||
    344       llvm::find_if(argv, [](const char *F) {
    345         return F && strcmp(F, "--driver-mode=cl") == 0;
    346       }) != argv.end()) {
    347     ClangCLMode = true;
    348   }
    349   enum { Default, POSIX, Windows } RSPQuoting = Default;
    350   for (const char *F : argv) {
    351     if (strcmp(F, "--rsp-quoting=posix") == 0)
    352       RSPQuoting = POSIX;
    353     else if (strcmp(F, "--rsp-quoting=windows") == 0)
    354       RSPQuoting = Windows;
    355   }
    356 
    357   // Determines whether we want nullptr markers in argv to indicate response
    358   // files end-of-lines. We only use this for the /LINK driver argument with
    359   // clang-cl.exe on Windows.
    360   bool MarkEOLs = ClangCLMode;
    361 
    362   llvm::cl::TokenizerCallback Tokenizer;
    363   if (RSPQuoting == Windows || (RSPQuoting == Default && ClangCLMode))
    364     Tokenizer = &llvm::cl::TokenizeWindowsCommandLine;
    365   else
    366     Tokenizer = &llvm::cl::TokenizeGNUCommandLine;
    367 
    368   if (MarkEOLs && argv.size() > 1 && StringRef(argv[1]).startswith("-cc1"))
    369     MarkEOLs = false;
    370   llvm::cl::ExpandResponseFiles(Saver, Tokenizer, argv, MarkEOLs);
    371 
    372   // Handle -cc1 integrated tools, even if -cc1 was expanded from a response
    373   // file.
    374   auto FirstArg = std::find_if(argv.begin() + 1, argv.end(),
    375                                [](const char *A) { return A != nullptr; });
    376   if (FirstArg != argv.end() && StringRef(*FirstArg).startswith("-cc1")) {
    377     // If -cc1 came from a response file, remove the EOL sentinels.
    378     if (MarkEOLs) {
    379       auto newEnd = std::remove(argv.begin(), argv.end(), nullptr);
    380       argv.resize(newEnd - argv.begin());
    381     }
    382     return ExecuteCC1Tool(argv, argv[1] + 4);
    383   }
    384 
    385   bool CanonicalPrefixes = true;
    386   for (int i = 1, size = argv.size(); i < size; ++i) {
    387     // Skip end-of-line response file markers
    388     if (argv[i] == nullptr)
    389       continue;
    390     if (StringRef(argv[i]) == "-no-canonical-prefixes") {
    391       CanonicalPrefixes = false;
    392       break;
    393     }
    394   }
    395 
    396   // Handle CL and _CL_ which permits additional command line options to be
    397   // prepended or appended.
    398   if (ClangCLMode) {
    399     // Arguments in "CL" are prepended.
    400     llvm::Optional<std::string> OptCL = llvm::sys::Process::GetEnv("CL");
    401     if (OptCL.hasValue()) {
    402       SmallVector<const char *, 8> PrependedOpts;
    403       getCLEnvVarOptions(OptCL.getValue(), Saver, PrependedOpts);
    404 
    405       // Insert right after the program name to prepend to the argument list.
    406       argv.insert(argv.begin() + 1, PrependedOpts.begin(), PrependedOpts.end());
    407     }
    408     // Arguments in "_CL_" are appended.
    409     llvm::Optional<std::string> Opt_CL_ = llvm::sys::Process::GetEnv("_CL_");
    410     if (Opt_CL_.hasValue()) {
    411       SmallVector<const char *, 8> AppendedOpts;
    412       getCLEnvVarOptions(Opt_CL_.getValue(), Saver, AppendedOpts);
    413 
    414       // Insert at the end of the argument list to append.
    415       argv.append(AppendedOpts.begin(), AppendedOpts.end());
    416     }
    417   }
    418 
    419   std::set<std::string> SavedStrings;
    420   // Handle CCC_OVERRIDE_OPTIONS, used for editing a command line behind the
    421   // scenes.
    422   if (const char *OverrideStr = ::getenv("CCC_OVERRIDE_OPTIONS")) {
    423     // FIXME: Driver shouldn't take extra initial argument.
    424     ApplyQAOverride(argv, OverrideStr, SavedStrings);
    425   }
    426 
    427   std::string Path = GetExecutablePath(argv[0], CanonicalPrefixes);
    428 
    429   IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts =
    430       CreateAndPopulateDiagOpts(argv);
    431 
    432   TextDiagnosticPrinter *DiagClient
    433     = new TextDiagnosticPrinter(llvm::errs(), &*DiagOpts);
    434   FixupDiagPrefixExeName(DiagClient, Path);
    435 
    436   IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
    437 
    438   DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagClient);
    439 
    440   if (!DiagOpts->DiagnosticSerializationFile.empty()) {
    441     auto SerializedConsumer =
    442         clang::serialized_diags::create(DiagOpts->DiagnosticSerializationFile,
    443                                         &*DiagOpts, /*MergeChildRecords=*/true);
    444     Diags.setClient(new ChainedDiagnosticConsumer(
    445         Diags.takeClient(), std::move(SerializedConsumer)));
    446   }
    447 
    448   ProcessWarningOptions(Diags, *DiagOpts, /*ReportDiags=*/false);
    449 
    450   Driver TheDriver(Path, llvm::sys::getDefaultTargetTriple(), Diags);
    451   SetInstallDir(argv, TheDriver, CanonicalPrefixes);
    452   TheDriver.setTargetAndMode(TargetAndMode);
    453 
    454   insertTargetAndModeArgs(TargetAndMode, argv, SavedStrings);
    455 
    456   SetBackdoorDriverOutputsFromEnvVars(TheDriver);
    457 
    458   std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(argv));
    459   int Res = 1;
    460   if (C && !C->containsError()) {
    461     SmallVector<std::pair<int, const Command *>, 4> FailingCommands;
    462     Res = TheDriver.ExecuteCompilation(*C, FailingCommands);
    463 
    464     // Force a crash to test the diagnostics.
    465     if (TheDriver.GenReproducer) {
    466       Diags.Report(diag::err_drv_force_crash)
    467         << !::getenv("FORCE_CLANG_DIAGNOSTICS_CRASH");
    468 
    469       // Pretend that every command failed.
    470       FailingCommands.clear();
    471       for (const auto &J : C->getJobs())
    472         if (const Command *C = dyn_cast<Command>(&J))
    473           FailingCommands.push_back(std::make_pair(-1, C));
    474     }
    475 
    476     for (const auto &P : FailingCommands) {
    477       int CommandRes = P.first;
    478       const Command *FailingCommand = P.second;
    479       if (!Res)
    480         Res = CommandRes;
    481 
    482       // If result status is < 0, then the driver command signalled an error.
    483       // If result status is 70, then the driver command reported a fatal error.
    484       // On Windows, abort will return an exit code of 3.  In these cases,
    485       // generate additional diagnostic information if possible.
    486       bool DiagnoseCrash = CommandRes < 0 || CommandRes == 70;
    487 #ifdef _WIN32
    488       DiagnoseCrash |= CommandRes == 3;
    489 #endif
    490       if (DiagnoseCrash) {
    491         TheDriver.generateCompilationDiagnostics(*C, *FailingCommand);
    492         break;
    493       }
    494     }
    495   }
    496 
    497   Diags.getClient()->finish();
    498 
    499   // If any timers were active but haven't been destroyed yet, print their
    500   // results now.  This happens in -disable-free mode.
    501   llvm::TimerGroup::printAll(llvm::errs());
    502   llvm::TimerGroup::clearAll();
    503 
    504 #ifdef _WIN32
    505   // Exit status should not be negative on Win32, unless abnormal termination.
    506   // Once abnormal termiation was caught, negative status should not be
    507   // propagated.
    508   if (Res < 0)
    509     Res = 1;
    510 #endif
    511 
    512   // If we have multiple failing commands, we return the result of the first
    513   // failing command.
    514   return Res;
    515 }
    516