Home | History | Annotate | Download | only in Driver
      1 //===--- Job.cpp - Command to Execute -------------------------------------===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 
     10 #include "clang/Driver/Driver.h"
     11 #include "clang/Driver/DriverDiagnostic.h"
     12 #include "clang/Driver/Job.h"
     13 #include "clang/Driver/Tool.h"
     14 #include "clang/Driver/ToolChain.h"
     15 #include "llvm/ADT/ArrayRef.h"
     16 #include "llvm/ADT/STLExtras.h"
     17 #include "llvm/ADT/StringRef.h"
     18 #include "llvm/ADT/StringSet.h"
     19 #include "llvm/ADT/StringSwitch.h"
     20 #include "llvm/Support/Program.h"
     21 #include "llvm/Support/raw_ostream.h"
     22 #include <cassert>
     23 using namespace clang::driver;
     24 using llvm::raw_ostream;
     25 using llvm::StringRef;
     26 using llvm::ArrayRef;
     27 
     28 Job::~Job() {}
     29 
     30 Command::Command(const Action &_Source, const Tool &_Creator,
     31                  const char *_Executable,
     32                  const ArgStringList &_Arguments)
     33     : Job(CommandClass), Source(_Source), Creator(_Creator),
     34       Executable(_Executable), Arguments(_Arguments),
     35       ResponseFile(nullptr) {}
     36 
     37 static int skipArgs(const char *Flag, bool HaveCrashVFS) {
     38   // These flags are all of the form -Flag <Arg> and are treated as two
     39   // arguments.  Therefore, we need to skip the flag and the next argument.
     40   bool Res = llvm::StringSwitch<bool>(Flag)
     41     .Cases("-I", "-MF", "-MT", "-MQ", true)
     42     .Cases("-o", "-coverage-file", "-dependency-file", true)
     43     .Cases("-fdebug-compilation-dir", "-idirafter", true)
     44     .Cases("-include", "-include-pch", "-internal-isystem", true)
     45     .Cases("-internal-externc-isystem", "-iprefix", "-iwithprefix", true)
     46     .Cases("-iwithprefixbefore", "-isystem", "-iquote", true)
     47     .Cases("-resource-dir", "-serialize-diagnostic-file", true)
     48     .Cases("-dwarf-debug-flags", "-ivfsoverlay", true)
     49     // Some include flags shouldn't be skipped if we have a crash VFS
     50     .Case("-isysroot", !HaveCrashVFS)
     51     .Default(false);
     52 
     53   // Match found.
     54   if (Res)
     55     return 2;
     56 
     57   // The remaining flags are treated as a single argument.
     58 
     59   // These flags are all of the form -Flag and have no second argument.
     60   Res = llvm::StringSwitch<bool>(Flag)
     61     .Cases("-M", "-MM", "-MG", "-MP", "-MD", true)
     62     .Case("-MMD", true)
     63     .Default(false);
     64 
     65   // Match found.
     66   if (Res)
     67     return 1;
     68 
     69   // These flags are treated as a single argument (e.g., -F<Dir>).
     70   StringRef FlagRef(Flag);
     71   if (FlagRef.startswith("-F") || FlagRef.startswith("-I") ||
     72       FlagRef.startswith("-fmodules-cache-path="))
     73     return 1;
     74 
     75   return 0;
     76 }
     77 
     78 static void PrintArg(raw_ostream &OS, const char *Arg, bool Quote) {
     79   const bool Escape = std::strpbrk(Arg, "\"\\$");
     80 
     81   if (!Quote && !Escape) {
     82     OS << Arg;
     83     return;
     84   }
     85 
     86   // Quote and escape. This isn't really complete, but good enough.
     87   OS << '"';
     88   while (const char c = *Arg++) {
     89     if (c == '"' || c == '\\' || c == '$')
     90       OS << '\\';
     91     OS << c;
     92   }
     93   OS << '"';
     94 }
     95 
     96 void Command::writeResponseFile(raw_ostream &OS) const {
     97   // In a file list, we only write the set of inputs to the response file
     98   if (Creator.getResponseFilesSupport() == Tool::RF_FileList) {
     99     for (const char *Arg : InputFileList) {
    100       OS << Arg << '\n';
    101     }
    102     return;
    103   }
    104 
    105   // In regular response files, we send all arguments to the response file
    106   for (const char *Arg : Arguments) {
    107     OS << '"';
    108 
    109     for (; *Arg != '\0'; Arg++) {
    110       if (*Arg == '\"' || *Arg == '\\') {
    111         OS << '\\';
    112       }
    113       OS << *Arg;
    114     }
    115 
    116     OS << "\" ";
    117   }
    118 }
    119 
    120 void Command::buildArgvForResponseFile(
    121     llvm::SmallVectorImpl<const char *> &Out) const {
    122   // When not a file list, all arguments are sent to the response file.
    123   // This leaves us to set the argv to a single parameter, requesting the tool
    124   // to read the response file.
    125   if (Creator.getResponseFilesSupport() != Tool::RF_FileList) {
    126     Out.push_back(Executable);
    127     Out.push_back(ResponseFileFlag.c_str());
    128     return;
    129   }
    130 
    131   llvm::StringSet<> Inputs;
    132   for (const char *InputName : InputFileList)
    133     Inputs.insert(InputName);
    134   Out.push_back(Executable);
    135   // In a file list, build args vector ignoring parameters that will go in the
    136   // response file (elements of the InputFileList vector)
    137   bool FirstInput = true;
    138   for (const char *Arg : Arguments) {
    139     if (Inputs.count(Arg) == 0) {
    140       Out.push_back(Arg);
    141     } else if (FirstInput) {
    142       FirstInput = false;
    143       Out.push_back(Creator.getResponseFileFlag());
    144       Out.push_back(ResponseFile);
    145     }
    146   }
    147 }
    148 
    149 void Command::Print(raw_ostream &OS, const char *Terminator, bool Quote,
    150                     CrashReportInfo *CrashInfo) const {
    151   // Always quote the exe.
    152   OS << ' ';
    153   PrintArg(OS, Executable, /*Quote=*/true);
    154 
    155   llvm::ArrayRef<const char *> Args = Arguments;
    156   llvm::SmallVector<const char *, 128> ArgsRespFile;
    157   if (ResponseFile != nullptr) {
    158     buildArgvForResponseFile(ArgsRespFile);
    159     Args = ArrayRef<const char *>(ArgsRespFile).slice(1); // no executable name
    160   }
    161 
    162   StringRef MainFilename;
    163   // We'll need the argument to -main-file-name to find the input file name.
    164   if (CrashInfo)
    165     for (size_t I = 0, E = Args.size(); I + 1 < E; ++I)
    166       if (StringRef(Args[I]).equals("-main-file-name"))
    167         MainFilename = Args[I + 1];
    168 
    169   bool HaveCrashVFS = CrashInfo && !CrashInfo->VFSPath.empty();
    170   for (size_t i = 0, e = Args.size(); i < e; ++i) {
    171     const char *const Arg = Args[i];
    172 
    173     if (CrashInfo) {
    174       if (int Skip = skipArgs(Arg, HaveCrashVFS)) {
    175         i += Skip - 1;
    176         continue;
    177       } else if (llvm::sys::path::filename(Arg) == MainFilename &&
    178                  (i == 0 || StringRef(Args[i - 1]) != "-main-file-name")) {
    179         // Replace the input file name with the crashinfo's file name.
    180         OS << ' ';
    181         StringRef ShortName = llvm::sys::path::filename(CrashInfo->Filename);
    182         PrintArg(OS, ShortName.str().c_str(), Quote);
    183         continue;
    184       }
    185     }
    186 
    187     OS << ' ';
    188     PrintArg(OS, Arg, Quote);
    189   }
    190 
    191   if (CrashInfo && HaveCrashVFS) {
    192     OS << ' ';
    193     PrintArg(OS, "-ivfsoverlay", Quote);
    194     OS << ' ';
    195     PrintArg(OS, CrashInfo->VFSPath.str().c_str(), Quote);
    196   }
    197 
    198   if (ResponseFile != nullptr) {
    199     OS << "\n Arguments passed via response file:\n";
    200     writeResponseFile(OS);
    201     // Avoiding duplicated newline terminator, since FileLists are
    202     // newline-separated.
    203     if (Creator.getResponseFilesSupport() != Tool::RF_FileList)
    204       OS << "\n";
    205     OS << " (end of response file)";
    206   }
    207 
    208   OS << Terminator;
    209 }
    210 
    211 void Command::setResponseFile(const char *FileName) {
    212   ResponseFile = FileName;
    213   ResponseFileFlag = Creator.getResponseFileFlag();
    214   ResponseFileFlag += FileName;
    215 }
    216 
    217 int Command::Execute(const StringRef **Redirects, std::string *ErrMsg,
    218                      bool *ExecutionFailed) const {
    219   SmallVector<const char*, 128> Argv;
    220 
    221   if (ResponseFile == nullptr) {
    222     Argv.push_back(Executable);
    223     Argv.append(Arguments.begin(), Arguments.end());
    224     Argv.push_back(nullptr);
    225 
    226     return llvm::sys::ExecuteAndWait(Executable, Argv.data(), /*env*/ nullptr,
    227                                      Redirects, /*secondsToWait*/ 0,
    228                                      /*memoryLimit*/ 0, ErrMsg,
    229                                      ExecutionFailed);
    230   }
    231 
    232   // We need to put arguments in a response file (command is too large)
    233   // Open stream to store the response file contents
    234   std::string RespContents;
    235   llvm::raw_string_ostream SS(RespContents);
    236 
    237   // Write file contents and build the Argv vector
    238   writeResponseFile(SS);
    239   buildArgvForResponseFile(Argv);
    240   Argv.push_back(nullptr);
    241   SS.flush();
    242 
    243   // Save the response file in the appropriate encoding
    244   if (std::error_code EC = writeFileWithEncoding(
    245           ResponseFile, RespContents, Creator.getResponseFileEncoding())) {
    246     if (ErrMsg)
    247       *ErrMsg = EC.message();
    248     if (ExecutionFailed)
    249       *ExecutionFailed = true;
    250     return -1;
    251   }
    252 
    253   return llvm::sys::ExecuteAndWait(Executable, Argv.data(), /*env*/ nullptr,
    254                                    Redirects, /*secondsToWait*/ 0,
    255                                    /*memoryLimit*/ 0, ErrMsg, ExecutionFailed);
    256 }
    257 
    258 FallbackCommand::FallbackCommand(const Action &Source_, const Tool &Creator_,
    259                                  const char *Executable_,
    260                                  const ArgStringList &Arguments_,
    261                                  std::unique_ptr<Command> Fallback_)
    262     : Command(Source_, Creator_, Executable_, Arguments_),
    263       Fallback(std::move(Fallback_)) {}
    264 
    265 void FallbackCommand::Print(raw_ostream &OS, const char *Terminator,
    266                             bool Quote, CrashReportInfo *CrashInfo) const {
    267   Command::Print(OS, "", Quote, CrashInfo);
    268   OS << " ||";
    269   Fallback->Print(OS, Terminator, Quote, CrashInfo);
    270 }
    271 
    272 static bool ShouldFallback(int ExitCode) {
    273   // FIXME: We really just want to fall back for internal errors, such
    274   // as when some symbol cannot be mangled, when we should be able to
    275   // parse something but can't, etc.
    276   return ExitCode != 0;
    277 }
    278 
    279 int FallbackCommand::Execute(const StringRef **Redirects, std::string *ErrMsg,
    280                              bool *ExecutionFailed) const {
    281   int PrimaryStatus = Command::Execute(Redirects, ErrMsg, ExecutionFailed);
    282   if (!ShouldFallback(PrimaryStatus))
    283     return PrimaryStatus;
    284 
    285   // Clear ExecutionFailed and ErrMsg before falling back.
    286   if (ErrMsg)
    287     ErrMsg->clear();
    288   if (ExecutionFailed)
    289     *ExecutionFailed = false;
    290 
    291   const Driver &D = getCreator().getToolChain().getDriver();
    292   D.Diag(diag::warn_drv_invoking_fallback) << Fallback->getExecutable();
    293 
    294   int SecondaryStatus = Fallback->Execute(Redirects, ErrMsg, ExecutionFailed);
    295   return SecondaryStatus;
    296 }
    297 
    298 JobList::JobList() : Job(JobListClass) {}
    299 
    300 void JobList::Print(raw_ostream &OS, const char *Terminator, bool Quote,
    301                     CrashReportInfo *CrashInfo) const {
    302   for (const auto &Job : *this)
    303     Job.Print(OS, Terminator, Quote, CrashInfo);
    304 }
    305 
    306 void JobList::clear() { Jobs.clear(); }
    307