39 #include "llvm/ADT/ArrayRef.h" 40 #include "llvm/ADT/IntrusiveRefCntPtr.h" 41 #include "llvm/ADT/SmallString.h" 42 #include "llvm/ADT/StringRef.h" 43 #include "llvm/ADT/Twine.h" 44 #include "llvm/Option/ArgList.h" 45 #include "llvm/Option/OptTable.h" 46 #include "llvm/Option/Option.h" 47 #include "llvm/Support/Casting.h" 48 #include "llvm/Support/Debug.h" 49 #include "llvm/Support/ErrorHandling.h" 50 #include "llvm/Support/FileSystem.h" 51 #include "llvm/Support/Host.h" 52 #include "llvm/Support/MemoryBuffer.h" 53 #include "llvm/Support/Path.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,
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));
203 StringRef RelativePath(File);
205 if (RelativePath.startswith(
"./")) {
206 RelativePath = RelativePath.substr(strlen(
"./"));
210 std::error_code EC = llvm::sys::fs::make_absolute(AbsolutePath);
213 llvm::sys::path::native(AbsolutePath);
214 return AbsolutePath.str();
218 StringRef InvokedAs) {
219 if (!CommandLine.empty() && !InvokedAs.empty()) {
220 bool AlreadyHasTarget =
false;
221 bool AlreadyHasMode =
false;
223 for (
auto Token = ++CommandLine.begin();
Token != CommandLine.end();
225 StringRef TokenRef(*
Token);
227 (TokenRef ==
"-target" || TokenRef.startswith(
"-target="));
228 AlreadyHasMode |= (TokenRef ==
"--driver-mode" ||
229 TokenRef.startswith(
"--driver-mode="));
233 if (!AlreadyHasMode && TargetMode.DriverMode) {
234 CommandLine.insert(++CommandLine.begin(), TargetMode.DriverMode);
236 if (!AlreadyHasTarget && TargetMode.TargetIsValid) {
237 CommandLine.insert(++CommandLine.begin(), {
"-target",
238 TargetMode.TargetPrefix});
252 SingleFrontendActionFactory(
FrontendAction *Action) : Action(Action) {}
260 std::vector<std::string> CommandLine,
ToolAction *Action,
261 FileManager *Files, std::shared_ptr<PCHContainerOperations> PCHContainerOps)
262 : CommandLine(
std::move(CommandLine)), Action(Action), OwnsAction(
false),
263 Files(Files), PCHContainerOps(
std::move(PCHContainerOps)) {}
267 FileManager *Files, std::shared_ptr<PCHContainerOperations> PCHContainerOps)
268 : CommandLine(
std::move(CommandLine)),
269 Action(new SingleFrontendActionFactory(FAction)), OwnsAction(
true),
270 Files(Files), PCHContainerOps(
std::move(PCHContainerOps)) {}
279 llvm::sys::path::native(FilePath, PathStorage);
280 MappedFileContents[PathStorage] = Content;
284 std::vector<const char*> Argv;
285 for (
const std::string &Str : CommandLine)
286 Argv.push_back(Str.c_str());
287 const char *
const BinaryName = Argv[0];
289 unsigned MissingArgIndex, MissingArgCount;
291 llvm::opt::InputArgList ParsedArgs = Opts->ParseArgs(
295 llvm::errs(), &*DiagOpts);
298 DiagConsumer ? DiagConsumer : &DiagnosticPrinter,
false);
300 const std::unique_ptr<driver::Driver> Driver(
303 Driver->setCheckInputsExist(
false);
304 const std::unique_ptr<driver::Compilation> Compilation(
305 Driver->BuildCompilation(llvm::makeArrayRef(Argv)));
309 &Diagnostics, Compilation.get());
312 std::unique_ptr<CompilerInvocation> Invocation(
315 for (
const auto &It : MappedFileContents) {
317 std::unique_ptr<llvm::MemoryBuffer> Input =
318 llvm::MemoryBuffer::getMemBuffer(It.getValue());
319 Invocation->getPreprocessorOpts().addRemappedFile(It.getKey(),
322 return runInvocation(BinaryName, Compilation.get(), std::move(Invocation),
323 std::move(PCHContainerOps));
326 bool ToolInvocation::runInvocation(
328 std::shared_ptr<CompilerInvocation> Invocation,
329 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
331 if (Invocation->getHeaderSearchOpts().Verbose) {
332 llvm::errs() <<
"clang Invocation:\n";
334 llvm::errs() <<
"\n";
338 std::move(PCHContainerOps), DiagConsumer);
342 std::shared_ptr<CompilerInvocation> Invocation,
FileManager *Files,
343 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
353 std::unique_ptr<FrontendAction> ScopedToolAction(
create());
362 const bool Success = Compiler.
ExecuteAction(*ScopedToolAction);
370 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
372 : Compilations(Compilations), SourcePaths(SourcePaths),
373 PCHContainerOps(
std::move(PCHContainerOps)),
374 OverlayFileSystem(new vfs::OverlayFileSystem(
std::move(BaseFS))),
375 InMemoryFileSystem(new vfs::InMemoryFileSystem),
377 OverlayFileSystem->pushOverlay(InMemoryFileSystem);
386 MappedFileContents.push_back(std::make_pair(FilePath, Content));
390 ArgsAdjuster =
combineAdjusters(std::move(ArgsAdjuster), std::move(Adjuster));
394 ArgsAdjuster =
nullptr;
400 for (StringRef Arg : Args)
401 if (Arg.startswith(
"-resource-dir"))
405 Args.push_back(
"-resource-dir=" +
412 static int StaticSymbol;
414 std::string InitialDirectory;
415 if (llvm::ErrorOr<std::string> CWD =
416 OverlayFileSystem->getCurrentWorkingDirectory()) {
417 InitialDirectory = std::move(*CWD);
419 llvm::report_fatal_error(
"Cannot detect current path: " +
420 Twine(CWD.getError().message()));
425 if (SeenWorkingDirectories.insert(
"/").second)
426 for (
const auto &MappedFile : MappedFileContents)
427 if (llvm::sys::path::is_absolute(MappedFile.first))
428 InMemoryFileSystem->addFile(
430 llvm::MemoryBuffer::getMemBuffer(MappedFile.second));
432 bool ProcessingFailed =
false;
433 bool FileSkipped =
false;
434 for (
const auto &SourcePath : SourcePaths) {
444 std::vector<CompileCommand> CompileCommandsForFile =
446 if (CompileCommandsForFile.empty()) {
447 llvm::errs() <<
"Skipping " << File <<
". Compile command not found.\n";
459 if (OverlayFileSystem->setCurrentWorkingDirectory(
461 llvm::report_fatal_error(
"Cannot chdir into \"" +
468 for (
const auto &MappedFile : MappedFileContents)
469 if (!llvm::sys::path::is_absolute(MappedFile.first))
470 InMemoryFileSystem->addFile(
472 llvm::MemoryBuffer::getMemBuffer(MappedFile.second));
477 assert(!CommandLine.empty());
491 LLVM_DEBUG({ llvm::dbgs() <<
"Processing: " << File <<
".\n"; });
492 ToolInvocation Invocation(std::move(CommandLine), Action, Files.get(),
496 if (!Invocation.run()) {
498 llvm::errs() <<
"Error while processing " << File <<
".\n";
499 ProcessingFailed =
true;
503 if (OverlayFileSystem->setCurrentWorkingDirectory(InitialDirectory.c_str()))
504 llvm::report_fatal_error(
"Cannot chdir into \"" +
505 Twine(InitialDirectory) +
"\n!");
508 return ProcessingFailed ? 1 : (FileSkipped ? 2 : 0);
514 std::vector<std::unique_ptr<ASTUnit>> &ASTs;
517 ASTBuilderAction(std::vector<std::unique_ptr<ASTUnit>> &ASTs) : ASTs(ASTs) {}
519 bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
521 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
523 std::unique_ptr<ASTUnit> AST = ASTUnit::LoadFromCompilerInvocation(
524 Invocation, std::move(PCHContainerOps),
532 ASTs.push_back(std::move(AST));
540 ASTBuilderAction Action(ASTs);
547 std::unique_ptr<ASTUnit>
549 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
551 "clang-tool", std::move(PCHContainerOps));
555 const Twine &Code,
const std::vector<std::string> &Args,
556 const Twine &FileName,
const Twine &ToolName,
557 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
560 StringRef FileNameRef = FileName.toNullTerminatedStringRef(FileNameStorage);
562 std::vector<std::unique_ptr<ASTUnit>> ASTs;
563 ASTBuilderAction Action(ASTs);
568 OverlayFileSystem->pushOverlay(InMemoryFileSystem);
574 &Action, Files.get(), std::move(PCHContainerOps));
577 InMemoryFileSystem->addFile(FileNameRef, 0,
578 llvm::MemoryBuffer::getMemBuffer(
579 Code.toNullTerminatedStringRef(CodeStorage)));
580 if (!Invocation.run())
583 assert(ASTs.size() == 1);
584 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.
void createDiagnostics(DiagnosticConsumer *Client=nullptr, bool ShouldOwnClient=true)
Create the diagnostics engine using the invocation's diagnostic options and replace any existing one ...
IntrusiveRefCntPtr< FileSystem > getRealFileSystem()
Gets an vfs::FileSystem for the 'real' file system, as seen by the operating system.
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.
Token - This structure provides full information about a lexed token.
An in-memory file system.
A file system that allows overlaying one AbstractFileSystem on top of another.
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...
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.
Options for controlling the compiler diagnostics engine.
void clearStatCaches()
Removes all FileSystemStatCache objects from the manager.
void setTitle(std::string Value)
Dataflow Directional Tag Classes.
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.
Defines the virtual file system interface vfs::FileSystem.
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.
IntrusiveRefCntPtr< vfs::FileSystem > getVirtualFileSystem() const
Defines the Diagnostic IDs-related interfaces.