38 #include "llvm/ADT/ArrayRef.h" 39 #include "llvm/ADT/IntrusiveRefCntPtr.h" 40 #include "llvm/ADT/SmallString.h" 41 #include "llvm/ADT/StringRef.h" 42 #include "llvm/ADT/Twine.h" 43 #include "llvm/Option/ArgList.h" 44 #include "llvm/Option/OptTable.h" 45 #include "llvm/Option/Option.h" 46 #include "llvm/Support/Casting.h" 47 #include "llvm/Support/Debug.h" 48 #include "llvm/Support/ErrorHandling.h" 49 #include "llvm/Support/FileSystem.h" 50 #include "llvm/Support/Host.h" 51 #include "llvm/Support/MemoryBuffer.h" 52 #include "llvm/Support/Path.h" 53 #include "llvm/Support/VirtualFileSystem.h" 54 #include "llvm/Support/raw_ostream.h" 59 #include <system_error> 63 #define DEBUG_TYPE "clang-tooling" 65 using namespace clang;
66 using namespace tooling;
81 new driver::Driver(BinaryName, llvm::sys::getDefaultTargetTriple(),
82 *Diagnostics, std::move(VFS));
83 CompilerDriver->
setTitle(
"clang_based_tool");
84 return CompilerDriver;
95 if (Jobs.
size() != 1 || !isa<driver::Command>(*Jobs.
begin())) {
97 llvm::raw_svector_ostream error_stream(error_msg);
98 Jobs.
Print(error_stream,
"; ",
true);
99 Diagnostics->
Report(diag::err_fe_expected_compiler_job)
100 << error_stream.str();
105 const auto &
Cmd = cast<driver::Command>(*Jobs.
begin());
106 if (StringRef(
Cmd.getCreator().getName()) !=
"clang") {
107 Diagnostics->
Report(diag::err_fe_expected_clang_command);
111 return &
Cmd.getArguments();
120 assert(!CC1Args.empty() &&
"Must at least contain the program name!");
123 *Invocation, CC1Args.data() + 1, CC1Args.data() + CC1Args.size(),
125 Invocation->getFrontendOpts().DisableFree =
false;
126 Invocation->getCodeGenOpts().DisableFree =
false;
131 const Twine &FileName,
132 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
134 FileName,
"clang-tool",
135 std::move(PCHContainerOps));
141 static std::vector<std::string>
143 const std::vector<std::string> &ExtraArgs,
144 StringRef FileName) {
145 std::vector<std::string> Args;
146 Args.push_back(ToolName.str());
147 Args.push_back(
"-fsyntax-only");
148 Args.insert(Args.end(), ExtraArgs.begin(), ExtraArgs.end());
149 Args.push_back(FileName.str());
159 const std::vector<std::string> &Args,
const Twine &FileName,
160 const Twine &ToolName,
161 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
163 StringRef FileNameRef = FileName.toNullTerminatedStringRef(FileNameStorage);
170 ToolAction, Files.get(),
171 std::move(PCHContainerOps));
172 return Invocation.
run();
177 const std::vector<std::string> &Args,
const Twine &FileName,
178 const Twine &ToolName,
179 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
182 new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
184 new llvm::vfs::InMemoryFileSystem);
185 OverlayFileSystem->pushOverlay(InMemoryFileSystem);
188 InMemoryFileSystem->addFile(FileName, 0,
189 llvm::MemoryBuffer::getMemBuffer(
190 Code.toNullTerminatedStringRef(CodeStorage)));
192 for (
auto &FilenameWithContent : VirtualMappedFiles) {
193 InMemoryFileSystem->addFile(
194 FilenameWithContent.first, 0,
195 llvm::MemoryBuffer::getMemBuffer(FilenameWithContent.second));
204 StringRef RelativePath(File);
206 if (RelativePath.startswith(
"./")) {
207 RelativePath = RelativePath.substr(strlen(
"./"));
211 if (
auto EC = FS.makeAbsolute(AbsolutePath))
212 return llvm::errorCodeToError(EC);
213 llvm::sys::path::native(AbsolutePath);
214 return AbsolutePath.str();
218 return llvm::cantFail(
getAbsolutePath(*llvm::vfs::getRealFileSystem(), File));
222 StringRef InvokedAs) {
223 if (!CommandLine.empty() && !InvokedAs.empty()) {
224 bool AlreadyHasTarget =
false;
225 bool AlreadyHasMode =
false;
227 for (
auto Token = ++CommandLine.begin();
Token != CommandLine.end();
229 StringRef TokenRef(*
Token);
231 (TokenRef ==
"-target" || TokenRef.startswith(
"-target="));
232 AlreadyHasMode |= (TokenRef ==
"--driver-mode" ||
233 TokenRef.startswith(
"--driver-mode="));
237 if (!AlreadyHasMode && TargetMode.DriverMode) {
238 CommandLine.insert(++CommandLine.begin(), TargetMode.DriverMode);
240 if (!AlreadyHasTarget && TargetMode.TargetIsValid) {
241 CommandLine.insert(++CommandLine.begin(), {
"-target",
242 TargetMode.TargetPrefix});
256 SingleFrontendActionFactory(
FrontendAction *Action) : Action(Action) {}
264 std::vector<std::string> CommandLine,
ToolAction *Action,
265 FileManager *Files, std::shared_ptr<PCHContainerOperations> PCHContainerOps)
266 : CommandLine(
std::move(CommandLine)), Action(Action), OwnsAction(
false),
267 Files(Files), PCHContainerOps(
std::move(PCHContainerOps)) {}
271 FileManager *Files, std::shared_ptr<PCHContainerOperations> PCHContainerOps)
272 : CommandLine(
std::move(CommandLine)),
273 Action(new SingleFrontendActionFactory(FAction)), OwnsAction(
true),
274 Files(Files), PCHContainerOps(
std::move(PCHContainerOps)) {}
283 llvm::sys::path::native(FilePath, PathStorage);
284 MappedFileContents[PathStorage] = Content;
288 std::vector<const char*> Argv;
289 for (
const std::string &Str : CommandLine)
290 Argv.push_back(Str.c_str());
291 const char *
const BinaryName = Argv[0];
293 unsigned MissingArgIndex, MissingArgCount;
295 llvm::opt::InputArgList ParsedArgs = Opts->ParseArgs(
299 llvm::errs(), &*DiagOpts);
302 DiagConsumer ? DiagConsumer : &DiagnosticPrinter,
false);
304 const std::unique_ptr<driver::Driver> Driver(
311 Driver->setCheckInputsExist(
false);
312 const std::unique_ptr<driver::Compilation> Compilation(
313 Driver->BuildCompilation(llvm::makeArrayRef(Argv)));
317 &Diagnostics, Compilation.get());
320 std::unique_ptr<CompilerInvocation> Invocation(
323 for (
const auto &It : MappedFileContents) {
325 std::unique_ptr<llvm::MemoryBuffer> Input =
326 llvm::MemoryBuffer::getMemBuffer(It.getValue());
327 Invocation->getPreprocessorOpts().addRemappedFile(It.getKey(),
330 return runInvocation(BinaryName, Compilation.get(), std::move(Invocation),
331 std::move(PCHContainerOps));
334 bool ToolInvocation::runInvocation(
336 std::shared_ptr<CompilerInvocation> Invocation,
337 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
339 if (Invocation->getHeaderSearchOpts().Verbose) {
340 llvm::errs() <<
"clang Invocation:\n";
342 llvm::errs() <<
"\n";
346 std::move(PCHContainerOps), DiagConsumer);
350 std::shared_ptr<CompilerInvocation> Invocation,
FileManager *Files,
351 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
361 std::unique_ptr<FrontendAction> ScopedToolAction(
create());
370 const bool Success = Compiler.
ExecuteAction(*ScopedToolAction);
378 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
380 : Compilations(Compilations), SourcePaths(SourcePaths),
381 PCHContainerOps(
std::move(PCHContainerOps)),
382 OverlayFileSystem(new
llvm::vfs::OverlayFileSystem(
std::move(BaseFS))),
383 InMemoryFileSystem(new
llvm::vfs::InMemoryFileSystem),
385 OverlayFileSystem->pushOverlay(InMemoryFileSystem);
394 MappedFileContents.push_back(std::make_pair(FilePath, Content));
398 ArgsAdjuster =
combineAdjusters(std::move(ArgsAdjuster), std::move(Adjuster));
402 ArgsAdjuster =
nullptr;
408 for (StringRef Arg : Args)
409 if (Arg.startswith(
"-resource-dir"))
413 Args.push_back(
"-resource-dir=" +
420 static int StaticSymbol;
424 if (SeenWorkingDirectories.insert(
"/").second)
425 for (
const auto &MappedFile : MappedFileContents)
426 if (llvm::sys::path::is_absolute(MappedFile.first))
427 InMemoryFileSystem->addFile(
429 llvm::MemoryBuffer::getMemBuffer(MappedFile.second));
431 bool ProcessingFailed =
false;
432 bool FileSkipped =
false;
435 std::vector<std::string> AbsolutePaths;
436 AbsolutePaths.reserve(SourcePaths.size());
437 for (
const auto &SourcePath : SourcePaths) {
440 llvm::errs() <<
"Skipping " << SourcePath
441 <<
". Error while getting an absolute path: " 445 AbsolutePaths.push_back(std::move(*AbsPath));
449 std::string InitialWorkingDir;
451 if (
auto CWD = OverlayFileSystem->getCurrentWorkingDirectory()) {
452 InitialWorkingDir = std::move(*CWD);
454 llvm::errs() <<
"Could not get working directory: " 455 << CWD.getError().message() <<
"\n";
459 for (llvm::StringRef File : AbsolutePaths) {
467 std::vector<CompileCommand> CompileCommandsForFile =
469 if (CompileCommandsForFile.empty()) {
470 llvm::errs() <<
"Skipping " << File <<
". Compile command not found.\n";
482 if (OverlayFileSystem->setCurrentWorkingDirectory(
484 llvm::report_fatal_error(
"Cannot chdir into \"" +
491 for (
const auto &MappedFile : MappedFileContents)
492 if (!llvm::sys::path::is_absolute(MappedFile.first))
493 InMemoryFileSystem->addFile(
495 llvm::MemoryBuffer::getMemBuffer(MappedFile.second));
500 assert(!CommandLine.empty());
514 LLVM_DEBUG({ llvm::dbgs() <<
"Processing: " << File <<
".\n"; });
515 ToolInvocation Invocation(std::move(CommandLine), Action, Files.get(),
519 if (!Invocation.run()) {
521 llvm::errs() <<
"Error while processing " << File <<
".\n";
522 ProcessingFailed =
true;
527 if (!InitialWorkingDir.empty()) {
529 OverlayFileSystem->setCurrentWorkingDirectory(InitialWorkingDir))
530 llvm::errs() <<
"Error when trying to restore working dir: " 531 << EC.message() <<
"\n";
533 return ProcessingFailed ? 1 : (FileSkipped ? 2 : 0);
539 std::vector<std::unique_ptr<ASTUnit>> &ASTs;
542 ASTBuilderAction(std::vector<std::unique_ptr<ASTUnit>> &ASTs) : ASTs(ASTs) {}
544 bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
546 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
548 std::unique_ptr<ASTUnit> AST = ASTUnit::LoadFromCompilerInvocation(
549 Invocation, std::move(PCHContainerOps),
557 ASTs.push_back(std::move(AST));
565 ASTBuilderAction Action(ASTs);
570 this->RestoreCWD = RestoreCWD;
576 std::unique_ptr<ASTUnit>
578 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
580 "clang-tool", std::move(PCHContainerOps));
584 StringRef Code,
const std::vector<std::string> &Args, StringRef FileName,
585 StringRef ToolName, std::shared_ptr<PCHContainerOperations> PCHContainerOps,
587 std::vector<std::unique_ptr<ASTUnit>> ASTs;
588 ASTBuilderAction Action(ASTs);
590 new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
592 new llvm::vfs::InMemoryFileSystem);
593 OverlayFileSystem->pushOverlay(InMemoryFileSystem);
599 &Action, Files.get(), std::move(PCHContainerOps));
601 InMemoryFileSystem->addFile(FileName, 0,
602 llvm::MemoryBuffer::getMemBufferCopy(Code));
603 if (!Invocation.run())
606 assert(ASTs.size() == 1);
607 return std::move(ASTs[0]);
bool ParseDiagnosticArgs(DiagnosticOptions &Opts, llvm::opt::ArgList &Args, DiagnosticsEngine *Diags=nullptr, bool DefaultDiagColor=true, bool DefaultShowOpt=true)
Fill out Opts based on the options given in Args.
Implements support for file system lookup, file system caching, and directory search management...
Defines the clang::FileManager interface and associated types.
DominatorTree GraphTraits specialization so the DominatorTree can be iterable by generic graph iterat...
void createDiagnostics(DiagnosticConsumer *Client=nullptr, bool ShouldOwnClient=true)
Create the diagnostics engine using the invocation's diagnostic options and replace any existing one ...
Abstract base class for actions which can be performed by the frontend.
DiagnosticBuilder Report(SourceLocation Loc, unsigned DiagID)
Issue the message to the client.
static bool CreateFromArgs(CompilerInvocation &Res, const char *const *ArgBegin, const char *const *ArgEnd, DiagnosticsEngine &Diags)
Create a compiler invocation from a list of input options.
Abstract interface, implemented by clients of the front-end, which formats and prints fully processed...
void createSourceManager(FileManager &FileMgr)
Create the source manager and replace any existing one with it.
static std::string toString(const clang::SanitizerSet &Sanitizers)
Produce a string containing comma-separated names of sanitizers in Sanitizers set.
Token - This structure provides full information about a lexed token.
Forward-declares and imports various common LLVM datatypes that clang wants to use unqualified...
Concrete class used by the front-end to report problems and issues.
Defines the Diagnostic-related interfaces.
std::unique_ptr< llvm::opt::OptTable > createDriverOptTable()
Driver - Encapsulate logic for constructing compilation processes from a set of gcc-driver-like comma...
void setFileManager(FileManager *Value)
Replace the current file manager and virtual file system.
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...
std::string WorkingDir
If set, paths are resolved as if the working directory was set to the value of WorkingDir.
JobList - A sequence of jobs to perform.
void Print(llvm::raw_ostream &OS, const char *Terminator, bool Quote, CrashReportInfo *CrashInfo=nullptr) const
bool ExecuteAction(FrontendAction &Act)
ExecuteAction - Execute the provided action against the compiler's CompilerInvocation object...
CompilerInstance - Helper class for managing a single instance of the Clang compiler.
FileSystemOptions & getFileSystemOpts()
Returns the current file system options.
Options for controlling the compiler diagnostics engine.
void setTitle(std::string Value)
Dataflow Directional Tag Classes.
void clearStatCache()
Removes the FileSystemStatCache object from the manager.
std::unique_ptr< DiagnosticConsumer > create(StringRef OutputFile, DiagnosticOptions *Diags, bool MergeChildRecords=false)
Returns a DiagnosticConsumer that serializes diagnostics to a bitcode file.
Used for handling and querying diagnostic IDs.
Helper class for holding the data necessary to invoke the compiler.
Compilation - A set of tasks to perform for a single driver invocation.
Defines the clang::FileSystemOptions interface.
bool hasDiagnostics() const
Keeps track of options that affect how file operations are performed.
void setInvocation(std::shared_ptr< CompilerInvocation > Value)
setInvocation - Replace the current invocation.
Defines the Diagnostic IDs-related interfaces.
IntrusiveRefCntPtr< llvm::vfs::FileSystem > getVirtualFileSystem() const