1 //===- Compilation.cpp - Compilation Task Implementation ------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "clang/Driver/Compilation.h"
10 #include "clang/Basic/LLVM.h"
11 #include "clang/Driver/Action.h"
12 #include "clang/Driver/Driver.h"
13 #include "clang/Driver/Job.h"
14 #include "clang/Driver/Options.h"
15 #include "clang/Driver/ToolChain.h"
16 #include "clang/Driver/Util.h"
17 #include "llvm/Option/ArgList.h"
18 #include "llvm/Option/OptSpecifier.h"
19 #include "llvm/Option/Option.h"
20 #include "llvm/Support/FileSystem.h"
21 #include "llvm/Support/raw_ostream.h"
22 #include "llvm/TargetParser/Triple.h"
23 #include <cassert>
24 #include <string>
25 #include <system_error>
26 #include <utility>
27
28 using namespace clang;
29 using namespace driver;
30 using namespace llvm::opt;
31
Compilation(const Driver & D,const ToolChain & _DefaultToolChain,InputArgList * _Args,DerivedArgList * _TranslatedArgs,bool ContainsError)32 Compilation::Compilation(const Driver &D, const ToolChain &_DefaultToolChain,
33 InputArgList *_Args, DerivedArgList *_TranslatedArgs,
34 bool ContainsError)
35 : TheDriver(D), DefaultToolChain(_DefaultToolChain), Args(_Args),
36 TranslatedArgs(_TranslatedArgs), ContainsError(ContainsError) {
37 // The offloading host toolchain is the default toolchain.
38 OrderedOffloadingToolchains.insert(
39 std::make_pair(Action::OFK_Host, &DefaultToolChain));
40 }
41
~Compilation()42 Compilation::~Compilation() {
43 // Remove temporary files. This must be done before arguments are freed, as
44 // the file names might be derived from the input arguments.
45 if (!TheDriver.isSaveTempsEnabled() && !ForceKeepTempFiles)
46 CleanupFileList(TempFiles);
47
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 &
getArgsForToolChain(const ToolChain * TC,StringRef BoundArch,Action::OffloadKind DeviceOffloadKind)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 DerivedArgList *NewDAL = nullptr;
76 if (!OpenMPArgs) {
77 NewDAL = TC->TranslateXarchArgs(*TranslatedArgs, BoundArch,
78 DeviceOffloadKind, &AllocatedArgs);
79 } else {
80 NewDAL = TC->TranslateXarchArgs(*OpenMPArgs, BoundArch, DeviceOffloadKind,
81 &AllocatedArgs);
82 if (!NewDAL)
83 NewDAL = OpenMPArgs;
84 else
85 delete OpenMPArgs;
86 }
87
88 if (!NewDAL) {
89 Entry = TC->TranslateArgs(*TranslatedArgs, BoundArch, DeviceOffloadKind);
90 if (!Entry)
91 Entry = TranslatedArgs;
92 } else {
93 Entry = TC->TranslateArgs(*NewDAL, BoundArch, DeviceOffloadKind);
94 if (!Entry)
95 Entry = NewDAL;
96 else
97 delete NewDAL;
98 }
99
100 // Add allocated arguments to the final DAL.
101 for (auto *ArgPtr : AllocatedArgs)
102 Entry->AddSynthesizedArg(ArgPtr);
103 }
104
105 return *Entry;
106 }
107
CleanupFile(const char * File,bool IssueErrors) const108 bool Compilation::CleanupFile(const char *File, bool IssueErrors) const {
109 // FIXME: Why are we trying to remove files that we have not created? For
110 // example we should only try to remove a temporary assembly file if
111 // "clang -cc1" succeed in writing it. Was this a workaround for when
112 // clang was writing directly to a .s file and sometimes leaving it behind
113 // during a failure?
114
115 // FIXME: If this is necessary, we can still try to split
116 // llvm::sys::fs::remove into a removeFile and a removeDir and avoid the
117 // duplicated stat from is_regular_file.
118
119 // Don't try to remove files which we don't have write access to (but may be
120 // able to remove), or non-regular files. Underlying tools may have
121 // intentionally not overwritten them.
122 if (!llvm::sys::fs::can_write(File) || !llvm::sys::fs::is_regular_file(File))
123 return true;
124
125 if (std::error_code EC = llvm::sys::fs::remove(File)) {
126 // Failure is only failure if the file exists and is "regular". We checked
127 // for it being regular before, and llvm::sys::fs::remove ignores ENOENT,
128 // so we don't need to check again.
129
130 if (IssueErrors)
131 getDriver().Diag(diag::err_drv_unable_to_remove_file)
132 << EC.message();
133 return false;
134 }
135 return true;
136 }
137
CleanupFileList(const llvm::opt::ArgStringList & Files,bool IssueErrors) const138 bool Compilation::CleanupFileList(const llvm::opt::ArgStringList &Files,
139 bool IssueErrors) const {
140 bool Success = true;
141 for (const auto &File: Files)
142 Success &= CleanupFile(File, IssueErrors);
143 return Success;
144 }
145
CleanupFileMap(const ArgStringMap & Files,const JobAction * JA,bool IssueErrors) const146 bool Compilation::CleanupFileMap(const ArgStringMap &Files,
147 const JobAction *JA,
148 bool IssueErrors) const {
149 bool Success = true;
150 for (const auto &File : Files) {
151 // If specified, only delete the files associated with the JobAction.
152 // Otherwise, delete all files in the map.
153 if (JA && File.first != JA)
154 continue;
155 Success &= CleanupFile(File.second, IssueErrors);
156 }
157 return Success;
158 }
159
ExecuteCommand(const Command & C,const Command * & FailingCommand,bool LogOnly) const160 int Compilation::ExecuteCommand(const Command &C,
161 const Command *&FailingCommand,
162 bool LogOnly) const {
163 if ((getDriver().CCPrintOptions ||
164 getArgs().hasArg(options::OPT_v)) && !getDriver().CCGenDiagnostics) {
165 raw_ostream *OS = &llvm::errs();
166 std::unique_ptr<llvm::raw_fd_ostream> OwnedStream;
167
168 // Follow gcc implementation of CC_PRINT_OPTIONS; we could also cache the
169 // output stream.
170 if (getDriver().CCPrintOptions &&
171 !getDriver().CCPrintOptionsFilename.empty()) {
172 std::error_code EC;
173 OwnedStream.reset(new llvm::raw_fd_ostream(
174 getDriver().CCPrintOptionsFilename, EC,
175 llvm::sys::fs::OF_Append | llvm::sys::fs::OF_TextWithCRLF));
176 if (EC) {
177 getDriver().Diag(diag::err_drv_cc_print_options_failure)
178 << EC.message();
179 FailingCommand = &C;
180 return 1;
181 }
182 OS = OwnedStream.get();
183 }
184
185 if (getDriver().CCPrintOptions)
186 *OS << "[Logging clang options]\n";
187
188 C.Print(*OS, "\n", /*Quote=*/getDriver().CCPrintOptions);
189 }
190
191 if (LogOnly)
192 return 0;
193
194 std::string Error;
195 bool ExecutionFailed;
196 int Res = C.Execute(Redirects, &Error, &ExecutionFailed);
197 if (PostCallback)
198 PostCallback(C, Res);
199 if (!Error.empty()) {
200 assert(Res && "Error string set with 0 result code!");
201 getDriver().Diag(diag::err_drv_command_failure) << Error;
202 }
203
204 if (Res)
205 FailingCommand = &C;
206
207 return ExecutionFailed ? 1 : Res;
208 }
209
210 using FailingCommandList = SmallVectorImpl<std::pair<int, const Command *>>;
211
ActionFailed(const Action * A,const FailingCommandList & FailingCommands)212 static bool ActionFailed(const Action *A,
213 const FailingCommandList &FailingCommands) {
214 if (FailingCommands.empty())
215 return false;
216
217 // CUDA/HIP/SYCL can have the same input source code compiled multiple times
218 // so do not compile again if there are already failures. It is OK to abort
219 // the CUDA/HIP/SYCL pipeline on errors.
220 if (A->isOffloading(Action::OFK_Cuda) || A->isOffloading(Action::OFK_HIP) ||
221 A->isOffloading(Action::OFK_SYCL))
222 return true;
223
224 for (const auto &CI : FailingCommands)
225 if (A == &(CI.second->getSource()))
226 return true;
227
228 for (const auto *AI : A->inputs())
229 if (ActionFailed(AI, FailingCommands))
230 return true;
231
232 return false;
233 }
234
InputsOk(const Command & C,const FailingCommandList & FailingCommands)235 static bool InputsOk(const Command &C,
236 const FailingCommandList &FailingCommands) {
237 return !ActionFailed(&C.getSource(), FailingCommands);
238 }
239
ExecuteJobs(const JobList & Jobs,FailingCommandList & FailingCommands,bool LogOnly) const240 void Compilation::ExecuteJobs(const JobList &Jobs,
241 FailingCommandList &FailingCommands,
242 bool LogOnly) const {
243 // According to UNIX standard, driver need to continue compiling all the
244 // inputs on the command line even one of them failed.
245 // In all but CLMode, execute all the jobs unless the necessary inputs for the
246 // job is missing due to previous failures.
247 for (const auto &Job : Jobs) {
248 if (!InputsOk(Job, FailingCommands))
249 continue;
250 const Command *FailingCommand = nullptr;
251 if (int Res = ExecuteCommand(Job, FailingCommand, LogOnly)) {
252 FailingCommands.push_back(std::make_pair(Res, FailingCommand));
253 // Bail as soon as one command fails in cl driver mode.
254 if (TheDriver.IsCLMode())
255 return;
256 }
257 }
258 }
259
initCompilationForDiagnostics()260 void Compilation::initCompilationForDiagnostics() {
261 ForDiagnostics = true;
262
263 // Free actions and jobs.
264 Actions.clear();
265 AllActions.clear();
266 Jobs.clear();
267
268 // Remove temporary files.
269 if (!TheDriver.isSaveTempsEnabled() && !ForceKeepTempFiles)
270 CleanupFileList(TempFiles);
271
272 // Clear temporary/results file lists.
273 TempFiles.clear();
274 ResultFiles.clear();
275 FailureResultFiles.clear();
276
277 // Remove any user specified output. Claim any unclaimed arguments, so as
278 // to avoid emitting warnings about unused args.
279 OptSpecifier OutputOpts[] = {
280 options::OPT_o, options::OPT_MD, options::OPT_MMD, options::OPT_M,
281 options::OPT_MM, options::OPT_MF, options::OPT_MG, options::OPT_MJ,
282 options::OPT_MQ, options::OPT_MT, options::OPT_MV};
283 for (const auto &Opt : OutputOpts) {
284 if (TranslatedArgs->hasArg(Opt))
285 TranslatedArgs->eraseArg(Opt);
286 }
287 TranslatedArgs->ClaimAllArgs();
288
289 // Force re-creation of the toolchain Args, otherwise our modifications just
290 // above will have no effect.
291 for (auto Arg : TCArgs)
292 if (Arg.second != TranslatedArgs)
293 delete Arg.second;
294 TCArgs.clear();
295
296 // Redirect stdout/stderr to /dev/null.
297 Redirects = {std::nullopt, {""}, {""}};
298
299 // Temporary files added by diagnostics should be kept.
300 ForceKeepTempFiles = true;
301 }
302
getSysRoot() const303 StringRef Compilation::getSysRoot() const {
304 return getDriver().SysRoot;
305 }
306
Redirect(ArrayRef<std::optional<StringRef>> Redirects)307 void Compilation::Redirect(ArrayRef<std::optional<StringRef>> Redirects) {
308 this->Redirects = Redirects;
309 }
310