37#include "llvm/ADT/ArrayRef.h"
38#include "llvm/ADT/IntrusiveRefCntPtr.h"
39#include "llvm/ADT/SmallString.h"
40#include "llvm/ADT/StringRef.h"
41#include "llvm/ADT/Twine.h"
42#include "llvm/Option/ArgList.h"
43#include "llvm/Option/OptTable.h"
44#include "llvm/Option/Option.h"
45#include "llvm/Support/Casting.h"
46#include "llvm/Support/CommandLine.h"
47#include "llvm/Support/Debug.h"
48#include "llvm/Support/ErrorHandling.h"
49#include "llvm/Support/FileSystem.h"
50#include "llvm/Support/MemoryBuffer.h"
51#include "llvm/Support/Path.h"
52#include "llvm/Support/VirtualFileSystem.h"
53#include "llvm/Support/raw_ostream.h"
54#include "llvm/TargetParser/Host.h"
59#include <system_error>
63#define DEBUG_TYPE "clang-tooling"
66using namespace tooling;
81 new driver::Driver(BinaryName, llvm::sys::getDefaultTargetTriple(),
82 *Diagnostics,
"clang LLVM compiler", std::move(VFS));
83 CompilerDriver->
setTitle(
"clang_based_tool");
84 return CompilerDriver;
92 bool OffloadCompilation =
false;
97 for (
const auto &Job : Jobs)
98 if (StringRef(Job.getExecutable()) ==
"clang-offload-bundler")
99 OffloadCompilation =
true;
101 if (Jobs.
size() > 1) {
102 for (
auto *A : Actions){
104 if (isa<driver::BindArchAction>(A))
105 A = *A->input_begin();
106 if (isa<driver::OffloadAction>(A)) {
113 assert(Actions.size() > 1);
115 isa<driver::CompileJobAction>(Actions.front()) ||
118 (isa<driver::BindArchAction>(Actions.front()) &&
119 isa<driver::CompileJobAction>(*Actions.front()->input_begin())));
120 OffloadCompilation =
true;
126 return OffloadCompilation;
132const llvm::opt::ArgStringList *
138 return StringRef(
Cmd.getCreator().getName()) ==
"clang";
142 return isSrcFile(II.getType());
147 if (IsCC1Command(Job) && llvm::all_of(Job.getInputInfos(), IsSrcFile))
148 CC1Jobs.push_back(&Job);
150 if (CC1Jobs.empty() ||
153 llvm::raw_svector_ostream error_stream(error_msg);
154 Jobs.
Print(error_stream,
"; ",
true);
155 Diagnostics->
Report(diag::err_fe_expected_compiler_job)
156 << error_stream.str();
160 return &CC1Jobs[0]->getArguments();
166 const char *
const BinaryName) {
167 assert(!CC1Args.empty() &&
"Must at least contain the program name!");
177 const Twine &Code,
const Twine &FileName,
178 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
180 std::vector<std::string>(), FileName,
181 "clang-tool", std::move(PCHContainerOps));
187static std::vector<std::string>
189 const std::vector<std::string> &ExtraArgs,
190 StringRef FileName) {
191 std::vector<std::string> Args;
192 Args.push_back(ToolName.str());
193 Args.push_back(
"-fsyntax-only");
194 Args.insert(Args.end(), ExtraArgs.begin(), ExtraArgs.end());
195 Args.push_back(FileName.str());
203 std::unique_ptr<FrontendAction>
ToolAction,
const Twine &Code,
205 const std::vector<std::string> &Args,
const Twine &FileName,
206 const Twine &ToolName,
207 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
209 StringRef FileNameRef = FileName.toNullTerminatedStringRef(FileNameStorage);
216 std::move(
ToolAction), Files.get(), std::move(PCHContainerOps));
217 return Invocation.
run();
221 std::unique_ptr<FrontendAction>
ToolAction,
const Twine &Code,
222 const std::vector<std::string> &Args,
const Twine &FileName,
223 const Twine &ToolName,
224 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
227 new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
229 new llvm::vfs::InMemoryFileSystem);
230 OverlayFileSystem->pushOverlay(InMemoryFileSystem);
233 InMemoryFileSystem->addFile(FileName, 0,
234 llvm::MemoryBuffer::getMemBuffer(
235 Code.toNullTerminatedStringRef(CodeStorage)));
237 for (
auto &FilenameWithContent : VirtualMappedFiles) {
238 InMemoryFileSystem->addFile(
239 FilenameWithContent.first, 0,
240 llvm::MemoryBuffer::getMemBuffer(FilenameWithContent.second));
244 Args, FileName, ToolName);
249 StringRef RelativePath(
File);
251 if (RelativePath.startswith(
"./")) {
252 RelativePath = RelativePath.substr(strlen(
"./"));
256 if (
auto EC = FS.makeAbsolute(AbsolutePath))
257 return llvm::errorCodeToError(EC);
258 llvm::sys::path::native(AbsolutePath);
259 return std::string(AbsolutePath.str());
267 StringRef InvokedAs) {
268 if (CommandLine.empty() || InvokedAs.empty())
272 const std::string TargetOPT =
273 Table.getOption(driver::options::OPT_target).getPrefixedName();
275 const std::string TargetOPTLegacy =
276 Table.getOption(driver::options::OPT_target_legacy_spelling)
279 const std::string DriverModeOPT =
280 Table.getOption(driver::options::OPT_driver_mode).getPrefixedName();
284 bool ShouldAddTarget = TargetMode.TargetIsValid;
285 bool ShouldAddMode = TargetMode.DriverMode !=
nullptr;
287 for (
auto Token = ++CommandLine.begin();
Token != CommandLine.end();
289 StringRef TokenRef(*
Token);
290 ShouldAddTarget = ShouldAddTarget && !TokenRef.startswith(TargetOPT) &&
291 !TokenRef.equals(TargetOPTLegacy);
292 ShouldAddMode = ShouldAddMode && !TokenRef.startswith(DriverModeOPT);
295 CommandLine.insert(++CommandLine.begin(), TargetMode.DriverMode);
297 if (ShouldAddTarget) {
298 CommandLine.insert(++CommandLine.begin(),
299 TargetOPT + TargetMode.TargetPrefix);
304 llvm::StringRef WorkingDir,
305 llvm::cl::TokenizerCallback Tokenizer,
306 llvm::vfs::FileSystem &FS) {
307 bool SeenRSPFile =
false;
309 Argv.reserve(CommandLine.size());
310 for (
auto &Arg : CommandLine) {
311 Argv.push_back(Arg.c_str());
313 SeenRSPFile |= Arg.front() ==
'@';
317 llvm::BumpPtrAllocator Alloc;
318 llvm::cl::ExpansionContext ECtx(Alloc, Tokenizer);
320 ECtx.setVFS(&FS).setCurrentDir(WorkingDir).expandResponseFiles(Argv);
324 std::vector<std::string> ExpandedArgv(Argv.begin(), Argv.end());
325 CommandLine = std::move(ExpandedArgv);
334 std::unique_ptr<FrontendAction> Action;
337 SingleFrontendActionFactory(std::unique_ptr<FrontendAction> Action)
338 : Action(
std::move(Action)) {}
340 std::unique_ptr<FrontendAction>
create()
override {
341 return std::move(Action);
348 std::vector<std::string> CommandLine,
ToolAction *Action,
349 FileManager *Files, std::shared_ptr<PCHContainerOperations> PCHContainerOps)
350 : CommandLine(
std::move(CommandLine)), Action(Action), OwnsAction(
false),
351 Files(Files), PCHContainerOps(
std::move(PCHContainerOps)) {}
354 std::vector<std::string> CommandLine,
355 std::unique_ptr<FrontendAction> FAction,
FileManager *Files,
356 std::shared_ptr<PCHContainerOperations> PCHContainerOps)
357 : CommandLine(
std::move(CommandLine)),
358 Action(new SingleFrontendActionFactory(
std::move(FAction))),
359 OwnsAction(
true), Files(Files),
360 PCHContainerOps(
std::move(PCHContainerOps)) {}
368 llvm::opt::ArgStringList Argv;
369 for (
const std::string &Str : CommandLine)
370 Argv.push_back(Str.c_str());
371 const char *
const BinaryName = Argv[0];
379 DiagOpts = &*ParsedDiagOpts;
385 &*DiagOpts, DiagConsumer ? DiagConsumer : &DiagnosticPrinter,
false);
389 Diagnostics->setSourceManager(&SrcMgr);
392 if (CommandLine.size() >= 2 && CommandLine[1] ==
"-cc1") {
394 std::unique_ptr<CompilerInvocation> Invocation(
396 if (Diagnostics->hasErrorOccurred())
399 std::move(PCHContainerOps), DiagConsumer);
402 const std::unique_ptr<driver::Driver> Driver(
409 Driver->setCheckInputsExist(
false);
410 const std::unique_ptr<driver::Compilation> Compilation(
415 &*Diagnostics, Compilation.get());
418 std::unique_ptr<CompilerInvocation> Invocation(
420 return runInvocation(BinaryName, Compilation.get(), std::move(Invocation),
421 std::move(PCHContainerOps));
424bool ToolInvocation::runInvocation(
426 std::shared_ptr<CompilerInvocation> Invocation,
427 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
429 if (Invocation->getHeaderSearchOpts().Verbose) {
430 llvm::errs() <<
"clang Invocation:\n";
432 llvm::errs() <<
"\n";
436 std::move(PCHContainerOps), DiagConsumer);
440 std::shared_ptr<CompilerInvocation> Invocation,
FileManager *Files,
441 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
451 std::unique_ptr<FrontendAction> ScopedToolAction(
create());
460 const bool Success = Compiler.
ExecuteAction(*ScopedToolAction);
468 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
471 : Compilations(Compilations), SourcePaths(SourcePaths),
472 PCHContainerOps(
std::move(PCHContainerOps)),
473 OverlayFileSystem(new
llvm::vfs::OverlayFileSystem(
std::move(BaseFS))),
474 InMemoryFileSystem(new
llvm::vfs::InMemoryFileSystem),
477 OverlayFileSystem->pushOverlay(InMemoryFileSystem);
482 Files->setVirtualFileSystem(OverlayFileSystem);
488 MappedFileContents.push_back(std::make_pair(FilePath, Content));
492 ArgsAdjuster =
combineAdjusters(std::move(ArgsAdjuster), std::move(Adjuster));
496 ArgsAdjuster =
nullptr;
502 for (StringRef Arg : Args)
503 if (Arg.startswith(
"-resource-dir"))
515 static int StaticSymbol;
519 if (SeenWorkingDirectories.insert(
"/").second)
520 for (
const auto &MappedFile : MappedFileContents)
521 if (llvm::sys::path::is_absolute(MappedFile.first))
522 InMemoryFileSystem->addFile(
524 llvm::MemoryBuffer::getMemBuffer(MappedFile.second));
526 bool ProcessingFailed =
false;
527 bool FileSkipped =
false;
530 std::vector<std::string> AbsolutePaths;
531 AbsolutePaths.reserve(SourcePaths.size());
532 for (
const auto &SourcePath : SourcePaths) {
535 llvm::errs() <<
"Skipping " << SourcePath
536 <<
". Error while getting an absolute path: "
537 << llvm::toString(AbsPath.takeError()) <<
"\n";
540 AbsolutePaths.push_back(std::move(*AbsPath));
544 std::string InitialWorkingDir;
546 if (
auto CWD = OverlayFileSystem->getCurrentWorkingDirectory()) {
547 InitialWorkingDir = std::move(*CWD);
549 llvm::errs() <<
"Could not get working directory: "
550 << CWD.getError().message() <<
"\n";
554 for (llvm::StringRef
File : AbsolutePaths) {
562 std::vector<CompileCommand> CompileCommandsForFile =
564 if (CompileCommandsForFile.empty()) {
565 llvm::errs() <<
"Skipping " <<
File <<
". Compile command not found.\n";
577 if (OverlayFileSystem->setCurrentWorkingDirectory(
579 llvm::report_fatal_error(
"Cannot chdir into \"" +
586 for (
const auto &MappedFile : MappedFileContents)
587 if (!llvm::sys::path::is_absolute(MappedFile.first))
588 InMemoryFileSystem->addFile(
590 llvm::MemoryBuffer::getMemBuffer(MappedFile.second));
595 assert(!CommandLine.empty());
609 LLVM_DEBUG({ llvm::dbgs() <<
"Processing: " <<
File <<
".\n"; });
610 ToolInvocation Invocation(std::move(CommandLine), Action, Files.get(),
612 Invocation.setDiagnosticConsumer(DiagConsumer);
614 if (!Invocation.run()) {
616 if (PrintErrorMessage)
617 llvm::errs() <<
"Error while processing " <<
File <<
".\n";
618 ProcessingFailed =
true;
623 if (!InitialWorkingDir.empty()) {
625 OverlayFileSystem->setCurrentWorkingDirectory(InitialWorkingDir))
626 llvm::errs() <<
"Error when trying to restore working dir: "
627 << EC.message() <<
"\n";
629 return ProcessingFailed ? 1 : (FileSkipped ? 2 : 0);
635 std::vector<std::unique_ptr<ASTUnit>> &ASTs;
638 ASTBuilderAction(std::vector<std::unique_ptr<ASTUnit>> &ASTs) : ASTs(ASTs) {}
640 bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
642 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
644 std::unique_ptr<ASTUnit> AST = ASTUnit::LoadFromCompilerInvocation(
645 Invocation, std::move(PCHContainerOps),
653 ASTs.push_back(std::move(AST));
661 ASTBuilderAction Action(ASTs);
666 this->RestoreCWD = RestoreCWD;
670 this->PrintErrorMessage = PrintErrorMessage;
676std::unique_ptr<ASTUnit>
678 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
680 "clang-tool", std::move(PCHContainerOps));
684 StringRef Code,
const std::vector<std::string> &Args, StringRef FileName,
685 StringRef ToolName, std::shared_ptr<PCHContainerOperations> PCHContainerOps,
688 std::vector<std::unique_ptr<ASTUnit>> ASTs;
689 ASTBuilderAction Action(ASTs);
691 new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
693 new llvm::vfs::InMemoryFileSystem);
694 OverlayFileSystem->pushOverlay(InMemoryFileSystem);
700 &Action, Files.get(), std::move(PCHContainerOps));
703 InMemoryFileSystem->addFile(FileName, 0,
704 llvm::MemoryBuffer::getMemBufferCopy(Code));
705 for (
auto &FilenameWithContent : VirtualMappedFiles) {
706 InMemoryFileSystem->addFile(
707 FilenameWithContent.first, 0,
708 llvm::MemoryBuffer::getMemBuffer(FilenameWithContent.second));
711 if (!Invocation.
run())
714 assert(ASTs.size() == 1);
715 return std::move(ASTs[0]);
Defines the Diagnostic-related interfaces.
Defines the Diagnostic IDs-related interfaces.
Defines the clang::FileManager interface and associated types.
Defines the clang::FileSystemOptions interface.
Forward-declares and imports various common LLVM datatypes that clang wants to use unqualified.
CompilerInstance - Helper class for managing a single instance of the Clang compiler.
void setFileManager(FileManager *Value)
Replace the current file manager and virtual file system.
void createSourceManager(FileManager &FileMgr)
Create the source manager and replace any existing one with it.
void createDiagnostics(DiagnosticConsumer *Client=nullptr, bool ShouldOwnClient=true)
Create the diagnostics engine using the invocation's diagnostic options and replace any existing one ...
void setInvocation(std::shared_ptr< CompilerInvocation > Value)
setInvocation - Replace the current invocation.
bool hasDiagnostics() const
bool ExecuteAction(FrontendAction &Act)
ExecuteAction - Execute the provided action against the compiler's CompilerInvocation object.
CodeGenOptions & getCodeGenOpts()
FrontendOptions & getFrontendOpts()
Helper class for holding the data necessary to invoke the compiler.
static std::string GetResourcesPath(const char *Argv0, void *MainAddr)
Get the directory where the compiler headers reside, relative to the compiler binary (found by the pa...
static bool CreateFromArgs(CompilerInvocation &Res, ArrayRef< const char * > CommandLineArgs, DiagnosticsEngine &Diags, const char *Argv0=nullptr)
Create a compiler invocation from a list of input options.
Abstract interface, implemented by clients of the front-end, which formats and prints fully processed...
Options for controlling the compiler diagnostics engine.
Concrete class used by the front-end to report problems and issues.
DiagnosticBuilder Report(SourceLocation Loc, unsigned DiagID)
Issue the message to the client.
Implements support for file system lookup, file system caching, and directory search management.
void clearStatCache()
Removes the FileSystemStatCache object from the manager.
llvm::vfs::FileSystem & getVirtualFileSystem() const
FileSystemOptions & getFileSystemOpts()
Returns the current file system options.
Keeps track of options that affect how file operations are performed.
std::string WorkingDir
If set, paths are resolved as if the working directory was set to the value of WorkingDir.
unsigned DisableFree
Disable memory freeing on exit.
This class handles loading and caching of source files into memory.
Token - This structure provides full information about a lexed token.
Command - An executable path/name and argument vector to execute.
Compilation - A set of tasks to perform for a single driver invocation.
ActionList & getActions()
Driver - Encapsulate logic for constructing compilation processes from a set of gcc-driver-like comma...
void setTitle(std::string Value)
JobList - A sequence of jobs to perform.
void Print(llvm::raw_ostream &OS, const char *Terminator, bool Quote, CrashReportInfo *CrashInfo=nullptr) const
const llvm::opt::OptTable & getDriverOptTable()
std::unique_ptr< DiagnosticOptions > CreateAndPopulateDiagOpts(ArrayRef< const char * > Argv)
YAML serialization mapping.