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>
24 using namespace clang::driver;
25 using llvm::raw_ostream;
26 using llvm::StringRef;
27 using llvm::ArrayRef;
28 
Command(const Action & Source,const Tool & Creator,const char * Executable,const ArgStringList & Arguments,ArrayRef<InputInfo> Inputs)29 Command::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 
skipArgs(const char * Flag,bool HaveCrashVFS)39 static 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 
printArg(raw_ostream & OS,const char * Arg,bool Quote)81 void 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 
writeResponseFile(raw_ostream & OS) const99 void 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 
buildArgvForResponseFile(llvm::SmallVectorImpl<const char * > & Out) const125 void 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 
Print(raw_ostream & OS,const char * Terminator,bool Quote,CrashReportInfo * CrashInfo) const154 void 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 
setResponseFile(const char * FileName)212 void Command::setResponseFile(const char *FileName) {
213   ResponseFile = FileName;
214   ResponseFileFlag = Creator.getResponseFileFlag();
215   ResponseFileFlag += FileName;
216 }
217 
Execute(const StringRef ** Redirects,std::string * ErrMsg,bool * ExecutionFailed) const218 int 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 
FallbackCommand(const Action & Source_,const Tool & Creator_,const char * Executable_,const ArgStringList & Arguments_,ArrayRef<InputInfo> Inputs,std::unique_ptr<Command> Fallback_)259 FallbackCommand::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 
Print(raw_ostream & OS,const char * Terminator,bool Quote,CrashReportInfo * CrashInfo) const267 void 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 
ShouldFallback(int ExitCode)274 static 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 
Execute(const StringRef ** Redirects,std::string * ErrMsg,bool * ExecutionFailed) const281 int 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 
Print(raw_ostream & OS,const char * Terminator,bool Quote,CrashReportInfo * CrashInfo) const300 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 
clear()306 void JobList::clear() { Jobs.clear(); }
307