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