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