1 //===- Compilation.cpp - Compilation Task Implementation ------------------===//
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/Compilation.h"
11 #include "clang/Basic/LLVM.h"
12 #include "clang/Driver/Action.h"
13 #include "clang/Driver/Driver.h"
14 #include "clang/Driver/DriverDiagnostic.h"
15 #include "clang/Driver/Job.h"
16 #include "clang/Driver/Options.h"
17 #include "clang/Driver/ToolChain.h"
18 #include "clang/Driver/Util.h"
19 #include "llvm/ADT/None.h"
20 #include "llvm/ADT/STLExtras.h"
21 #include "llvm/ADT/SmallVector.h"
22 #include "llvm/ADT/Triple.h"
23 #include "llvm/Option/ArgList.h"
24 #include "llvm/Option/OptSpecifier.h"
25 #include "llvm/Option/Option.h"
26 #include "llvm/Support/FileSystem.h"
27 #include "llvm/Support/raw_ostream.h"
28 #include <cassert>
29 #include <string>
30 #include <system_error>
31 #include <utility>
32 
33 using namespace clang;
34 using namespace driver;
35 using namespace llvm::opt;
36 
37 Compilation::Compilation(const Driver &D, const ToolChain &_DefaultToolChain,
38                          InputArgList *_Args, DerivedArgList *_TranslatedArgs,
39                          bool ContainsError)
40     : TheDriver(D), DefaultToolChain(_DefaultToolChain), Args(_Args),
41       TranslatedArgs(_TranslatedArgs), ContainsError(ContainsError) {
42   // The offloading host toolchain is the default toolchain.
43   OrderedOffloadingToolchains.insert(
44       std::make_pair(Action::OFK_Host, &DefaultToolChain));
45 }
46 
47 Compilation::~Compilation() {
48   delete TranslatedArgs;
49   delete Args;
50 
51   // Free any derived arg lists.
52   for (auto Arg : TCArgs)
53     if (Arg.second != TranslatedArgs)
54       delete Arg.second;
55 }
56 
57 const DerivedArgList &
58 Compilation::getArgsForToolChain(const ToolChain *TC, StringRef BoundArch,
59                                  Action::OffloadKind DeviceOffloadKind) {
60   if (!TC)
61     TC = &DefaultToolChain;
62 
63   DerivedArgList *&Entry = TCArgs[{TC, BoundArch, DeviceOffloadKind}];
64   if (!Entry) {
65     SmallVector<Arg *, 4> AllocatedArgs;
66     DerivedArgList *OpenMPArgs = nullptr;
67     // Translate OpenMP toolchain arguments provided via the -Xopenmp-target flags.
68     if (DeviceOffloadKind == Action::OFK_OpenMP) {
69       const ToolChain *HostTC = getSingleOffloadToolChain<Action::OFK_Host>();
70       bool SameTripleAsHost = (TC->getTriple() == HostTC->getTriple());
71       OpenMPArgs = TC->TranslateOpenMPTargetArgs(
72           *TranslatedArgs, SameTripleAsHost, AllocatedArgs);
73     }
74 
75     if (!OpenMPArgs) {
76       Entry = TC->TranslateArgs(*TranslatedArgs, BoundArch, DeviceOffloadKind);
77       if (!Entry)
78         Entry = TranslatedArgs;
79     } else {
80       Entry = TC->TranslateArgs(*OpenMPArgs, BoundArch, DeviceOffloadKind);
81       if (!Entry)
82         Entry = OpenMPArgs;
83       else
84         delete OpenMPArgs;
85     }
86 
87     // Add allocated arguments to the final DAL.
88     for (auto ArgPtr : AllocatedArgs)
89       Entry->AddSynthesizedArg(ArgPtr);
90   }
91 
92   return *Entry;
93 }
94 
95 bool Compilation::CleanupFile(const char *File, bool IssueErrors) const {
96   // FIXME: Why are we trying to remove files that we have not created? For
97   // example we should only try to remove a temporary assembly file if
98   // "clang -cc1" succeed in writing it. Was this a workaround for when
99   // clang was writing directly to a .s file and sometimes leaving it behind
100   // during a failure?
101 
102   // FIXME: If this is necessary, we can still try to split
103   // llvm::sys::fs::remove into a removeFile and a removeDir and avoid the
104   // duplicated stat from is_regular_file.
105 
106   // Don't try to remove files which we don't have write access to (but may be
107   // able to remove), or non-regular files. Underlying tools may have
108   // intentionally not overwritten them.
109   if (!llvm::sys::fs::can_write(File) || !llvm::sys::fs::is_regular_file(File))
110     return true;
111 
112   if (std::error_code EC = llvm::sys::fs::remove(File)) {
113     // Failure is only failure if the file exists and is "regular". We checked
114     // for it being regular before, and llvm::sys::fs::remove ignores ENOENT,
115     // so we don't need to check again.
116 
117     if (IssueErrors)
118       getDriver().Diag(diag::err_drv_unable_to_remove_file)
119         << EC.message();
120     return false;
121   }
122   return true;
123 }
124 
125 bool Compilation::CleanupFileList(const ArgStringList &Files,
126                                   bool IssueErrors) const {
127   bool Success = true;
128   for (const auto &File: Files)
129     Success &= CleanupFile(File, IssueErrors);
130   return Success;
131 }
132 
133 bool Compilation::CleanupFileMap(const ArgStringMap &Files,
134                                  const JobAction *JA,
135                                  bool IssueErrors) const {
136   bool Success = true;
137   for (const auto &File : Files) {
138     // If specified, only delete the files associated with the JobAction.
139     // Otherwise, delete all files in the map.
140     if (JA && File.first != JA)
141       continue;
142     Success &= CleanupFile(File.second, IssueErrors);
143   }
144   return Success;
145 }
146 
147 int Compilation::ExecuteCommand(const Command &C,
148                                 const Command *&FailingCommand) const {
149   if ((getDriver().CCPrintOptions ||
150        getArgs().hasArg(options::OPT_v)) && !getDriver().CCGenDiagnostics) {
151     raw_ostream *OS = &llvm::errs();
152 
153     // Follow gcc implementation of CC_PRINT_OPTIONS; we could also cache the
154     // output stream.
155     if (getDriver().CCPrintOptions && getDriver().CCPrintOptionsFilename) {
156       std::error_code EC;
157       OS = new llvm::raw_fd_ostream(getDriver().CCPrintOptionsFilename, EC,
158                                     llvm::sys::fs::F_Append |
159                                         llvm::sys::fs::F_Text);
160       if (EC) {
161         getDriver().Diag(diag::err_drv_cc_print_options_failure)
162             << EC.message();
163         FailingCommand = &C;
164         delete OS;
165         return 1;
166       }
167     }
168 
169     if (getDriver().CCPrintOptions)
170       *OS << "[Logging clang options]";
171 
172     C.Print(*OS, "\n", /*Quote=*/getDriver().CCPrintOptions);
173 
174     if (OS != &llvm::errs())
175       delete OS;
176   }
177 
178   std::string Error;
179   bool ExecutionFailed;
180   int Res = C.Execute(Redirects, &Error, &ExecutionFailed);
181   if (!Error.empty()) {
182     assert(Res && "Error string set with 0 result code!");
183     getDriver().Diag(diag::err_drv_command_failure) << Error;
184   }
185 
186   if (Res)
187     FailingCommand = &C;
188 
189   return ExecutionFailed ? 1 : Res;
190 }
191 
192 using FailingCommandList = SmallVectorImpl<std::pair<int, const Command *>>;
193 
194 static bool ActionFailed(const Action *A,
195                          const FailingCommandList &FailingCommands) {
196   if (FailingCommands.empty())
197     return false;
198 
199   // CUDA can have the same input source code compiled multiple times so do not
200   // compiled again if there are already failures. It is OK to abort the CUDA
201   // pipeline on errors.
202   if (A->isOffloading(Action::OFK_Cuda))
203     return true;
204 
205   for (const auto &CI : FailingCommands)
206     if (A == &(CI.second->getSource()))
207       return true;
208 
209   for (const auto *AI : A->inputs())
210     if (ActionFailed(AI, FailingCommands))
211       return true;
212 
213   return false;
214 }
215 
216 static bool InputsOk(const Command &C,
217                      const FailingCommandList &FailingCommands) {
218   return !ActionFailed(&C.getSource(), FailingCommands);
219 }
220 
221 void Compilation::ExecuteJobs(const JobList &Jobs,
222                               FailingCommandList &FailingCommands) const {
223   // According to UNIX standard, driver need to continue compiling all the
224   // inputs on the command line even one of them failed.
225   // In all but CLMode, execute all the jobs unless the necessary inputs for the
226   // job is missing due to previous failures.
227   for (const auto &Job : Jobs) {
228     if (!InputsOk(Job, FailingCommands))
229       continue;
230     const Command *FailingCommand = nullptr;
231     if (int Res = ExecuteCommand(Job, FailingCommand)) {
232       FailingCommands.push_back(std::make_pair(Res, FailingCommand));
233       // Bail as soon as one command fails in cl driver mode.
234       if (TheDriver.IsCLMode())
235         return;
236     }
237   }
238 }
239 
240 void Compilation::initCompilationForDiagnostics() {
241   ForDiagnostics = true;
242 
243   // Free actions and jobs.
244   Actions.clear();
245   AllActions.clear();
246   Jobs.clear();
247 
248   // Clear temporary/results file lists.
249   TempFiles.clear();
250   ResultFiles.clear();
251   FailureResultFiles.clear();
252 
253   // Remove any user specified output.  Claim any unclaimed arguments, so as
254   // to avoid emitting warnings about unused args.
255   OptSpecifier OutputOpts[] = { options::OPT_o, options::OPT_MD,
256                                 options::OPT_MMD };
257   for (unsigned i = 0, e = llvm::array_lengthof(OutputOpts); i != e; ++i) {
258     if (TranslatedArgs->hasArg(OutputOpts[i]))
259       TranslatedArgs->eraseArg(OutputOpts[i]);
260   }
261   TranslatedArgs->ClaimAllArgs();
262 
263   // Redirect stdout/stderr to /dev/null.
264   Redirects = {None, {""}, {""}};
265 }
266 
267 StringRef Compilation::getSysRoot() const {
268   return getDriver().SysRoot;
269 }
270 
271 void Compilation::Redirect(ArrayRef<Optional<StringRef>> Redirects) {
272   this->Redirects = Redirects;
273 }
274