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 if (Actions.size() > 1) {
119 OffloadCompilation =
true;
125 return OffloadCompilation;
131const llvm::opt::ArgStringList *
137 return StringRef(Cmd.getCreator().getName()) ==
"clang";
141 return isSrcFile(II.getType());
146 if (IsCC1Command(Job) && llvm::all_of(Job.getInputInfos(), IsSrcFile))
147 CC1Jobs.push_back(&Job);
153 if (IsCC1Command(Job))
154 CC1Jobs.push_back(&Job);
156 if (CC1Jobs.empty() ||
159 llvm::raw_svector_ostream error_stream(error_msg);
160 Jobs.
Print(error_stream,
"; ",
true);
161 Diagnostics->
Report(diag::err_fe_expected_compiler_job)
162 << error_stream.str();
166 return &CC1Jobs[0]->getArguments();
172 const char *
const BinaryName) {
173 assert(!CC1Args.empty() &&
"Must at least contain the program name!");
183 const Twine &Code,
const Twine &
FileName,
184 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
186 std::vector<std::string>(),
FileName,
187 "clang-tool", std::move(PCHContainerOps));
193static std::vector<std::string>
195 const std::vector<std::string> &ExtraArgs,
197 std::vector<std::string> Args;
198 Args.push_back(ToolName.str());
199 Args.push_back(
"-fsyntax-only");
200 llvm::append_range(Args, ExtraArgs);
209 std::unique_ptr<FrontendAction>
ToolAction,
const Twine &Code,
211 const std::vector<std::string> &Args,
const Twine &
FileName,
212 const Twine &ToolName,
213 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
215 StringRef FileNameRef =
FileName.toNullTerminatedStringRef(FileNameStorage);
222 std::move(
ToolAction), Files.get(), std::move(PCHContainerOps));
223 return Invocation.
run();
227 std::unique_ptr<FrontendAction>
ToolAction,
const Twine &Code,
228 const std::vector<std::string> &Args,
const Twine &
FileName,
229 const Twine &ToolName,
230 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
232 auto OverlayFileSystem =
233 llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(
234 llvm::vfs::getRealFileSystem());
235 auto InMemoryFileSystem =
236 llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
237 OverlayFileSystem->pushOverlay(InMemoryFileSystem);
240 InMemoryFileSystem->addFile(
FileName, 0,
241 llvm::MemoryBuffer::getMemBuffer(
242 Code.toNullTerminatedStringRef(CodeStorage)));
244 for (
auto &FilenameWithContent : VirtualMappedFiles) {
245 InMemoryFileSystem->addFile(
246 FilenameWithContent.first, 0,
247 llvm::MemoryBuffer::getMemBuffer(FilenameWithContent.second));
256 StringRef RelativePath(
File);
258 RelativePath.consume_front(
"./");
261 if (
auto EC = FS.makeAbsolute(AbsolutePath))
262 return llvm::errorCodeToError(EC);
263 llvm::sys::path::native(AbsolutePath);
264 return std::string(AbsolutePath);
272 StringRef InvokedAs) {
273 if (CommandLine.empty() || InvokedAs.empty())
277 StringRef TargetOPT = Table.getOption(options::OPT_target).getPrefixedName();
279 StringRef TargetOPTLegacy =
280 Table.getOption(options::OPT_target_legacy_spelling).getPrefixedName();
282 StringRef DriverModeOPT =
283 Table.getOption(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(
418 const llvm::opt::ArgStringList *
const CC1Args =
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));
457 std::unique_ptr<FrontendAction> ScopedToolAction(
create());
467 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
470 : Compilations(Compilations), SourcePaths(SourcePaths),
471 PCHContainerOps(
std::move(PCHContainerOps)),
472 OverlayFileSystem(
llvm::makeIntrusiveRefCnt<
llvm::
vfs::OverlayFileSystem>(
475 llvm::makeIntrusiveRefCnt<
llvm::
vfs::InMemoryFileSystem>()),
478 OverlayFileSystem)) {
479 OverlayFileSystem->pushOverlay(InMemoryFileSystem);
484 Files->setVirtualFileSystem(OverlayFileSystem);
490 MappedFileContents.push_back(std::make_pair(FilePath, Content));
494 ArgsAdjuster =
combineAdjusters(std::move(ArgsAdjuster), std::move(Adjuster));
502 for (StringRef Arg : Args)
503 if (Arg.starts_with(
"-resource-dir"))
514 static int StaticSymbol;
518 if (SeenWorkingDirectories.insert(
"/").second)
519 for (
const auto &MappedFile : MappedFileContents)
520 if (llvm::sys::path::is_absolute(MappedFile.first))
521 InMemoryFileSystem->addFile(
523 llvm::MemoryBuffer::getMemBuffer(MappedFile.second));
525 bool ProcessingFailed =
false;
526 bool FileSkipped =
false;
529 std::vector<std::string> AbsolutePaths;
530 AbsolutePaths.reserve(SourcePaths.size());
531 for (
const auto &SourcePath : SourcePaths) {
534 llvm::errs() <<
"Skipping " << SourcePath
535 <<
". Error while getting an absolute path: "
536 << llvm::toString(AbsPath.takeError()) <<
"\n";
539 AbsolutePaths.push_back(std::move(*AbsPath));
543 std::string InitialWorkingDir;
544 if (
auto CWD = OverlayFileSystem->getCurrentWorkingDirectory()) {
545 InitialWorkingDir = std::move(*CWD);
547 llvm::errs() <<
"Could not get working directory: "
548 << CWD.getError().message() <<
"\n";
551 size_t NumOfTotalFiles = AbsolutePaths.size();
552 unsigned CurrentFileIndex = 0;
553 for (llvm::StringRef
File : AbsolutePaths) {
562 std::vector<CompileCommand> CompileCommandsForFile =
563 Compilations.getCompileCommands(
File);
564 if (CompileCommandsForFile.empty()) {
565 llvm::errs() <<
"Skipping " <<
File <<
". Compile command not found.\n";
569 unsigned CurrentCommandIndexForFile = 0;
574 if (Directory.empty()) {
575 llvm::errs() <<
"'directory' field of compilation database is empty; "
576 "using the current working directory instead.\n";
577 Directory = InitialWorkingDir;
587 if (OverlayFileSystem->setCurrentWorkingDirectory(Directory))
588 llvm::report_fatal_error(
"Cannot chdir into \"" + Twine(Directory) +
594 if (SeenWorkingDirectories.insert(Directory).second)
595 for (
const auto &MappedFile : MappedFileContents)
596 if (!llvm::sys::path::is_absolute(MappedFile.first))
597 InMemoryFileSystem->addFile(
599 llvm::MemoryBuffer::getMemBuffer(MappedFile.second));
604 assert(!CommandLine.empty());
616 ++CurrentCommandIndexForFile;
620 if (NumOfTotalFiles > 1 || CompileCommandsForFile.size() > 1) {
621 llvm::errs() <<
"[" << std::to_string(CurrentFileIndex) <<
"/"
622 << std::to_string(NumOfTotalFiles) <<
"]";
623 if (CompileCommandsForFile.size() > 1) {
624 llvm::errs() <<
" (" << std::to_string(CurrentCommandIndexForFile)
625 <<
"/" << std::to_string(CompileCommandsForFile.size())
628 llvm::errs() <<
" Processing file " <<
File <<
".\n";
630 ToolInvocation Invocation(std::move(CommandLine), Action, Files.get(),
634 if (!Invocation.
run()) {
636 if (PrintErrorMessage)
637 llvm::errs() <<
"Error while processing " <<
File <<
".\n";
638 ProcessingFailed =
true;
643 if (!InitialWorkingDir.empty()) {
645 OverlayFileSystem->setCurrentWorkingDirectory(InitialWorkingDir))
646 llvm::errs() <<
"Error when trying to restore working dir: "
647 << EC.message() <<
"\n";
649 return ProcessingFailed ? 1 : (FileSkipped ? 2 : 0);
655 std::vector<std::unique_ptr<ASTUnit>> &ASTs;
660 std::vector<std::unique_ptr<ASTUnit>> &ASTs,
662 : ASTs(ASTs), CaptureKind(CaptureDiagnosticsKind) {}
664 bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
666 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
668 std::unique_ptr<ASTUnit> AST = ASTUnit::LoadFromCompilerInvocation(
669 Invocation, std::move(PCHContainerOps),
nullptr,
671 Invocation->getDiagnosticOpts(),
674 Files,
false, CaptureKind);
678 ASTs.push_back(std::move(AST));
686 ASTBuilderAction Action(ASTs);
691 this->PrintErrorMessage = PrintErrorMessage;
697std::unique_ptr<ASTUnit>
699 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
701 "clang-tool", std::move(PCHContainerOps));
705 StringRef Code,
const std::vector<std::string> &Args, StringRef
FileName,
706 StringRef ToolName, std::shared_ptr<PCHContainerOperations> PCHContainerOps,
711 std::vector<std::unique_ptr<ASTUnit>> ASTs;
713 ASTBuilderAction Action(ASTs, CaptureKind);
715 auto OverlayFileSystem =
716 llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(
718 auto InMemoryFileSystem =
719 llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
720 OverlayFileSystem->pushOverlay(InMemoryFileSystem);
727 &Action, Files.get(), std::move(PCHContainerOps));
730 InMemoryFileSystem->addFile(
FileName, 0,
731 llvm::MemoryBuffer::getMemBufferCopy(Code));
732 for (
auto &FilenameWithContent : VirtualMappedFiles) {
733 InMemoryFileSystem->addFile(
734 FilenameWithContent.first, 0,
735 llvm::MemoryBuffer::getMemBuffer(FilenameWithContent.second));
738 if (!Invocation.
run())
741 assert(ASTs.size() == 1);
742 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.