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 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 =
278 Table.getOption(driver::options::OPT_target).getPrefixedName();
280 StringRef TargetOPTLegacy =
281 Table.getOption(driver::options::OPT_target_legacy_spelling)
284 StringRef DriverModeOPT =
285 Table.getOption(driver::options::OPT_driver_mode).getPrefixedName();
289 bool ShouldAddTarget = TargetMode.TargetIsValid;
290 bool ShouldAddMode = TargetMode.DriverMode !=
nullptr;
292 for (
auto Token = ++CommandLine.begin();
Token != CommandLine.end();
294 StringRef TokenRef(*
Token);
295 ShouldAddTarget = ShouldAddTarget && !TokenRef.starts_with(TargetOPT) &&
296 TokenRef != TargetOPTLegacy;
297 ShouldAddMode = ShouldAddMode && !TokenRef.starts_with(DriverModeOPT);
300 CommandLine.insert(++CommandLine.begin(), TargetMode.DriverMode);
302 if (ShouldAddTarget) {
303 CommandLine.insert(++CommandLine.begin(),
304 (TargetOPT + TargetMode.TargetPrefix).str());
309 llvm::StringRef WorkingDir,
310 llvm::cl::TokenizerCallback Tokenizer,
311 llvm::vfs::FileSystem &FS) {
312 bool SeenRSPFile =
false;
314 Argv.reserve(CommandLine.size());
315 for (
auto &Arg : CommandLine) {
316 Argv.push_back(Arg.c_str());
318 SeenRSPFile |= Arg.front() ==
'@';
322 llvm::BumpPtrAllocator Alloc;
323 llvm::cl::ExpansionContext ECtx(Alloc, Tokenizer);
325 ECtx.setVFS(&FS).setCurrentDir(WorkingDir).expandResponseFiles(Argv);
329 std::vector<std::string> ExpandedArgv(Argv.begin(), Argv.end());
330 CommandLine = std::move(ExpandedArgv);
339 std::unique_ptr<FrontendAction> Action;
342 SingleFrontendActionFactory(std::unique_ptr<FrontendAction> Action)
343 : Action(
std::move(Action)) {}
345 std::unique_ptr<FrontendAction>
create()
override {
346 return std::move(Action);
353 std::vector<std::string> CommandLine,
ToolAction *Action,
354 FileManager *Files, std::shared_ptr<PCHContainerOperations> PCHContainerOps)
355 : CommandLine(
std::move(CommandLine)), Action(Action), OwnsAction(
false),
356 Files(Files), PCHContainerOps(
std::move(PCHContainerOps)) {}
359 std::vector<std::string> CommandLine,
360 std::unique_ptr<FrontendAction> FAction,
FileManager *Files,
361 std::shared_ptr<PCHContainerOperations> PCHContainerOps)
362 : CommandLine(
std::move(CommandLine)),
363 Action(new SingleFrontendActionFactory(
std::move(FAction))),
364 OwnsAction(
true), Files(Files),
365 PCHContainerOps(
std::move(PCHContainerOps)) {}
373 llvm::opt::ArgStringList Argv;
374 for (
const std::string &Str : CommandLine)
375 Argv.push_back(Str.c_str());
376 const char *
const BinaryName = Argv[0];
384 DiagOpts = &*ParsedDiagOpts;
390 &*DiagOpts, DiagConsumer ? DiagConsumer : &DiagnosticPrinter,
false);
394 Diagnostics->setSourceManager(&SrcMgr);
397 if (CommandLine.size() >= 2 && CommandLine[1] ==
"-cc1") {
399 std::unique_ptr<CompilerInvocation> Invocation(
401 if (Diagnostics->hasErrorOccurred())
404 std::move(PCHContainerOps), DiagConsumer);
407 const std::unique_ptr<driver::Driver> Driver(
414 Driver->setCheckInputsExist(
false);
415 const std::unique_ptr<driver::Compilation> Compilation(
420 &*Diagnostics, Compilation.get());
423 std::unique_ptr<CompilerInvocation> Invocation(
425 return runInvocation(BinaryName, Compilation.get(), std::move(Invocation),
426 std::move(PCHContainerOps));
429bool ToolInvocation::runInvocation(
431 std::shared_ptr<CompilerInvocation> Invocation,
432 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
434 if (Invocation->getHeaderSearchOpts().Verbose) {
435 llvm::errs() <<
"clang Invocation:\n";
437 llvm::errs() <<
"\n";
441 std::move(PCHContainerOps), DiagConsumer);
445 std::shared_ptr<CompilerInvocation> Invocation,
FileManager *Files,
446 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
456 std::unique_ptr<FrontendAction> ScopedToolAction(
create());
473 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
476 : Compilations(Compilations), SourcePaths(SourcePaths),
477 PCHContainerOps(
std::move(PCHContainerOps)),
478 OverlayFileSystem(new
llvm::vfs::OverlayFileSystem(
std::move(BaseFS))),
479 InMemoryFileSystem(new
llvm::vfs::InMemoryFileSystem),
482 OverlayFileSystem->pushOverlay(InMemoryFileSystem);
487 Files->setVirtualFileSystem(OverlayFileSystem);
493 MappedFileContents.push_back(std::make_pair(FilePath, Content));
497 ArgsAdjuster =
combineAdjusters(std::move(ArgsAdjuster), std::move(Adjuster));
501 ArgsAdjuster =
nullptr;
507 for (StringRef Arg : Args)
508 if (Arg.starts_with(
"-resource-dir"))
520 static int StaticSymbol;
524 if (SeenWorkingDirectories.insert(
"/").second)
525 for (
const auto &MappedFile : MappedFileContents)
526 if (llvm::sys::path::is_absolute(MappedFile.first))
527 InMemoryFileSystem->addFile(
529 llvm::MemoryBuffer::getMemBuffer(MappedFile.second));
531 bool ProcessingFailed =
false;
532 bool FileSkipped =
false;
535 std::vector<std::string> AbsolutePaths;
536 AbsolutePaths.reserve(SourcePaths.size());
537 for (
const auto &SourcePath : SourcePaths) {
540 llvm::errs() <<
"Skipping " << SourcePath
541 <<
". Error while getting an absolute path: "
542 << llvm::toString(AbsPath.takeError()) <<
"\n";
545 AbsolutePaths.push_back(std::move(*AbsPath));
549 std::string InitialWorkingDir;
550 if (
auto CWD = OverlayFileSystem->getCurrentWorkingDirectory()) {
551 InitialWorkingDir = std::move(*CWD);
553 llvm::errs() <<
"Could not get working directory: "
554 << CWD.getError().message() <<
"\n";
557 size_t NumOfTotalFiles = AbsolutePaths.size();
558 unsigned ProcessedFileCounter = 0;
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 if (NumOfTotalFiles > 1)
615 llvm::errs() <<
"[" + std::to_string(++ProcessedFileCounter) +
"/" +
616 std::to_string(NumOfTotalFiles) +
617 "] Processing file " +
File
619 ToolInvocation Invocation(std::move(CommandLine), Action, Files.get(),
623 if (!Invocation.
run()) {
625 if (PrintErrorMessage)
626 llvm::errs() <<
"Error while processing " <<
File <<
".\n";
627 ProcessingFailed =
true;
632 if (!InitialWorkingDir.empty()) {
634 OverlayFileSystem->setCurrentWorkingDirectory(InitialWorkingDir))
635 llvm::errs() <<
"Error when trying to restore working dir: "
636 << EC.message() <<
"\n";
638 return ProcessingFailed ? 1 : (FileSkipped ? 2 : 0);
644 std::vector<std::unique_ptr<ASTUnit>> &ASTs;
647 ASTBuilderAction(std::vector<std::unique_ptr<ASTUnit>> &ASTs) : ASTs(ASTs) {}
649 bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
651 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
653 std::unique_ptr<ASTUnit> AST = ASTUnit::LoadFromCompilerInvocation(
654 Invocation, std::move(PCHContainerOps),
662 ASTs.push_back(std::move(AST));
670 ASTBuilderAction Action(ASTs);
675 this->PrintErrorMessage = PrintErrorMessage;
681std::unique_ptr<ASTUnit>
683 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
685 "clang-tool", std::move(PCHContainerOps));
689 StringRef Code,
const std::vector<std::string> &Args, StringRef
FileName,
690 StringRef ToolName, std::shared_ptr<PCHContainerOperations> PCHContainerOps,
693 std::vector<std::unique_ptr<ASTUnit>> ASTs;
694 ASTBuilderAction Action(ASTs);
696 new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
698 new llvm::vfs::InMemoryFileSystem);
699 OverlayFileSystem->pushOverlay(InMemoryFileSystem);
705 &Action, Files.get(), std::move(PCHContainerOps));
708 InMemoryFileSystem->addFile(
FileName, 0,
709 llvm::MemoryBuffer::getMemBufferCopy(Code));
710 for (
auto &FilenameWithContent : VirtualMappedFiles) {
711 InMemoryFileSystem->addFile(
712 FilenameWithContent.first, 0,
713 llvm::MemoryBuffer::getMemBuffer(FilenameWithContent.second));
716 if (!Invocation.
run())
719 assert(ASTs.size() == 1);
720 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()
The JSON file list parser is used to communicate input to InstallAPI.
std::unique_ptr< DiagnosticOptions > CreateAndPopulateDiagOpts(ArrayRef< const char * > Argv)
@ Success
Template argument deduction was successful.
Diagnostic wrappers for TextAPI types for error reporting.