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