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