1 //===- Compilation.cpp - Compilation Task Implementation ------------------===//
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
7 //===----------------------------------------------------------------------===//
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/DriverDiagnostic.h"
14 #include "clang/Driver/Job.h"
15 #include "clang/Driver/Options.h"
16 #include "clang/Driver/ToolChain.h"
17 #include "clang/Driver/Util.h"
18 #include "llvm/ADT/STLExtras.h"
19 #include "llvm/ADT/SmallVector.h"
20 #include "llvm/Option/ArgList.h"
21 #include "llvm/Option/OptSpecifier.h"
22 #include "llvm/Option/Option.h"
23 #include "llvm/Support/FileSystem.h"
24 #include "llvm/Support/raw_ostream.h"
25 #include "llvm/TargetParser/Triple.h"
28 #include <system_error>
31 using namespace clang
;
32 using namespace driver
;
33 using namespace llvm::opt
;
35 Compilation::Compilation(const Driver
&D
, const ToolChain
&_DefaultToolChain
,
36 InputArgList
*_Args
, DerivedArgList
*_TranslatedArgs
,
38 : TheDriver(D
), DefaultToolChain(_DefaultToolChain
), Args(_Args
),
39 TranslatedArgs(_TranslatedArgs
), ContainsError(ContainsError
) {
40 // The offloading host toolchain is the default toolchain.
41 OrderedOffloadingToolchains
.insert(
42 std::make_pair(Action::OFK_Host
, &DefaultToolChain
));
45 Compilation::~Compilation() {
46 // Remove temporary files. This must be done before arguments are freed, as
47 // the file names might be derived from the input arguments.
48 if (!TheDriver
.isSaveTempsEnabled() && !ForceKeepTempFiles
)
49 CleanupFileList(TempFiles
);
51 delete TranslatedArgs
;
54 // Free any derived arg lists.
55 for (auto Arg
: TCArgs
)
56 if (Arg
.second
!= TranslatedArgs
)
60 const DerivedArgList
&
61 Compilation::getArgsForToolChain(const ToolChain
*TC
, StringRef BoundArch
,
62 Action::OffloadKind DeviceOffloadKind
) {
64 TC
= &DefaultToolChain
;
66 DerivedArgList
*&Entry
= TCArgs
[{TC
, BoundArch
, DeviceOffloadKind
}];
68 SmallVector
<Arg
*, 4> AllocatedArgs
;
69 DerivedArgList
*OpenMPArgs
= nullptr;
70 // Translate OpenMP toolchain arguments provided via the -Xopenmp-target flags.
71 if (DeviceOffloadKind
== Action::OFK_OpenMP
) {
72 const ToolChain
*HostTC
= getSingleOffloadToolChain
<Action::OFK_Host
>();
73 bool SameTripleAsHost
= (TC
->getTriple() == HostTC
->getTriple());
74 OpenMPArgs
= TC
->TranslateOpenMPTargetArgs(
75 *TranslatedArgs
, SameTripleAsHost
, AllocatedArgs
);
78 DerivedArgList
*NewDAL
= nullptr;
80 NewDAL
= TC
->TranslateXarchArgs(*TranslatedArgs
, BoundArch
,
81 DeviceOffloadKind
, &AllocatedArgs
);
83 NewDAL
= TC
->TranslateXarchArgs(*OpenMPArgs
, BoundArch
, DeviceOffloadKind
,
92 Entry
= TC
->TranslateArgs(*TranslatedArgs
, BoundArch
, DeviceOffloadKind
);
94 Entry
= TranslatedArgs
;
96 Entry
= TC
->TranslateArgs(*NewDAL
, BoundArch
, DeviceOffloadKind
);
103 // Add allocated arguments to the final DAL.
104 for (auto *ArgPtr
: AllocatedArgs
)
105 Entry
->AddSynthesizedArg(ArgPtr
);
111 bool Compilation::CleanupFile(const char *File
, bool IssueErrors
) const {
112 // FIXME: Why are we trying to remove files that we have not created? For
113 // example we should only try to remove a temporary assembly file if
114 // "clang -cc1" succeed in writing it. Was this a workaround for when
115 // clang was writing directly to a .s file and sometimes leaving it behind
118 // FIXME: If this is necessary, we can still try to split
119 // llvm::sys::fs::remove into a removeFile and a removeDir and avoid the
120 // duplicated stat from is_regular_file.
122 // Don't try to remove files which we don't have write access to (but may be
123 // able to remove), or non-regular files. Underlying tools may have
124 // intentionally not overwritten them.
125 if (!llvm::sys::fs::can_write(File
) || !llvm::sys::fs::is_regular_file(File
))
128 if (std::error_code EC
= llvm::sys::fs::remove(File
)) {
129 // Failure is only failure if the file exists and is "regular". We checked
130 // for it being regular before, and llvm::sys::fs::remove ignores ENOENT,
131 // so we don't need to check again.
134 getDriver().Diag(diag::err_drv_unable_to_remove_file
)
141 bool Compilation::CleanupFileList(const llvm::opt::ArgStringList
&Files
,
142 bool IssueErrors
) const {
144 for (const auto &File
: Files
)
145 Success
&= CleanupFile(File
, IssueErrors
);
149 bool Compilation::CleanupFileMap(const ArgStringMap
&Files
,
151 bool IssueErrors
) const {
153 for (const auto &File
: Files
) {
154 // If specified, only delete the files associated with the JobAction.
155 // Otherwise, delete all files in the map.
156 if (JA
&& File
.first
!= JA
)
158 Success
&= CleanupFile(File
.second
, IssueErrors
);
163 int Compilation::ExecuteCommand(const Command
&C
,
164 const Command
*&FailingCommand
,
165 bool LogOnly
) const {
166 if ((getDriver().CCPrintOptions
||
167 getArgs().hasArg(options::OPT_v
)) && !getDriver().CCGenDiagnostics
) {
168 raw_ostream
*OS
= &llvm::errs();
169 std::unique_ptr
<llvm::raw_fd_ostream
> OwnedStream
;
171 // Follow gcc implementation of CC_PRINT_OPTIONS; we could also cache the
173 if (getDriver().CCPrintOptions
&&
174 !getDriver().CCPrintOptionsFilename
.empty()) {
176 OwnedStream
.reset(new llvm::raw_fd_ostream(
177 getDriver().CCPrintOptionsFilename
, EC
,
178 llvm::sys::fs::OF_Append
| llvm::sys::fs::OF_TextWithCRLF
));
180 getDriver().Diag(diag::err_drv_cc_print_options_failure
)
185 OS
= OwnedStream
.get();
188 if (getDriver().CCPrintOptions
)
189 *OS
<< "[Logging clang options]\n";
191 C
.Print(*OS
, "\n", /*Quote=*/getDriver().CCPrintOptions
);
198 bool ExecutionFailed
;
199 int Res
= C
.Execute(Redirects
, &Error
, &ExecutionFailed
);
201 PostCallback(C
, Res
);
202 if (!Error
.empty()) {
203 assert(Res
&& "Error string set with 0 result code!");
204 getDriver().Diag(diag::err_drv_command_failure
) << Error
;
210 return ExecutionFailed
? 1 : Res
;
213 using FailingCommandList
= SmallVectorImpl
<std::pair
<int, const Command
*>>;
215 static bool ActionFailed(const Action
*A
,
216 const FailingCommandList
&FailingCommands
) {
217 if (FailingCommands
.empty())
220 // CUDA/HIP can have the same input source code compiled multiple times so do
221 // not compiled again if there are already failures. It is OK to abort the
222 // CUDA pipeline on errors.
223 if (A
->isOffloading(Action::OFK_Cuda
) || A
->isOffloading(Action::OFK_HIP
))
226 for (const auto &CI
: FailingCommands
)
227 if (A
== &(CI
.second
->getSource()))
230 for (const auto *AI
: A
->inputs())
231 if (ActionFailed(AI
, FailingCommands
))
237 static bool InputsOk(const Command
&C
,
238 const FailingCommandList
&FailingCommands
) {
239 return !ActionFailed(&C
.getSource(), FailingCommands
);
242 void Compilation::ExecuteJobs(const JobList
&Jobs
,
243 FailingCommandList
&FailingCommands
,
244 bool LogOnly
) const {
245 // According to UNIX standard, driver need to continue compiling all the
246 // inputs on the command line even one of them failed.
247 // In all but CLMode, execute all the jobs unless the necessary inputs for the
248 // job is missing due to previous failures.
249 for (const auto &Job
: Jobs
) {
250 if (!InputsOk(Job
, FailingCommands
))
252 const Command
*FailingCommand
= nullptr;
253 if (int Res
= ExecuteCommand(Job
, FailingCommand
, LogOnly
)) {
254 FailingCommands
.push_back(std::make_pair(Res
, FailingCommand
));
255 // Bail as soon as one command fails in cl driver mode.
256 if (TheDriver
.IsCLMode())
262 void Compilation::initCompilationForDiagnostics() {
263 ForDiagnostics
= true;
265 // Free actions and jobs.
270 // Remove temporary files.
271 if (!TheDriver
.isSaveTempsEnabled() && !ForceKeepTempFiles
)
272 CleanupFileList(TempFiles
);
274 // Clear temporary/results file lists.
277 FailureResultFiles
.clear();
279 // Remove any user specified output. Claim any unclaimed arguments, so as
280 // to avoid emitting warnings about unused args.
281 OptSpecifier OutputOpts
[] = {
282 options::OPT_o
, options::OPT_MD
, options::OPT_MMD
, options::OPT_M
,
283 options::OPT_MM
, options::OPT_MF
, options::OPT_MG
, options::OPT_MJ
,
284 options::OPT_MQ
, options::OPT_MT
, options::OPT_MV
};
285 for (const auto &Opt
: OutputOpts
) {
286 if (TranslatedArgs
->hasArg(Opt
))
287 TranslatedArgs
->eraseArg(Opt
);
289 TranslatedArgs
->ClaimAllArgs();
291 // Force re-creation of the toolchain Args, otherwise our modifications just
292 // above will have no effect.
293 for (auto Arg
: TCArgs
)
294 if (Arg
.second
!= TranslatedArgs
)
298 // Redirect stdout/stderr to /dev/null.
299 Redirects
= {std::nullopt
, {""}, {""}};
301 // Temporary files added by diagnostics should be kept.
302 ForceKeepTempFiles
= true;
305 StringRef
Compilation::getSysRoot() const {
306 return getDriver().SysRoot
;
309 void Compilation::Redirect(ArrayRef
<std::optional
<StringRef
>> Redirects
) {
310 this->Redirects
= Redirects
;