37#include "llvm/ADT/ArrayRef.h"
38#include "llvm/ADT/IntrusiveRefCntPtr.h"
39#include "llvm/ADT/StringRef.h"
40#include "llvm/ADT/Twine.h"
41#include "llvm/Option/ArgList.h"
42#include "llvm/Option/OptTable.h"
43#include "llvm/Option/Option.h"
44#include "llvm/Support/CommandLine.h"
45#include "llvm/Support/Debug.h"
46#include "llvm/Support/ErrorHandling.h"
47#include "llvm/Support/MemoryBuffer.h"
48#include "llvm/Support/Path.h"
49#include "llvm/Support/VirtualFileSystem.h"
50#include "llvm/Support/raw_ostream.h"
51#include "llvm/TargetParser/Host.h"
56#include <system_error>
60#define DEBUG_TYPE "clang-tooling"
78 new driver::Driver(BinaryName, llvm::sys::getDefaultTargetTriple(),
79 *Diagnostics,
"clang LLVM compiler", std::move(VFS));
80 CompilerDriver->
setTitle(
"clang_based_tool");
81 return CompilerDriver;
89 bool OffloadCompilation =
false;
94 for (
const auto &Job : Jobs)
95 if (StringRef(Job.getExecutable()) ==
"clang-offload-bundler")
96 OffloadCompilation =
true;
98 if (Jobs.
size() > 1) {
99 for (
auto *A : Actions){
102 A = *A->input_begin();
110 assert(Actions.size() > 1);
117 OffloadCompilation =
true;
123 return OffloadCompilation;
129const llvm::opt::ArgStringList *
135 return StringRef(Cmd.getCreator().getName()) ==
"clang";
139 return isSrcFile(II.getType());
144 if (IsCC1Command(Job) && llvm::all_of(Job.getInputInfos(), IsSrcFile))
145 CC1Jobs.push_back(&Job);
151 if (IsCC1Command(Job))
152 CC1Jobs.push_back(&Job);
154 if (CC1Jobs.empty() ||
157 llvm::raw_svector_ostream error_stream(error_msg);
158 Jobs.
Print(error_stream,
"; ",
true);
159 Diagnostics->
Report(diag::err_fe_expected_compiler_job)
160 << error_stream.str();
164 return &CC1Jobs[0]->getArguments();
170 const char *
const BinaryName) {
171 assert(!CC1Args.empty() &&
"Must at least contain the program name!");
181 const Twine &Code,
const Twine &
FileName,
182 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
184 std::vector<std::string>(),
FileName,
185 "clang-tool", std::move(PCHContainerOps));
191static std::vector<std::string>
193 const std::vector<std::string> &ExtraArgs,
195 std::vector<std::string> Args;
196 Args.push_back(ToolName.str());
197 Args.push_back(
"-fsyntax-only");
198 llvm::append_range(Args, ExtraArgs);
207 std::unique_ptr<FrontendAction>
ToolAction,
const Twine &Code,
209 const std::vector<std::string> &Args,
const Twine &
FileName,
210 const Twine &ToolName,
211 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
213 StringRef FileNameRef =
FileName.toNullTerminatedStringRef(FileNameStorage);
220 std::move(
ToolAction), Files.get(), std::move(PCHContainerOps));
221 return Invocation.
run();
225 std::unique_ptr<FrontendAction>
ToolAction,
const Twine &Code,
226 const std::vector<std::string> &Args,
const Twine &
FileName,
227 const Twine &ToolName,
228 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
230 auto OverlayFileSystem =
231 llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(
232 llvm::vfs::getRealFileSystem());
233 auto InMemoryFileSystem =
234 llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
235 OverlayFileSystem->pushOverlay(InMemoryFileSystem);
238 InMemoryFileSystem->addFile(
FileName, 0,
239 llvm::MemoryBuffer::getMemBuffer(
240 Code.toNullTerminatedStringRef(CodeStorage)));
242 for (
auto &FilenameWithContent : VirtualMappedFiles) {
243 InMemoryFileSystem->addFile(
244 FilenameWithContent.first, 0,
245 llvm::MemoryBuffer::getMemBuffer(FilenameWithContent.second));
254 StringRef RelativePath(
File);
256 RelativePath.consume_front(
"./");
259 if (
auto EC = FS.makeAbsolute(AbsolutePath))
260 return llvm::errorCodeToError(EC);
261 llvm::sys::path::native(AbsolutePath);
262 return std::string(AbsolutePath);
270 StringRef InvokedAs) {
271 if (CommandLine.empty() || InvokedAs.empty())
275 StringRef TargetOPT =
276 Table.getOption(driver::options::OPT_target).getPrefixedName();
278 StringRef TargetOPTLegacy =
279 Table.getOption(driver::options::OPT_target_legacy_spelling)
282 StringRef DriverModeOPT =
283 Table.getOption(driver::options::OPT_driver_mode).getPrefixedName();
287 bool ShouldAddTarget = TargetMode.TargetIsValid;
288 bool ShouldAddMode = TargetMode.DriverMode !=
nullptr;
290 for (
auto Token = ++CommandLine.begin();
Token != CommandLine.end();
292 StringRef TokenRef(*
Token);
293 ShouldAddTarget = ShouldAddTarget && !TokenRef.starts_with(TargetOPT) &&
294 TokenRef != TargetOPTLegacy;
295 ShouldAddMode = ShouldAddMode && !TokenRef.starts_with(DriverModeOPT);
298 CommandLine.insert(++CommandLine.begin(), TargetMode.DriverMode);
300 if (ShouldAddTarget) {
301 CommandLine.insert(++CommandLine.begin(),
302 (TargetOPT + TargetMode.TargetPrefix).str());
307 llvm::StringRef WorkingDir,
308 llvm::cl::TokenizerCallback Tokenizer,
309 llvm::vfs::FileSystem &FS) {
310 bool SeenRSPFile =
false;
312 Argv.reserve(CommandLine.size());
313 for (
auto &Arg : CommandLine) {
314 Argv.push_back(Arg.c_str());
316 SeenRSPFile |= Arg.front() ==
'@';
320 llvm::BumpPtrAllocator Alloc;
321 llvm::cl::ExpansionContext ECtx(Alloc, Tokenizer);
323 ECtx.setVFS(&FS).setCurrentDir(WorkingDir).expandResponseFiles(Argv);
327 std::vector<std::string> ExpandedArgv(Argv.begin(), Argv.end());
328 CommandLine = std::move(ExpandedArgv);
337 std::unique_ptr<FrontendAction> Action;
340 SingleFrontendActionFactory(std::unique_ptr<FrontendAction> Action)
341 : Action(std::move(Action)) {}
343 std::unique_ptr<FrontendAction>
create()
override {
344 return std::move(Action);
351 std::vector<std::string> CommandLine,
ToolAction *Action,
352 FileManager *Files, std::shared_ptr<PCHContainerOperations> PCHContainerOps)
353 : CommandLine(
std::move(CommandLine)), Action(Action), OwnsAction(
false),
354 Files(Files), PCHContainerOps(
std::move(PCHContainerOps)) {}
357 std::vector<std::string> CommandLine,
358 std::unique_ptr<FrontendAction> FAction,
FileManager *Files,
359 std::shared_ptr<PCHContainerOperations> PCHContainerOps)
360 : CommandLine(
std::move(CommandLine)),
361 Action(new SingleFrontendActionFactory(
std::move(FAction))),
362 OwnsAction(
true), Files(Files),
363 PCHContainerOps(
std::move(PCHContainerOps)) {}
371 llvm::opt::ArgStringList Argv;
372 for (
const std::string &Str : CommandLine)
373 Argv.push_back(Str.c_str());
374 const char *
const BinaryName = Argv[0];
378 std::unique_ptr<DiagnosticOptions> ParsedDiagOpts;
382 DiagOpts = &*ParsedDiagOpts;
388 Files->getVirtualFileSystem(), *DiagOpts,
389 DiagConsumer ? DiagConsumer : &DiagnosticPrinter,
false);
393 Diagnostics->setSourceManager(&
SrcMgr);
396 if (CommandLine.size() >= 2 && CommandLine[1] ==
"-cc1") {
398 std::unique_ptr<CompilerInvocation> Invocation(
400 if (Diagnostics->hasErrorOccurred())
402 return Action->runInvocation(std::move(Invocation), Files,
403 std::move(PCHContainerOps), DiagConsumer);
406 const std::unique_ptr<driver::Driver> Driver(
407 newDriver(&*Diagnostics, BinaryName, Files->getVirtualFileSystemPtr()));
412 if (!Files->getFileSystemOpts().WorkingDir.empty())
413 Driver->setCheckInputsExist(
false);
414 const std::unique_ptr<driver::Compilation> Compilation(
419 &*Diagnostics, Compilation.get());
422 std::unique_ptr<CompilerInvocation> Invocation(
424 return runInvocation(BinaryName, Compilation.get(), std::move(Invocation),
425 std::move(PCHContainerOps));
428bool ToolInvocation::runInvocation(
430 std::shared_ptr<CompilerInvocation> Invocation,
431 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
433 if (Invocation->getHeaderSearchOpts().Verbose) {
434 llvm::errs() <<
"clang Invocation:\n";
436 llvm::errs() <<
"\n";
440 std::move(PCHContainerOps), DiagConsumer);
444 std::shared_ptr<CompilerInvocation> Invocation,
FileManager *Files,
445 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
448 CompilerInstance Compiler(std::move(Invocation), std::move(PCHContainerOps));
454 std::unique_ptr<FrontendAction> ScopedToolAction(
create());
472 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
475 : Compilations(Compilations), SourcePaths(SourcePaths),
476 PCHContainerOps(
std::move(PCHContainerOps)),
477 OverlayFileSystem(
llvm::makeIntrusiveRefCnt<
llvm::
vfs::OverlayFileSystem>(
480 llvm::makeIntrusiveRefCnt<
llvm::
vfs::InMemoryFileSystem>()),
483 OverlayFileSystem)) {
484 OverlayFileSystem->pushOverlay(InMemoryFileSystem);
489 Files->setVirtualFileSystem(OverlayFileSystem);
495 MappedFileContents.push_back(std::make_pair(FilePath, Content));
499 ArgsAdjuster =
combineAdjusters(std::move(ArgsAdjuster), std::move(Adjuster));
503 ArgsAdjuster =
nullptr;
509 for (StringRef Arg : Args)
510 if (Arg.starts_with(
"-resource-dir"))
522 static int StaticSymbol;
526 if (SeenWorkingDirectories.insert(
"/").second)
527 for (
const auto &MappedFile : MappedFileContents)
528 if (llvm::sys::path::is_absolute(MappedFile.first))
529 InMemoryFileSystem->addFile(
531 llvm::MemoryBuffer::getMemBuffer(MappedFile.second));
533 bool ProcessingFailed =
false;
534 bool FileSkipped =
false;
537 std::vector<std::string> AbsolutePaths;
538 AbsolutePaths.reserve(SourcePaths.size());
539 for (
const auto &SourcePath : SourcePaths) {
542 llvm::errs() <<
"Skipping " << SourcePath
543 <<
". Error while getting an absolute path: "
544 << llvm::toString(AbsPath.takeError()) <<
"\n";
547 AbsolutePaths.push_back(std::move(*AbsPath));
551 std::string InitialWorkingDir;
552 if (
auto CWD = OverlayFileSystem->getCurrentWorkingDirectory()) {
553 InitialWorkingDir = std::move(*CWD);
555 llvm::errs() <<
"Could not get working directory: "
556 << CWD.getError().message() <<
"\n";
559 size_t NumOfTotalFiles = AbsolutePaths.size();
560 unsigned ProcessedFileCounter = 0;
561 for (llvm::StringRef
File : AbsolutePaths) {
569 std::vector<CompileCommand> CompileCommandsForFile =
570 Compilations.getCompileCommands(
File);
571 if (CompileCommandsForFile.empty()) {
572 llvm::errs() <<
"Skipping " <<
File <<
". Compile command not found.\n";
580 if (Directory.empty()) {
581 llvm::errs() <<
"'directory' field of compilation database is empty; "
582 "using the current working directory instead.\n";
583 Directory = InitialWorkingDir;
593 if (OverlayFileSystem->setCurrentWorkingDirectory(Directory))
594 llvm::report_fatal_error(
"Cannot chdir into \"" + Twine(Directory) +
600 if (SeenWorkingDirectories.insert(Directory).second)
601 for (
const auto &MappedFile : MappedFileContents)
602 if (!llvm::sys::path::is_absolute(MappedFile.first))
603 InMemoryFileSystem->addFile(
605 llvm::MemoryBuffer::getMemBuffer(MappedFile.second));
610 assert(!CommandLine.empty());
624 if (NumOfTotalFiles > 1)
625 llvm::errs() <<
"[" + std::to_string(++ProcessedFileCounter) +
"/" +
626 std::to_string(NumOfTotalFiles) +
627 "] Processing file " +
File
629 ToolInvocation Invocation(std::move(CommandLine), Action, Files.get(),
633 if (!Invocation.
run()) {
635 if (PrintErrorMessage)
636 llvm::errs() <<
"Error while processing " <<
File <<
".\n";
637 ProcessingFailed =
true;
642 if (!InitialWorkingDir.empty()) {
644 OverlayFileSystem->setCurrentWorkingDirectory(InitialWorkingDir))
645 llvm::errs() <<
"Error when trying to restore working dir: "
646 << EC.message() <<
"\n";
648 return ProcessingFailed ? 1 : (FileSkipped ? 2 : 0);
654 std::vector<std::unique_ptr<ASTUnit>> &ASTs;
659 std::vector<std::unique_ptr<ASTUnit>> &ASTs,
661 : ASTs(ASTs), CaptureKind(CaptureDiagnosticsKind) {}
663 bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
665 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
667 std::unique_ptr<ASTUnit> AST = ASTUnit::LoadFromCompilerInvocation(
668 Invocation, std::move(PCHContainerOps),
nullptr,
670 Invocation->getDiagnosticOpts(),
673 Files,
false, CaptureKind);
677 ASTs.push_back(std::move(AST));
685 ASTBuilderAction Action(ASTs);
690 this->PrintErrorMessage = PrintErrorMessage;
696std::unique_ptr<ASTUnit>
698 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
700 "clang-tool", std::move(PCHContainerOps));
704 StringRef Code,
const std::vector<std::string> &Args, StringRef
FileName,
705 StringRef ToolName, std::shared_ptr<PCHContainerOperations> PCHContainerOps,
710 std::vector<std::unique_ptr<ASTUnit>> ASTs;
712 ASTBuilderAction Action(ASTs, CaptureKind);
714 auto OverlayFileSystem =
715 llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(
717 auto InMemoryFileSystem =
718 llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
719 OverlayFileSystem->pushOverlay(InMemoryFileSystem);
726 &Action, Files.get(), std::move(PCHContainerOps));
729 InMemoryFileSystem->addFile(
FileName, 0,
730 llvm::MemoryBuffer::getMemBufferCopy(Code));
731 for (
auto &FilenameWithContent : VirtualMappedFiles) {
732 InMemoryFileSystem->addFile(
733 FilenameWithContent.first, 0,
734 llvm::MemoryBuffer::getMemBuffer(FilenameWithContent.second));
737 if (!Invocation.
run())
740 assert(ASTs.size() == 1);
741 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 createSourceManager(FileManager &FileMgr)
Create the source manager and replace any existing one with it.
void createDiagnostics(llvm::vfs::FileSystem &VFS, DiagnosticConsumer *Client=nullptr, bool ShouldOwnClient=true)
Create the diagnostics engine using the invocation's diagnostic options and replace any existing one ...
bool hasDiagnostics() const
void setFileManager(IntrusiveRefCntPtr< FileManager > Value)
Replace the current file manager and virtual file system.
bool ExecuteAction(FrontendAction &Act)
ExecuteAction - Execute the provided action against the compiler's CompilerInvocation object.
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.
FrontendOptions & getFrontendOpts()
CodeGenOptions & getCodeGenOpts()
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
Keeps track of options that affect how file operations are performed.
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
Public enums and private classes that are part of the SourceManager implementation.
SmallVector< Action *, 3 > ActionList
ActionList - Type used for lists of actions.
const llvm::opt::OptTable & getDriverOptTable()
std::unique_ptr< DiagnosticConsumer > create(StringRef OutputFile, DiagnosticOptions &DiagOpts, bool MergeChildRecords=false)
Returns a DiagnosticConsumer that serializes diagnostics to a bitcode file.
The JSON file list parser is used to communicate input to InstallAPI.
bool isa(CodeGen::Address addr)
std::unique_ptr< DiagnosticOptions > CreateAndPopulateDiagOpts(ArrayRef< const char * > Argv)
@ Success
Annotation was successful.
CaptureDiagsKind
Enumerates the available kinds for capturing diagnostics.
Diagnostic wrappers for TextAPI types for error reporting.