28 #include "llvm/ADT/STLExtras.h"
29 #include "llvm/Config/llvm-config.h"
30 #include "llvm/Option/ArgList.h"
31 #include "llvm/Option/Option.h"
32 #include "llvm/Support/Debug.h"
33 #include "llvm/Support/FileSystem.h"
34 #include "llvm/Support/Host.h"
35 #include "llvm/Support/Path.h"
36 #include "llvm/Support/raw_ostream.h"
39 #define DEBUG_TYPE "clang-tooling"
58 *Diagnostics, std::move(VFS));
59 CompilerDriver->
setTitle(
"clang_based_tool");
60 return CompilerDriver;
72 if (Jobs.
size() != 1 || !isa<clang::driver::Command>(*Jobs.
begin())) {
74 llvm::raw_svector_ostream error_stream(error_msg);
75 Jobs.
Print(error_stream,
"; ",
true);
76 Diagnostics->
Report(clang::diag::err_fe_expected_compiler_job)
77 << error_stream.str();
83 cast<clang::driver::Command>(*Jobs.
begin());
85 Diagnostics->
Report(clang::diag::err_fe_expected_clang_command);
95 const llvm::opt::ArgStringList &CC1Args) {
96 assert(!CC1Args.empty() &&
"Must at least contain the program name!");
99 *Invocation, CC1Args.data() + 1, CC1Args.data() + CC1Args.size(),
101 Invocation->getFrontendOpts().DisableFree =
false;
102 Invocation->getCodeGenOpts().DisableFree =
false;
108 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
110 FileName,
"clang-tool",
111 std::move(PCHContainerOps));
114 static std::vector<std::string>
116 const std::vector<std::string> &ExtraArgs,
118 std::vector<std::string> Args;
119 Args.push_back(ToolName.str());
120 Args.push_back(
"-fsyntax-only");
121 Args.insert(Args.end(), ExtraArgs.begin(), ExtraArgs.end());
122 Args.push_back(FileName.str());
128 const std::vector<std::string> &Args,
const Twine &
FileName,
129 const Twine &ToolName,
130 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
134 StringRef FileNameRef = FileName.toNullTerminatedStringRef(FileNameStorage);
139 OverlayFileSystem->pushOverlay(InMemoryFileSystem);
145 ToolAction, Files.get(),
146 std::move(PCHContainerOps));
149 InMemoryFileSystem->addFile(FileNameRef, 0,
150 llvm::MemoryBuffer::getMemBuffer(
151 Code.toNullTerminatedStringRef(CodeStorage)));
153 for (
auto &FilenameWithContent : VirtualMappedFiles) {
154 InMemoryFileSystem->addFile(
155 FilenameWithContent.first, 0,
156 llvm::MemoryBuffer::getMemBuffer(FilenameWithContent.second));
159 return Invocation.run();
163 StringRef RelativePath(File);
165 if (RelativePath.startswith(
"./")) {
166 RelativePath = RelativePath.substr(strlen(
"./"));
170 std::error_code EC = llvm::sys::fs::make_absolute(AbsolutePath);
173 llvm::sys::path::native(AbsolutePath);
174 return AbsolutePath.str();
178 StringRef InvokedAs) {
179 if (!CommandLine.empty() && !InvokedAs.empty()) {
180 bool AlreadyHasTarget =
false;
181 bool AlreadyHasMode =
false;
183 for (
auto Token = ++CommandLine.begin();
Token != CommandLine.end();
185 StringRef TokenRef(*
Token);
187 (TokenRef ==
"-target" || TokenRef.startswith(
"-target="));
188 AlreadyHasMode |= (TokenRef ==
"--driver-mode" ||
189 TokenRef.startswith(
"--driver-mode="));
193 if (!AlreadyHasMode && !TargetMode.second.empty()) {
194 CommandLine.insert(++CommandLine.begin(), TargetMode.second);
196 if (!AlreadyHasTarget && !TargetMode.first.empty()) {
197 CommandLine.insert(++CommandLine.begin(), {
"-target", TargetMode.first});
204 class SingleFrontendActionFactory :
public FrontendActionFactory {
208 SingleFrontendActionFactory(FrontendAction *
Action) : Action(Action) {}
217 FileManager *Files, std::shared_ptr<PCHContainerOperations> PCHContainerOps)
218 : CommandLine(std::move(CommandLine)), Action(Action), OwnsAction(
false),
219 Files(Files), PCHContainerOps(std::move(PCHContainerOps)),
220 DiagConsumer(nullptr) {}
224 FileManager *Files, std::shared_ptr<PCHContainerOperations> PCHContainerOps)
225 : CommandLine(std::move(CommandLine)),
226 Action(new SingleFrontendActionFactory(FAction)), OwnsAction(
true),
227 Files(Files), PCHContainerOps(std::move(PCHContainerOps)),
228 DiagConsumer(nullptr) {}
237 llvm::sys::path::native(FilePath, PathStorage);
238 MappedFileContents[PathStorage] = Content;
242 std::vector<const char*> Argv;
243 for (
const std::string &Str : CommandLine)
244 Argv.push_back(Str.c_str());
245 const char *
const BinaryName = Argv[0];
247 unsigned MissingArgIndex, MissingArgCount;
249 llvm::opt::InputArgList ParsedArgs = Opts->ParseArgs(
253 llvm::errs(), &*DiagOpts);
256 DiagConsumer ? DiagConsumer : &DiagnosticPrinter,
false);
258 const std::unique_ptr<clang::driver::Driver> Driver(
261 Driver->setCheckInputsExist(
false);
262 const std::unique_ptr<clang::driver::Compilation> Compilation(
263 Driver->BuildCompilation(llvm::makeArrayRef(Argv)));
267 &Diagnostics, Compilation.get());
271 std::unique_ptr<clang::CompilerInvocation> Invocation(
274 for (
const auto &It : MappedFileContents) {
276 std::unique_ptr<llvm::MemoryBuffer>
Input =
277 llvm::MemoryBuffer::getMemBuffer(It.getValue());
278 Invocation->getPreprocessorOpts().addRemappedFile(It.getKey(),
281 return runInvocation(BinaryName, Compilation.get(), std::move(Invocation),
282 std::move(PCHContainerOps));
285 bool ToolInvocation::runInvocation(
287 std::shared_ptr<clang::CompilerInvocation> Invocation,
288 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
290 if (Invocation->getHeaderSearchOpts().Verbose) {
291 llvm::errs() <<
"clang Invocation:\n";
293 llvm::errs() <<
"\n";
297 std::move(PCHContainerOps), DiagConsumer);
301 std::shared_ptr<CompilerInvocation> Invocation,
FileManager *Files,
302 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
312 std::unique_ptr<FrontendAction> ScopedToolAction(
create());
321 const bool Success = Compiler.
ExecuteAction(*ScopedToolAction);
329 std::shared_ptr<PCHContainerOperations> PCHContainerOps)
330 : Compilations(Compilations), SourcePaths(SourcePaths),
331 PCHContainerOps(std::move(PCHContainerOps)),
333 InMemoryFileSystem(new vfs::InMemoryFileSystem),
335 DiagConsumer(nullptr) {
336 OverlayFileSystem->pushOverlay(InMemoryFileSystem);
345 MappedFileContents.push_back(std::make_pair(FilePath, Content));
353 ArgsAdjuster = std::move(Adjuster);
357 ArgsAdjuster =
nullptr;
363 for (StringRef Arg : Args)
364 if (Arg.startswith(
"-resource-dir"))
368 Args.push_back(
"-resource-dir=" +
375 static int StaticSymbol;
378 if (std::error_code EC = llvm::sys::fs::current_path(InitialDirectory))
379 llvm::report_fatal_error(
"Cannot detect current path: " +
380 Twine(EC.message()));
384 if (SeenWorkingDirectories.insert(
"/").second)
385 for (
const auto &MappedFile : MappedFileContents)
386 if (llvm::sys::path::is_absolute(MappedFile.first))
387 InMemoryFileSystem->addFile(
389 llvm::MemoryBuffer::getMemBuffer(MappedFile.second));
391 bool ProcessingFailed =
false;
392 for (
const auto &SourcePath : SourcePaths) {
402 std::vector<CompileCommand> CompileCommandsForFile =
404 if (CompileCommandsForFile.empty()) {
410 llvm::errs() <<
"Skipping " << File <<
". Compile command not found.\n";
421 if (OverlayFileSystem->setCurrentWorkingDirectory(
423 llvm::report_fatal_error(
"Cannot chdir into \"" +
430 for (
const auto &MappedFile : MappedFileContents)
431 if (!llvm::sys::path::is_absolute(MappedFile.first))
432 InMemoryFileSystem->addFile(
434 llvm::MemoryBuffer::getMemBuffer(MappedFile.second));
439 assert(!CommandLine.empty());
453 DEBUG({ llvm::dbgs() <<
"Processing: " << File <<
".\n"; });
454 ToolInvocation Invocation(std::move(CommandLine), Action, Files.get(),
458 if (!Invocation.run()) {
460 llvm::errs() <<
"Error while processing " << File <<
".\n";
461 ProcessingFailed =
true;
465 if (OverlayFileSystem->setCurrentWorkingDirectory(InitialDirectory.c_str()))
466 llvm::report_fatal_error(
"Cannot chdir into \"" +
467 Twine(InitialDirectory) +
"\n!");
470 return ProcessingFailed ? 1 : 0;
476 std::vector<std::unique_ptr<ASTUnit>> &
ASTs;
479 ASTBuilderAction(std::vector<std::unique_ptr<ASTUnit>> &
ASTs) :
ASTs(
ASTs) {}
481 bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
483 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
484 DiagnosticConsumer *DiagConsumer)
override {
485 std::unique_ptr<ASTUnit> AST = ASTUnit::LoadFromCompilerInvocation(
486 Invocation, std::move(PCHContainerOps),
494 ASTs.push_back(std::move(AST));
505 std::unique_ptr<ASTUnit>
507 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
509 "clang-tool", std::move(PCHContainerOps));
513 const Twine &Code,
const std::vector<std::string> &Args,
514 const Twine &
FileName,
const Twine &ToolName,
515 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
518 StringRef FileNameRef = FileName.toNullTerminatedStringRef(FileNameStorage);
520 std::vector<std::unique_ptr<ASTUnit>>
ASTs;
521 ASTBuilderAction
Action(ASTs);
526 OverlayFileSystem->pushOverlay(InMemoryFileSystem);
532 &Action, Files.get(), std::move(PCHContainerOps));
535 InMemoryFileSystem->addFile(FileNameRef, 0,
536 llvm::MemoryBuffer::getMemBuffer(
537 Code.toNullTerminatedStringRef(CodeStorage)));
538 if (!Invocation.run())
541 assert(ASTs.size() == 1);
542 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...
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.
const Tool & getCreator() const
getCreator - Return the Tool which caused the creation of this job.
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.
IntrusiveRefCntPtr< vfs::FileSystem > getVirtualFileSystem() const
Concrete class used by the front-end to report problems and issues.
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...
bool hasDiagnostics() const
JobList - A sequence of jobs to perform.
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.
Command - An executable path/name and argument vector to execute.
void clearStatCaches()
Removes all FileSystemStatCache objects from the manager.
void setTitle(std::string Value)
const llvm::opt::ArgStringList & getArguments() const
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.
std::vector< std::string > CommandLine
Compilation - A set of tasks to perform for a single driver invocation.
void Print(llvm::raw_ostream &OS, const char *Terminator, bool Quote, CrashReportInfo *CrashInfo=nullptr) const
Keeps track of options that affect how file operations are performed.
void setInvocation(std::shared_ptr< CompilerInvocation > Value)
setInvocation - Replace the current invocation.