37#include "llvm/ADT/ArrayRef.h"
38#include "llvm/ADT/IntrusiveRefCntPtr.h"
39#include "llvm/ADT/SmallString.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/Casting.h"
46#include "llvm/Support/CommandLine.h"
47#include "llvm/Support/Debug.h"
48#include "llvm/Support/ErrorHandling.h"
49#include "llvm/Support/FileSystem.h"
50#include "llvm/Support/MemoryBuffer.h"
51#include "llvm/Support/Path.h"
52#include "llvm/Support/VirtualFileSystem.h"
53#include "llvm/Support/raw_ostream.h"
54#include "llvm/TargetParser/Host.h"
59#include <system_error>
63#define DEBUG_TYPE "clang-tooling"
66using namespace tooling;
81 new driver::Driver(BinaryName, llvm::sys::getDefaultTargetTriple(),
82 *Diagnostics,
"clang LLVM compiler", std::move(VFS));
83 CompilerDriver->
setTitle(
"clang_based_tool");
84 return CompilerDriver;
92 bool OffloadCompilation =
false;
97 for (
const auto &Job : Jobs)
98 if (StringRef(Job.getExecutable()) ==
"clang-offload-bundler")
99 OffloadCompilation =
true;
101 if (Jobs.
size() > 1) {
102 for (
auto *A : Actions){
104 if (isa<driver::BindArchAction>(A))
105 A = *A->input_begin();
106 if (isa<driver::OffloadAction>(A)) {
113 assert(Actions.size() > 1);
115 isa<driver::CompileJobAction>(Actions.front()) ||
118 (isa<driver::BindArchAction>(Actions.front()) &&
119 isa<driver::CompileJobAction>(*Actions.front()->input_begin())));
120 OffloadCompilation =
true;
126 return OffloadCompilation;
132const llvm::opt::ArgStringList *
138 return StringRef(
Cmd.getCreator().getName()) ==
"clang";
142 return isSrcFile(II.getType());
147 if (IsCC1Command(Job) && llvm::all_of(Job.getInputInfos(), IsSrcFile))
148 CC1Jobs.push_back(&Job);
154 if (IsCC1Command(Job))
155 CC1Jobs.push_back(&Job);
157 if (CC1Jobs.empty() ||
160 llvm::raw_svector_ostream error_stream(error_msg);
161 Jobs.
Print(error_stream,
"; ",
true);
162 Diagnostics->
Report(diag::err_fe_expected_compiler_job)
163 << error_stream.str();
167 return &CC1Jobs[0]->getArguments();
173 const char *
const BinaryName) {
174 assert(!CC1Args.empty() &&
"Must at least contain the program name!");
184 const Twine &Code,
const Twine &
FileName,
185 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
187 std::vector<std::string>(),
FileName,
188 "clang-tool", std::move(PCHContainerOps));
194static std::vector<std::string>
196 const std::vector<std::string> &ExtraArgs,
198 std::vector<std::string> Args;
199 Args.push_back(ToolName.str());
200 Args.push_back(
"-fsyntax-only");
201 Args.insert(Args.end(), ExtraArgs.begin(), ExtraArgs.end());
210 std::unique_ptr<FrontendAction>
ToolAction,
const Twine &Code,
212 const std::vector<std::string> &Args,
const Twine &
FileName,
213 const Twine &ToolName,
214 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
216 StringRef FileNameRef =
FileName.toNullTerminatedStringRef(FileNameStorage);
223 std::move(
ToolAction), Files.get(), std::move(PCHContainerOps));
224 return Invocation.
run();
228 std::unique_ptr<FrontendAction>
ToolAction,
const Twine &Code,
229 const std::vector<std::string> &Args,
const Twine &
FileName,
230 const Twine &ToolName,
231 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
234 new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
236 new 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 if (RelativePath.startswith(
"./")) {
259 RelativePath = RelativePath.substr(strlen(
"./"));
263 if (
auto EC = FS.makeAbsolute(AbsolutePath))
264 return llvm::errorCodeToError(EC);
265 llvm::sys::path::native(AbsolutePath);
266 return std::string(AbsolutePath.str());
274 StringRef InvokedAs) {
275 if (CommandLine.empty() || InvokedAs.empty())
279 StringRef TargetOPT =
280 Table.getOption(driver::options::OPT_target).getPrefixedName();
282 StringRef TargetOPTLegacy =
283 Table.getOption(driver::options::OPT_target_legacy_spelling)
286 StringRef DriverModeOPT =
287 Table.getOption(driver::options::OPT_driver_mode).getPrefixedName();
291 bool ShouldAddTarget = TargetMode.TargetIsValid;
292 bool ShouldAddMode = TargetMode.DriverMode !=
nullptr;
294 for (
auto Token = ++CommandLine.begin();
Token != CommandLine.end();
296 StringRef TokenRef(*
Token);
297 ShouldAddTarget = ShouldAddTarget && !TokenRef.startswith(TargetOPT) &&
298 !TokenRef.equals(TargetOPTLegacy);
299 ShouldAddMode = ShouldAddMode && !TokenRef.startswith(DriverModeOPT);
302 CommandLine.insert(++CommandLine.begin(), TargetMode.DriverMode);
304 if (ShouldAddTarget) {
305 CommandLine.insert(++CommandLine.begin(),
306 (TargetOPT + TargetMode.TargetPrefix).str());
311 llvm::StringRef WorkingDir,
312 llvm::cl::TokenizerCallback Tokenizer,
313 llvm::vfs::FileSystem &FS) {
314 bool SeenRSPFile =
false;
316 Argv.reserve(CommandLine.size());
317 for (
auto &Arg : CommandLine) {
318 Argv.push_back(Arg.c_str());
320 SeenRSPFile |= Arg.front() ==
'@';
324 llvm::BumpPtrAllocator Alloc;
325 llvm::cl::ExpansionContext ECtx(Alloc, Tokenizer);
327 ECtx.setVFS(&FS).setCurrentDir(WorkingDir).expandResponseFiles(Argv);
331 std::vector<std::string> ExpandedArgv(Argv.begin(), Argv.end());
332 CommandLine = std::move(ExpandedArgv);
341 std::unique_ptr<FrontendAction> Action;
344 SingleFrontendActionFactory(std::unique_ptr<FrontendAction> Action)
345 : Action(
std::move(Action)) {}
347 std::unique_ptr<FrontendAction>
create()
override {
348 return std::move(Action);
355 std::vector<std::string> CommandLine,
ToolAction *Action,
356 FileManager *Files, std::shared_ptr<PCHContainerOperations> PCHContainerOps)
357 : CommandLine(
std::move(CommandLine)), Action(Action), OwnsAction(
false),
358 Files(Files), PCHContainerOps(
std::move(PCHContainerOps)) {}
361 std::vector<std::string> CommandLine,
362 std::unique_ptr<FrontendAction> FAction,
FileManager *Files,
363 std::shared_ptr<PCHContainerOperations> PCHContainerOps)
364 : CommandLine(
std::move(CommandLine)),
365 Action(new SingleFrontendActionFactory(
std::move(FAction))),
366 OwnsAction(
true), Files(Files),
367 PCHContainerOps(
std::move(PCHContainerOps)) {}
375 llvm::opt::ArgStringList Argv;
376 for (
const std::string &Str : CommandLine)
377 Argv.push_back(Str.c_str());
378 const char *
const BinaryName = Argv[0];
386 DiagOpts = &*ParsedDiagOpts;
392 &*DiagOpts, DiagConsumer ? DiagConsumer : &DiagnosticPrinter,
false);
396 Diagnostics->setSourceManager(&SrcMgr);
399 if (CommandLine.size() >= 2 && CommandLine[1] ==
"-cc1") {
401 std::unique_ptr<CompilerInvocation> Invocation(
403 if (Diagnostics->hasErrorOccurred())
406 std::move(PCHContainerOps), DiagConsumer);
409 const std::unique_ptr<driver::Driver> Driver(
416 Driver->setCheckInputsExist(
false);
417 const std::unique_ptr<driver::Compilation> Compilation(
422 &*Diagnostics, Compilation.get());
425 std::unique_ptr<CompilerInvocation> Invocation(
427 return runInvocation(BinaryName, Compilation.get(), std::move(Invocation),
428 std::move(PCHContainerOps));
431bool ToolInvocation::runInvocation(
433 std::shared_ptr<CompilerInvocation> Invocation,
434 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
436 if (Invocation->getHeaderSearchOpts().Verbose) {
437 llvm::errs() <<
"clang Invocation:\n";
439 llvm::errs() <<
"\n";
443 std::move(PCHContainerOps), DiagConsumer);
447 std::shared_ptr<CompilerInvocation> Invocation,
FileManager *Files,
448 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
458 std::unique_ptr<FrontendAction> ScopedToolAction(
create());
467 const bool Success = Compiler.
ExecuteAction(*ScopedToolAction);
475 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
478 : Compilations(Compilations), SourcePaths(SourcePaths),
479 PCHContainerOps(
std::move(PCHContainerOps)),
480 OverlayFileSystem(new
llvm::vfs::OverlayFileSystem(
std::move(BaseFS))),
481 InMemoryFileSystem(new
llvm::vfs::InMemoryFileSystem),
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.startswith(
"-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 for (llvm::StringRef
File : AbsolutePaths) {
567 std::vector<CompileCommand> CompileCommandsForFile =
569 if (CompileCommandsForFile.empty()) {
570 llvm::errs() <<
"Skipping " <<
File <<
". Compile command not found.\n";
582 if (OverlayFileSystem->setCurrentWorkingDirectory(
584 llvm::report_fatal_error(
"Cannot chdir into \"" +
591 for (
const auto &MappedFile : MappedFileContents)
592 if (!llvm::sys::path::is_absolute(MappedFile.first))
593 InMemoryFileSystem->addFile(
595 llvm::MemoryBuffer::getMemBuffer(MappedFile.second));
600 assert(!CommandLine.empty());
614 LLVM_DEBUG({ llvm::dbgs() <<
"Processing: " <<
File <<
".\n"; });
615 ToolInvocation Invocation(std::move(CommandLine), Action, Files.get(),
617 Invocation.setDiagnosticConsumer(DiagConsumer);
619 if (!Invocation.run()) {
621 if (PrintErrorMessage)
622 llvm::errs() <<
"Error while processing " <<
File <<
".\n";
623 ProcessingFailed =
true;
628 if (!InitialWorkingDir.empty()) {
630 OverlayFileSystem->setCurrentWorkingDirectory(InitialWorkingDir))
631 llvm::errs() <<
"Error when trying to restore working dir: "
632 << EC.message() <<
"\n";
634 return ProcessingFailed ? 1 : (FileSkipped ? 2 : 0);
640 std::vector<std::unique_ptr<ASTUnit>> &ASTs;
643 ASTBuilderAction(std::vector<std::unique_ptr<ASTUnit>> &ASTs) : ASTs(ASTs) {}
645 bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
647 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
649 std::unique_ptr<ASTUnit> AST = ASTUnit::LoadFromCompilerInvocation(
650 Invocation, std::move(PCHContainerOps),
658 ASTs.push_back(std::move(AST));
666 ASTBuilderAction Action(ASTs);
671 this->PrintErrorMessage = PrintErrorMessage;
677std::unique_ptr<ASTUnit>
679 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
681 "clang-tool", std::move(PCHContainerOps));
685 StringRef Code,
const std::vector<std::string> &Args, StringRef
FileName,
686 StringRef ToolName, std::shared_ptr<PCHContainerOperations> PCHContainerOps,
689 std::vector<std::unique_ptr<ASTUnit>> ASTs;
690 ASTBuilderAction Action(ASTs);
692 new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
694 new llvm::vfs::InMemoryFileSystem);
695 OverlayFileSystem->pushOverlay(InMemoryFileSystem);
701 &Action, Files.get(), std::move(PCHContainerOps));
704 InMemoryFileSystem->addFile(
FileName, 0,
705 llvm::MemoryBuffer::getMemBufferCopy(Code));
706 for (
auto &FilenameWithContent : VirtualMappedFiles) {
707 InMemoryFileSystem->addFile(
708 FilenameWithContent.first, 0,
709 llvm::MemoryBuffer::getMemBuffer(FilenameWithContent.second));
712 if (!Invocation.
run())
715 assert(ASTs.size() == 1);
716 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 setFileManager(FileManager *Value)
Replace the current file manager and virtual file system.
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 ...
void setInvocation(std::shared_ptr< CompilerInvocation > Value)
setInvocation - Replace the current invocation.
bool hasDiagnostics() const
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.
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()
std::unique_ptr< DiagnosticOptions > CreateAndPopulateDiagOpts(ArrayRef< const char * > Argv)
YAML serialization mapping.