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