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));
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));
506 for (StringRef Arg : Args)
507 if (Arg.starts_with(
"-resource-dir"))
518 static int StaticSymbol;
522 if (SeenWorkingDirectories.insert(
"/").second)
523 for (
const auto &MappedFile : MappedFileContents)
524 if (llvm::sys::path::is_absolute(MappedFile.first))
525 InMemoryFileSystem->addFile(
527 llvm::MemoryBuffer::getMemBuffer(MappedFile.second));
529 bool ProcessingFailed =
false;
530 bool FileSkipped =
false;
533 std::vector<std::string> AbsolutePaths;
534 AbsolutePaths.reserve(SourcePaths.size());
535 for (
const auto &SourcePath : SourcePaths) {
538 llvm::errs() <<
"Skipping " << SourcePath
539 <<
". Error while getting an absolute path: "
540 << llvm::toString(AbsPath.takeError()) <<
"\n";
543 AbsolutePaths.push_back(std::move(*AbsPath));
547 std::string InitialWorkingDir;
548 if (
auto CWD = OverlayFileSystem->getCurrentWorkingDirectory()) {
549 InitialWorkingDir = std::move(*CWD);
551 llvm::errs() <<
"Could not get working directory: "
552 << CWD.getError().message() <<
"\n";
555 size_t NumOfTotalFiles = AbsolutePaths.size();
556 unsigned CurrentFileIndex = 0;
557 for (llvm::StringRef
File : AbsolutePaths) {
566 std::vector<CompileCommand> CompileCommandsForFile =
567 Compilations.getCompileCommands(
File);
568 if (CompileCommandsForFile.empty()) {
569 llvm::errs() <<
"Skipping " <<
File <<
". Compile command not found.\n";
573 unsigned CurrentCommandIndexForFile = 0;
578 if (Directory.empty()) {
579 llvm::errs() <<
"'directory' field of compilation database is empty; "
580 "using the current working directory instead.\n";
581 Directory = InitialWorkingDir;
591 if (OverlayFileSystem->setCurrentWorkingDirectory(Directory))
592 llvm::report_fatal_error(
"Cannot chdir into \"" + Twine(Directory) +
598 if (SeenWorkingDirectories.insert(Directory).second)
599 for (
const auto &MappedFile : MappedFileContents)
600 if (!llvm::sys::path::is_absolute(MappedFile.first))
601 InMemoryFileSystem->addFile(
603 llvm::MemoryBuffer::getMemBuffer(MappedFile.second));
608 assert(!CommandLine.empty());
620 ++CurrentCommandIndexForFile;
624 if (NumOfTotalFiles > 1 || CompileCommandsForFile.size() > 1) {
625 llvm::errs() <<
"[" << std::to_string(CurrentFileIndex) <<
"/"
626 << std::to_string(NumOfTotalFiles) <<
"]";
627 if (CompileCommandsForFile.size() > 1) {
628 llvm::errs() <<
" (" << std::to_string(CurrentCommandIndexForFile)
629 <<
"/" << std::to_string(CompileCommandsForFile.size())
632 llvm::errs() <<
" Processing file " <<
File <<
".\n";
634 ToolInvocation Invocation(std::move(CommandLine), Action, Files.get(),
638 if (!Invocation.
run()) {
640 if (PrintErrorMessage)
641 llvm::errs() <<
"Error while processing " <<
File <<
".\n";
642 ProcessingFailed =
true;
647 if (!InitialWorkingDir.empty()) {
649 OverlayFileSystem->setCurrentWorkingDirectory(InitialWorkingDir))
650 llvm::errs() <<
"Error when trying to restore working dir: "
651 << EC.message() <<
"\n";
653 return ProcessingFailed ? 1 : (FileSkipped ? 2 : 0);
659 std::vector<std::unique_ptr<ASTUnit>> &ASTs;
664 std::vector<std::unique_ptr<ASTUnit>> &ASTs,
666 : ASTs(ASTs), CaptureKind(CaptureDiagnosticsKind) {}
668 bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
670 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
672 std::unique_ptr<ASTUnit> AST = ASTUnit::LoadFromCompilerInvocation(
673 Invocation, std::move(PCHContainerOps),
nullptr,
675 Invocation->getDiagnosticOpts(),
678 Files,
false, CaptureKind);
682 ASTs.push_back(std::move(AST));
690 ASTBuilderAction Action(ASTs);
695 this->PrintErrorMessage = PrintErrorMessage;
701std::unique_ptr<ASTUnit>
703 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
705 "clang-tool", std::move(PCHContainerOps));
709 StringRef Code,
const std::vector<std::string> &Args, StringRef
FileName,
710 StringRef ToolName, std::shared_ptr<PCHContainerOperations> PCHContainerOps,
715 std::vector<std::unique_ptr<ASTUnit>> ASTs;
717 ASTBuilderAction Action(ASTs, CaptureKind);
719 auto OverlayFileSystem =
720 llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(
722 auto InMemoryFileSystem =
723 llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
724 OverlayFileSystem->pushOverlay(InMemoryFileSystem);
731 &Action, Files.get(), std::move(PCHContainerOps));
734 InMemoryFileSystem->addFile(
FileName, 0,
735 llvm::MemoryBuffer::getMemBufferCopy(Code));
736 for (
auto &FilenameWithContent : VirtualMappedFiles) {
737 InMemoryFileSystem->addFile(
738 FilenameWithContent.first, 0,
739 llvm::MemoryBuffer::getMemBuffer(FilenameWithContent.second));
742 if (!Invocation.
run())
745 assert(ASTs.size() == 1);
746 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.
bool hasDiagnostics() const
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.