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;
391 DiagConsumer ? DiagConsumer : &DiagnosticPrinter,
false);
395 Diagnostics->setSourceManager(&SrcMgr);
398 if (CommandLine.size() >= 2 && CommandLine[1] ==
"-cc1") {
400 std::unique_ptr<CompilerInvocation> Invocation(
402 if (Diagnostics->hasErrorOccurred())
405 std::move(PCHContainerOps), DiagConsumer);
408 const std::unique_ptr<driver::Driver> Driver(
415 Driver->setCheckInputsExist(
false);
416 const std::unique_ptr<driver::Compilation> Compilation(
421 &*Diagnostics, Compilation.get());
424 std::unique_ptr<CompilerInvocation> Invocation(
426 return runInvocation(BinaryName, Compilation.get(), std::move(Invocation),
427 std::move(PCHContainerOps));
430bool ToolInvocation::runInvocation(
432 std::shared_ptr<CompilerInvocation> Invocation,
433 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
435 if (Invocation->getHeaderSearchOpts().Verbose) {
436 llvm::errs() <<
"clang Invocation:\n";
438 llvm::errs() <<
"\n";
442 std::move(PCHContainerOps), DiagConsumer);
446 std::shared_ptr<CompilerInvocation> Invocation,
FileManager *Files,
447 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
457 std::unique_ptr<FrontendAction> ScopedToolAction(
create());
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.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;
649 ASTBuilderAction(std::vector<std::unique_ptr<ASTUnit>> &ASTs) : ASTs(ASTs) {}
651 bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
653 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
655 std::unique_ptr<ASTUnit> AST = ASTUnit::LoadFromCompilerInvocation(
656 Invocation, std::move(PCHContainerOps),
658 &Invocation->getDiagnosticOpts(),
665 ASTs.push_back(std::move(AST));
673 ASTBuilderAction Action(ASTs);
678 this->PrintErrorMessage = PrintErrorMessage;
684std::unique_ptr<ASTUnit>
686 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
688 "clang-tool", std::move(PCHContainerOps));
692 StringRef Code,
const std::vector<std::string> &Args, StringRef
FileName,
693 StringRef ToolName, std::shared_ptr<PCHContainerOperations> PCHContainerOps,
696 std::vector<std::unique_ptr<ASTUnit>> ASTs;
697 ASTBuilderAction Action(ASTs);
699 new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
701 new llvm::vfs::InMemoryFileSystem);
702 OverlayFileSystem->pushOverlay(InMemoryFileSystem);
708 &Action, Files.get(), std::move(PCHContainerOps));
711 InMemoryFileSystem->addFile(
FileName, 0,
712 llvm::MemoryBuffer::getMemBufferCopy(Code));
713 for (
auto &FilenameWithContent : VirtualMappedFiles) {
714 InMemoryFileSystem->addFile(
715 FilenameWithContent.first, 0,
716 llvm::MemoryBuffer::getMemBuffer(FilenameWithContent.second));
719 if (!Invocation.
run())
722 assert(ASTs.size() == 1);
723 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(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 ...
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.