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"
63using namespace 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){
101 if (isa<driver::BindArchAction>(A))
102 A = *A->input_begin();
103 if (isa<driver::OffloadAction>(A)) {
110 assert(Actions.size() > 1);
112 isa<driver::CompileJobAction>(Actions.front()) ||
115 (isa<driver::BindArchAction>(Actions.front()) &&
116 isa<driver::CompileJobAction>(*Actions.front()->input_begin())));
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;
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())
403 std::move(PCHContainerOps), DiagConsumer);
406 const std::unique_ptr<driver::Driver> Driver(
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 =
571 if (CompileCommandsForFile.empty()) {
572 llvm::errs() <<
"Skipping " <<
File <<
". Compile command not found.\n";
584 if (OverlayFileSystem->setCurrentWorkingDirectory(
586 llvm::report_fatal_error(
"Cannot chdir into \"" +
593 for (
const auto &MappedFile : MappedFileContents)
594 if (!llvm::sys::path::is_absolute(MappedFile.first))
595 InMemoryFileSystem->addFile(
597 llvm::MemoryBuffer::getMemBuffer(MappedFile.second));
602 assert(!CommandLine.empty());
616 if (NumOfTotalFiles > 1)
617 llvm::errs() <<
"[" + std::to_string(++ProcessedFileCounter) +
"/" +
618 std::to_string(NumOfTotalFiles) +
619 "] Processing file " +
File
621 ToolInvocation Invocation(std::move(CommandLine), Action, Files.get(),
625 if (!Invocation.
run()) {
627 if (PrintErrorMessage)
628 llvm::errs() <<
"Error while processing " <<
File <<
".\n";
629 ProcessingFailed =
true;
634 if (!InitialWorkingDir.empty()) {
636 OverlayFileSystem->setCurrentWorkingDirectory(InitialWorkingDir))
637 llvm::errs() <<
"Error when trying to restore working dir: "
638 << EC.message() <<
"\n";
640 return ProcessingFailed ? 1 : (FileSkipped ? 2 : 0);
646 std::vector<std::unique_ptr<ASTUnit>> &ASTs;
651 std::vector<std::unique_ptr<ASTUnit>> &ASTs,
653 : ASTs(ASTs), CaptureKind(CaptureDiagnosticsKind) {}
655 bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
657 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
659 std::unique_ptr<ASTUnit> AST = ASTUnit::LoadFromCompilerInvocation(
660 Invocation, std::move(PCHContainerOps),
nullptr,
662 Invocation->getDiagnosticOpts(),
665 Files,
false, CaptureKind);
669 ASTs.push_back(std::move(AST));
677 ASTBuilderAction Action(ASTs);
682 this->PrintErrorMessage = PrintErrorMessage;
688std::unique_ptr<ASTUnit>
690 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
692 "clang-tool", std::move(PCHContainerOps));
696 StringRef Code,
const std::vector<std::string> &Args, StringRef
FileName,
697 StringRef ToolName, std::shared_ptr<PCHContainerOperations> PCHContainerOps,
702 std::vector<std::unique_ptr<ASTUnit>> ASTs;
704 ASTBuilderAction Action(ASTs, CaptureKind);
706 auto OverlayFileSystem =
707 llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(
709 auto InMemoryFileSystem =
710 llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
711 OverlayFileSystem->pushOverlay(InMemoryFileSystem);
718 &Action, Files.get(), std::move(PCHContainerOps));
721 InMemoryFileSystem->addFile(
FileName, 0,
722 llvm::MemoryBuffer::getMemBufferCopy(Code));
723 for (
auto &FilenameWithContent : VirtualMappedFiles) {
724 InMemoryFileSystem->addFile(
725 FilenameWithContent.first, 0,
726 llvm::MemoryBuffer::getMemBuffer(FilenameWithContent.second));
729 if (!Invocation.
run())
732 assert(ASTs.size() == 1);
733 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
FileSystemOptions & getFileSystemOpts()
Returns the current file system options.
llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > getVirtualFileSystemPtr() const
Keeps track of options that affect how file operations are performed.
std::string WorkingDir
If set, paths are resolved as if the working directory was set to the value of WorkingDir.
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
const llvm::opt::OptTable & getDriverOptTable()
The JSON file list parser is used to communicate input to InstallAPI.
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.