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());
471 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
474 : Compilations(Compilations), SourcePaths(SourcePaths),
475 PCHContainerOps(
std::move(PCHContainerOps)),
476 OverlayFileSystem(
llvm::makeIntrusiveRefCnt<
llvm::
vfs::OverlayFileSystem>(
479 llvm::makeIntrusiveRefCnt<
llvm::
vfs::InMemoryFileSystem>()),
482 OverlayFileSystem)) {
483 OverlayFileSystem->pushOverlay(InMemoryFileSystem);
488 Files->setVirtualFileSystem(OverlayFileSystem);
494 MappedFileContents.push_back(std::make_pair(FilePath, Content));
498 ArgsAdjuster =
combineAdjusters(std::move(ArgsAdjuster), std::move(Adjuster));
502 ArgsAdjuster =
nullptr;
508 for (StringRef Arg : Args)
509 if (Arg.starts_with(
"-resource-dir"))
521 static int StaticSymbol;
525 if (SeenWorkingDirectories.insert(
"/").second)
526 for (
const auto &MappedFile : MappedFileContents)
527 if (llvm::sys::path::is_absolute(MappedFile.first))
528 InMemoryFileSystem->addFile(
530 llvm::MemoryBuffer::getMemBuffer(MappedFile.second));
532 bool ProcessingFailed =
false;
533 bool FileSkipped =
false;
536 std::vector<std::string> AbsolutePaths;
537 AbsolutePaths.reserve(SourcePaths.size());
538 for (
const auto &SourcePath : SourcePaths) {
541 llvm::errs() <<
"Skipping " << SourcePath
542 <<
". Error while getting an absolute path: "
543 << llvm::toString(AbsPath.takeError()) <<
"\n";
546 AbsolutePaths.push_back(std::move(*AbsPath));
550 std::string InitialWorkingDir;
551 if (
auto CWD = OverlayFileSystem->getCurrentWorkingDirectory()) {
552 InitialWorkingDir = std::move(*CWD);
554 llvm::errs() <<
"Could not get working directory: "
555 << CWD.getError().message() <<
"\n";
558 size_t NumOfTotalFiles = AbsolutePaths.size();
559 unsigned ProcessedFileCounter = 0;
560 for (llvm::StringRef
File : AbsolutePaths) {
568 std::vector<CompileCommand> CompileCommandsForFile =
569 Compilations.getCompileCommands(
File);
570 if (CompileCommandsForFile.empty()) {
571 llvm::errs() <<
"Skipping " <<
File <<
". Compile command not found.\n";
579 if (Directory.empty()) {
580 llvm::errs() <<
"'directory' field of compilation database is empty; "
581 "using the current working directory instead.\n";
582 Directory = InitialWorkingDir;
592 if (OverlayFileSystem->setCurrentWorkingDirectory(Directory))
593 llvm::report_fatal_error(
"Cannot chdir into \"" + Twine(Directory) +
599 if (SeenWorkingDirectories.insert(Directory).second)
600 for (
const auto &MappedFile : MappedFileContents)
601 if (!llvm::sys::path::is_absolute(MappedFile.first))
602 InMemoryFileSystem->addFile(
604 llvm::MemoryBuffer::getMemBuffer(MappedFile.second));
609 assert(!CommandLine.empty());
623 if (NumOfTotalFiles > 1)
624 llvm::errs() <<
"[" + std::to_string(++ProcessedFileCounter) +
"/" +
625 std::to_string(NumOfTotalFiles) +
626 "] Processing file " +
File
628 ToolInvocation Invocation(std::move(CommandLine), Action, Files.get(),
632 if (!Invocation.
run()) {
634 if (PrintErrorMessage)
635 llvm::errs() <<
"Error while processing " <<
File <<
".\n";
636 ProcessingFailed =
true;
641 if (!InitialWorkingDir.empty()) {
643 OverlayFileSystem->setCurrentWorkingDirectory(InitialWorkingDir))
644 llvm::errs() <<
"Error when trying to restore working dir: "
645 << EC.message() <<
"\n";
647 return ProcessingFailed ? 1 : (FileSkipped ? 2 : 0);
653 std::vector<std::unique_ptr<ASTUnit>> &ASTs;
658 std::vector<std::unique_ptr<ASTUnit>> &ASTs,
660 : ASTs(ASTs), CaptureKind(CaptureDiagnosticsKind) {}
662 bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
664 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
666 std::unique_ptr<ASTUnit> AST = ASTUnit::LoadFromCompilerInvocation(
667 Invocation, std::move(PCHContainerOps),
nullptr,
669 Invocation->getDiagnosticOpts(),
672 Files,
false, CaptureKind);
676 ASTs.push_back(std::move(AST));
684 ASTBuilderAction Action(ASTs);
689 this->PrintErrorMessage = PrintErrorMessage;
695std::unique_ptr<ASTUnit>
697 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
699 "clang-tool", std::move(PCHContainerOps));
703 StringRef Code,
const std::vector<std::string> &Args, StringRef
FileName,
704 StringRef ToolName, std::shared_ptr<PCHContainerOperations> PCHContainerOps,
709 std::vector<std::unique_ptr<ASTUnit>> ASTs;
711 ASTBuilderAction Action(ASTs, CaptureKind);
713 auto OverlayFileSystem =
714 llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(
716 auto InMemoryFileSystem =
717 llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
718 OverlayFileSystem->pushOverlay(InMemoryFileSystem);
725 &Action, Files.get(), std::move(PCHContainerOps));
728 InMemoryFileSystem->addFile(
FileName, 0,
729 llvm::MemoryBuffer::getMemBufferCopy(Code));
730 for (
auto &FilenameWithContent : VirtualMappedFiles) {
731 InMemoryFileSystem->addFile(
732 FilenameWithContent.first, 0,
733 llvm::MemoryBuffer::getMemBuffer(FilenameWithContent.second));
736 if (!Invocation.
run())
739 assert(ASTs.size() == 1);
740 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(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.