305 lines · cpp
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-exception6//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/ToolChain.h"15#include "clang/Driver/Util.h"16#include "clang/Options/Options.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 28using namespace clang;29using namespace driver;30using namespace llvm::opt;31 32Compilation::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 42Compilation::~Compilation() {43 // Remove temporary files. This must be done before arguments are freed, as44 // 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 57const DerivedArgList &58Compilation::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 else85 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 else97 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 108bool Compilation::CleanupFile(const char *File, bool IssueErrors) const {109 // FIXME: Why are we trying to remove files that we have not created? For110 // example we should only try to remove a temporary assembly file if111 // "clang -cc1" succeed in writing it. Was this a workaround for when112 // clang was writing directly to a .s file and sometimes leaving it behind113 // during a failure?114 115 // FIXME: If this is necessary, we can still try to split116 // llvm::sys::fs::remove into a removeFile and a removeDir and avoid the117 // duplicated stat from is_regular_file.118 119 // Don't try to remove files which we don't have write access to (but may be120 // able to remove), or non-regular files. Underlying tools may have121 // 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 checked127 // 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 138bool 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 146bool 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 160int 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 the169 // 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 210using FailingCommandList = SmallVectorImpl<std::pair<int, const Command *>>;211 212static 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 times218 // so do not compile again if there are already failures. It is OK to abort219 // 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 235void Compilation::ExecuteJobs(const JobList &Jobs,236 FailingCommandList &FailingCommands,237 bool LogOnly) const {238 // According to UNIX standard, driver need to continue compiling all the239 // inputs on the command line even one of them failed.240 // In all but CLMode, execute all the jobs unless the necessary inputs for the241 // job is missing due to previous failures.242 for (const auto &Job : Jobs) {243 if (ActionFailed(&Job.getSource(), FailingCommands))244 continue;245 const Command *FailingCommand = nullptr;246 if (int Res = ExecuteCommand(Job, FailingCommand, LogOnly)) {247 FailingCommands.push_back(std::make_pair(Res, FailingCommand));248 // Bail as soon as one command fails in cl driver mode.249 if (TheDriver.IsCLMode())250 return;251 }252 }253}254 255void Compilation::initCompilationForDiagnostics() {256 ForDiagnostics = true;257 258 // Free actions and jobs.259 Actions.clear();260 AllActions.clear();261 Jobs.clear();262 263 // Remove temporary files.264 if (!TheDriver.isSaveTempsEnabled() && !ForceKeepTempFiles)265 CleanupFileList(TempFiles);266 267 // Clear temporary/results file lists.268 TempFiles.clear();269 ResultFiles.clear();270 FailureResultFiles.clear();271 272 // Remove any user specified output. Claim any unclaimed arguments, so as273 // to avoid emitting warnings about unused args.274 OptSpecifier OutputOpts[] = {275 options::OPT_o, options::OPT_MD, options::OPT_MMD, options::OPT_M,276 options::OPT_MM, options::OPT_MF, options::OPT_MG, options::OPT_MJ,277 options::OPT_MQ, options::OPT_MT, options::OPT_MV};278 for (const auto &Opt : OutputOpts) {279 if (TranslatedArgs->hasArg(Opt))280 TranslatedArgs->eraseArg(Opt);281 }282 TranslatedArgs->ClaimAllArgs();283 284 // Force re-creation of the toolchain Args, otherwise our modifications just285 // above will have no effect.286 for (auto Arg : TCArgs)287 if (Arg.second != TranslatedArgs)288 delete Arg.second;289 TCArgs.clear();290 291 // Redirect stdout/stderr to /dev/null.292 Redirects = {std::nullopt, {""}, {""}};293 294 // Temporary files added by diagnostics should be kept.295 ForceKeepTempFiles = true;296}297 298StringRef Compilation::getSysRoot() const {299 return getDriver().SysRoot;300}301 302void Compilation::Redirect(ArrayRef<std::optional<StringRef>> Redirects) {303 this->Redirects = Redirects;304}305