38#include "llvm/ADT/ArrayRef.h"
39#include "llvm/ADT/IntrusiveRefCntPtr.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/CommandLine.h"
46#include "llvm/Support/Debug.h"
47#include "llvm/Support/ErrorHandling.h"
48#include "llvm/Support/MemoryBuffer.h"
49#include "llvm/Support/Path.h"
50#include "llvm/Support/VirtualFileSystem.h"
51#include "llvm/Support/raw_ostream.h"
52#include "llvm/TargetParser/Host.h"
57#include <system_error>
61#define DEBUG_TYPE "clang-tooling"
79 new driver::Driver(BinaryName, llvm::sys::getDefaultTargetTriple(),
80 *Diagnostics,
"clang LLVM compiler", std::move(VFS));
81 CompilerDriver->
setTitle(
"clang_based_tool");
82 return CompilerDriver;
90 bool OffloadCompilation =
false;
95 for (
const auto &Job : Jobs)
96 if (StringRef(Job.getExecutable()) ==
"clang-offload-bundler")
97 OffloadCompilation =
true;
99 if (Jobs.
size() > 1) {
100 for (
auto *A : Actions) {
103 A = *A->input_begin();
111 assert(Actions.size() > 1);
118 OffloadCompilation =
true;
124 return OffloadCompilation;
130const llvm::opt::ArgStringList *
136 return StringRef(Cmd.getCreator().getName()) ==
"clang";
140 return isSrcFile(II.getType());
145 if (IsCC1Command(Job) && llvm::all_of(Job.getInputInfos(), IsSrcFile))
146 CC1Jobs.push_back(&Job);
152 if (IsCC1Command(Job))
153 CC1Jobs.push_back(&Job);
155 if (CC1Jobs.empty() ||
158 llvm::raw_svector_ostream error_stream(error_msg);
159 Jobs.
Print(error_stream,
"; ",
true);
160 Diagnostics->
Report(diag::err_fe_expected_compiler_job)
161 << error_stream.str();
165 return &CC1Jobs[0]->getArguments();
171 const char *
const BinaryName) {
172 assert(!CC1Args.empty() &&
"Must at least contain the program name!");
182 const Twine &Code,
const Twine &
FileName,
183 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
185 std::vector<std::string>(),
FileName,
186 "clang-tool", std::move(PCHContainerOps));
192static std::vector<std::string>
194 const std::vector<std::string> &ExtraArgs,
196 std::vector<std::string> Args;
197 Args.push_back(ToolName.str());
198 Args.push_back(
"-fsyntax-only");
199 llvm::append_range(Args, ExtraArgs);
208 std::unique_ptr<FrontendAction>
ToolAction,
const Twine &Code,
210 const std::vector<std::string> &Args,
const Twine &
FileName,
211 const Twine &ToolName,
212 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
214 StringRef FileNameRef =
FileName.toNullTerminatedStringRef(FileNameStorage);
221 std::move(
ToolAction), Files.get(), std::move(PCHContainerOps));
222 return Invocation.
run();
226 std::unique_ptr<FrontendAction>
ToolAction,
const Twine &Code,
227 const std::vector<std::string> &Args,
const Twine &
FileName,
228 const Twine &ToolName,
229 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
231 auto OverlayFileSystem =
232 llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(
233 llvm::vfs::getRealFileSystem());
234 auto InMemoryFileSystem =
235 llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
236 OverlayFileSystem->pushOverlay(InMemoryFileSystem);
239 InMemoryFileSystem->addFile(
FileName, 0,
240 llvm::MemoryBuffer::getMemBuffer(
241 Code.toNullTerminatedStringRef(CodeStorage)));
243 for (
auto &FilenameWithContent : VirtualMappedFiles) {
244 InMemoryFileSystem->addFile(
245 FilenameWithContent.first, 0,
246 llvm::MemoryBuffer::getMemBuffer(FilenameWithContent.second));
255 StringRef RelativePath(
File);
257 RelativePath.consume_front(
"./");
260 if (
auto EC = FS.makeAbsolute(AbsolutePath))
261 return llvm::errorCodeToError(EC);
262 llvm::sys::path::native(AbsolutePath);
263 return std::string(AbsolutePath);
271 StringRef InvokedAs) {
272 if (CommandLine.empty() || InvokedAs.empty())
276 StringRef TargetOPT = Table.getOption(options::OPT_target).getPrefixedName();
278 StringRef TargetOPTLegacy =
279 Table.getOption(options::OPT_target_legacy_spelling).getPrefixedName();
281 StringRef DriverModeOPT =
282 Table.getOption(options::OPT_driver_mode).getPrefixedName();
286 bool ShouldAddTarget = TargetMode.TargetIsValid;
287 bool ShouldAddMode = TargetMode.DriverMode !=
nullptr;
289 for (
auto Token = ++CommandLine.begin();
Token != CommandLine.end();
291 StringRef TokenRef(*
Token);
292 ShouldAddTarget = ShouldAddTarget && !TokenRef.starts_with(TargetOPT) &&
293 TokenRef != TargetOPTLegacy;
294 ShouldAddMode = ShouldAddMode && !TokenRef.starts_with(DriverModeOPT);
297 CommandLine.insert(++CommandLine.begin(), TargetMode.DriverMode);
299 if (ShouldAddTarget) {
300 CommandLine.insert(++CommandLine.begin(),
301 (TargetOPT + TargetMode.TargetPrefix).str());
306 llvm::StringRef WorkingDir,
307 llvm::cl::TokenizerCallback Tokenizer,
308 llvm::vfs::FileSystem &FS) {
309 bool SeenRSPFile =
false;
311 Argv.reserve(CommandLine.size());
312 for (
auto &Arg : CommandLine) {
313 Argv.push_back(Arg.c_str());
315 SeenRSPFile |= Arg.front() ==
'@';
319 llvm::BumpPtrAllocator Alloc;
320 llvm::cl::ExpansionContext ECtx(Alloc, Tokenizer);
322 ECtx.setVFS(&FS).setCurrentDir(WorkingDir).expandResponseFiles(Argv);
326 std::vector<std::string> ExpandedArgv(Argv.begin(), Argv.end());
327 CommandLine = std::move(ExpandedArgv);
336 std::unique_ptr<FrontendAction> Action;
339 SingleFrontendActionFactory(std::unique_ptr<FrontendAction> Action)
340 : Action(std::move(Action)) {}
342 std::unique_ptr<FrontendAction>
create()
override {
343 return std::move(Action);
350 std::vector<std::string> CommandLine,
ToolAction *Action,
351 FileManager *Files, std::shared_ptr<PCHContainerOperations> PCHContainerOps)
352 : CommandLine(
std::move(CommandLine)), Action(Action), OwnsAction(
false),
353 Files(Files), PCHContainerOps(
std::move(PCHContainerOps)) {}
356 std::vector<std::string> CommandLine,
357 std::unique_ptr<FrontendAction> FAction,
FileManager *Files,
358 std::shared_ptr<PCHContainerOperations> PCHContainerOps)
359 : CommandLine(
std::move(CommandLine)),
360 Action(new SingleFrontendActionFactory(
std::move(FAction))),
361 OwnsAction(
true), Files(Files),
362 PCHContainerOps(
std::move(PCHContainerOps)) {}
370 llvm::opt::ArgStringList Argv;
371 for (
const std::string &Str : CommandLine)
372 Argv.push_back(Str.c_str());
373 const char *
const BinaryName = Argv[0];
377 std::unique_ptr<DiagnosticOptions> ParsedDiagOpts;
381 DiagOpts = &*ParsedDiagOpts;
387 Files->getVirtualFileSystem(), *DiagOpts,
388 DiagConsumer ? DiagConsumer : &DiagnosticPrinter,
false);
392 Diagnostics->setSourceManager(&
SrcMgr);
395 if (CommandLine.size() >= 2 && CommandLine[1] ==
"-cc1") {
397 std::unique_ptr<CompilerInvocation> Invocation(
399 if (Diagnostics->hasErrorOccurred())
401 return Action->runInvocation(std::move(Invocation), Files,
402 std::move(PCHContainerOps), DiagConsumer);
405 const std::unique_ptr<driver::Driver> Driver(
406 newDriver(&*Diagnostics, BinaryName, Files->getVirtualFileSystemPtr()));
411 if (!Files->getFileSystemOpts().WorkingDir.empty())
412 Driver->setCheckInputsExist(
false);
413 const std::unique_ptr<driver::Compilation> Compilation(
417 const llvm::opt::ArgStringList *
const CC1Args =
421 std::unique_ptr<CompilerInvocation> Invocation(
423 return runInvocation(BinaryName, Compilation.get(), std::move(Invocation),
424 std::move(PCHContainerOps));
427bool ToolInvocation::runInvocation(
429 std::shared_ptr<CompilerInvocation> Invocation,
430 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
432 if (Invocation->getHeaderSearchOpts().Verbose) {
433 llvm::errs() <<
"clang Invocation:\n";
435 llvm::errs() <<
"\n";
439 std::move(PCHContainerOps), DiagConsumer);
443 std::shared_ptr<CompilerInvocation> Invocation,
FileManager *Files,
444 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
447 CompilerInstance Compiler(std::move(Invocation), std::move(PCHContainerOps));
456 std::unique_ptr<FrontendAction> ScopedToolAction(
create());
466 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
469 : Compilations(Compilations), SourcePaths(SourcePaths),
470 PCHContainerOps(
std::move(PCHContainerOps)),
471 OverlayFileSystem(
llvm::makeIntrusiveRefCnt<
llvm::
vfs::OverlayFileSystem>(
474 llvm::makeIntrusiveRefCnt<
llvm::
vfs::InMemoryFileSystem>()),
477 OverlayFileSystem)) {
478 OverlayFileSystem->pushOverlay(InMemoryFileSystem);
483 Files->setVirtualFileSystem(OverlayFileSystem);
489 MappedFileContents.push_back(std::make_pair(FilePath, Content));
493 ArgsAdjuster =
combineAdjusters(std::move(ArgsAdjuster), std::move(Adjuster));
501 for (StringRef Arg : Args)
502 if (Arg.starts_with(
"-resource-dir"))
513 static int StaticSymbol;
517 if (SeenWorkingDirectories.insert(
"/").second)
518 for (
const auto &MappedFile : MappedFileContents)
519 if (llvm::sys::path::is_absolute(MappedFile.first))
520 InMemoryFileSystem->addFile(
522 llvm::MemoryBuffer::getMemBuffer(MappedFile.second));
524 bool ProcessingFailed =
false;
525 bool FileSkipped =
false;
528 std::vector<std::string> AbsolutePaths;
529 AbsolutePaths.reserve(SourcePaths.size());
530 for (
const auto &SourcePath : SourcePaths) {
533 llvm::errs() <<
"Skipping " << SourcePath
534 <<
". Error while getting an absolute path: "
535 << llvm::toString(AbsPath.takeError()) <<
"\n";
538 AbsolutePaths.push_back(std::move(*AbsPath));
542 std::string InitialWorkingDir;
543 if (
auto CWD = OverlayFileSystem->getCurrentWorkingDirectory()) {
544 InitialWorkingDir = std::move(*CWD);
546 llvm::errs() <<
"Could not get working directory: "
547 << CWD.getError().message() <<
"\n";
550 size_t NumOfTotalFiles = AbsolutePaths.size();
551 unsigned CurrentFileIndex = 0;
552 for (llvm::StringRef
File : AbsolutePaths) {
561 std::vector<CompileCommand> CompileCommandsForFile =
562 Compilations.getCompileCommands(
File);
563 if (CompileCommandsForFile.empty()) {
564 llvm::errs() <<
"Skipping " <<
File <<
". Compile command not found.\n";
568 unsigned CurrentCommandIndexForFile = 0;
573 if (Directory.empty()) {
574 llvm::errs() <<
"'directory' field of compilation database is empty; "
575 "using the current working directory instead.\n";
576 Directory = InitialWorkingDir;
586 if (OverlayFileSystem->setCurrentWorkingDirectory(Directory))
587 llvm::report_fatal_error(
"Cannot chdir into \"" + Twine(Directory) +
593 if (SeenWorkingDirectories.insert(Directory).second)
594 for (
const auto &MappedFile : MappedFileContents)
595 if (!llvm::sys::path::is_absolute(MappedFile.first))
596 InMemoryFileSystem->addFile(
598 llvm::MemoryBuffer::getMemBuffer(MappedFile.second));
603 assert(!CommandLine.empty());
615 ++CurrentCommandIndexForFile;
619 if (NumOfTotalFiles > 1 || CompileCommandsForFile.size() > 1) {
620 llvm::errs() <<
"[" << std::to_string(CurrentFileIndex) <<
"/"
621 << std::to_string(NumOfTotalFiles) <<
"]";
622 if (CompileCommandsForFile.size() > 1) {
623 llvm::errs() <<
" (" << std::to_string(CurrentCommandIndexForFile)
624 <<
"/" << std::to_string(CompileCommandsForFile.size())
627 llvm::errs() <<
" Processing file " <<
File <<
".\n";
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 createDiagnostics(DiagnosticConsumer *Client=nullptr, bool ShouldOwnClient=true)
Create the diagnostics engine using the invocation's diagnostic options and replace any existing one ...
void setVirtualFileSystem(IntrusiveRefCntPtr< llvm::vfs::FileSystem > FS)
Use the given file system.
void createSourceManager()
Create the source manager and replace any existing one with it.
void setFileManager(IntrusiveRefCntPtr< FileManager > Value)
Replace the current file manager.
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 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
llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > getVirtualFileSystemPtr() 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.
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.
std::string GetResourcesPath(StringRef BinaryPath)
Get the directory where the compiler headers reside, relative to the compiler binary path BinaryPath.
const llvm::opt::OptTable & getDriverOptTable()
Diagnostic wrappers for TextAPI types for error reporting.