27 #include "llvm/ADT/STLExtras.h"
28 #include "llvm/Config/llvm-config.h"
29 #include "llvm/Option/Option.h"
30 #include "llvm/Support/Debug.h"
31 #include "llvm/Support/FileSystem.h"
32 #include "llvm/Support/Host.h"
33 #include "llvm/Support/raw_ostream.h"
36 #define DEBUG_TYPE "clang-tooling"
55 *Diagnostics, std::move(VFS));
56 CompilerDriver->
setTitle(
"clang_based_tool");
57 return CompilerDriver;
69 if (Jobs.
size() != 1 || !isa<clang::driver::Command>(*Jobs.
begin())) {
71 llvm::raw_svector_ostream error_stream(error_msg);
72 Jobs.
Print(error_stream,
"; ",
true);
73 Diagnostics->
Report(clang::diag::err_fe_expected_compiler_job)
74 << error_stream.str();
80 cast<clang::driver::Command>(*Jobs.
begin());
82 Diagnostics->
Report(clang::diag::err_fe_expected_clang_command);
92 const llvm::opt::ArgStringList &CC1Args) {
93 assert(!CC1Args.empty() &&
"Must at least contain the program name!");
96 *Invocation, CC1Args.data() + 1, CC1Args.data() + CC1Args.size(),
98 Invocation->getFrontendOpts().DisableFree =
false;
99 Invocation->getCodeGenOpts().DisableFree =
false;
106 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
108 FileName,
"clang-tool",
109 std::move(PCHContainerOps));
112 static std::vector<std::string>
114 const std::vector<std::string> &ExtraArgs,
116 std::vector<std::string> Args;
117 Args.push_back(ToolName.str());
118 Args.push_back(
"-fsyntax-only");
119 Args.insert(Args.end(), ExtraArgs.begin(), ExtraArgs.end());
120 Args.push_back(FileName.str());
126 const std::vector<std::string> &Args,
const Twine &
FileName,
127 const Twine &ToolName,
128 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
132 StringRef FileNameRef = FileName.toNullTerminatedStringRef(FileNameStorage);
137 OverlayFileSystem->pushOverlay(InMemoryFileSystem);
141 ToolAction, Files.get(),
142 std::move(PCHContainerOps));
145 InMemoryFileSystem->addFile(FileNameRef, 0,
146 llvm::MemoryBuffer::getMemBuffer(
147 Code.toNullTerminatedStringRef(CodeStorage)));
149 for (
auto &FilenameWithContent : VirtualMappedFiles) {
150 InMemoryFileSystem->addFile(
151 FilenameWithContent.first, 0,
152 llvm::MemoryBuffer::getMemBuffer(FilenameWithContent.second));
155 return Invocation.run();
159 StringRef RelativePath(File);
161 if (RelativePath.startswith(
"./")) {
162 RelativePath = RelativePath.substr(strlen(
"./"));
166 std::error_code EC = llvm::sys::fs::make_absolute(AbsolutePath);
169 llvm::sys::path::native(AbsolutePath);
170 return AbsolutePath.str();
174 StringRef InvokedAs) {
175 if (!CommandLine.empty() && !InvokedAs.empty()) {
176 bool AlreadyHasTarget =
false;
177 bool AlreadyHasMode =
false;
179 for (
auto Token = ++CommandLine.begin();
Token != CommandLine.end();
181 StringRef TokenRef(*
Token);
183 (TokenRef ==
"-target" || TokenRef.startswith(
"-target="));
184 AlreadyHasMode |= (TokenRef ==
"--driver-mode" ||
185 TokenRef.startswith(
"--driver-mode="));
189 if (!AlreadyHasMode && !TargetMode.second.empty()) {
190 CommandLine.insert(++CommandLine.begin(), TargetMode.second);
192 if (!AlreadyHasTarget && !TargetMode.first.empty()) {
193 CommandLine.insert(++CommandLine.begin(), {
"-target", TargetMode.first});
200 class SingleFrontendActionFactory :
public FrontendActionFactory {
204 SingleFrontendActionFactory(FrontendAction *
Action) : Action(Action) {}
213 FileManager *Files, std::shared_ptr<PCHContainerOperations> PCHContainerOps)
214 : CommandLine(std::move(CommandLine)), Action(Action), OwnsAction(
false),
215 Files(Files), PCHContainerOps(std::move(PCHContainerOps)),
216 DiagConsumer(nullptr) {}
220 FileManager *Files, std::shared_ptr<PCHContainerOperations> PCHContainerOps)
221 : CommandLine(std::move(CommandLine)),
222 Action(new SingleFrontendActionFactory(FAction)), OwnsAction(
true),
223 Files(Files), PCHContainerOps(std::move(PCHContainerOps)),
224 DiagConsumer(nullptr) {}
233 llvm::sys::path::native(FilePath, PathStorage);
234 MappedFileContents[PathStorage] = Content;
238 std::vector<const char*> Argv;
239 for (
const std::string &Str : CommandLine)
240 Argv.push_back(Str.c_str());
241 const char *
const BinaryName = Argv[0];
244 llvm::errs(), &*DiagOpts);
247 DiagConsumer ? DiagConsumer : &DiagnosticPrinter,
false);
249 const std::unique_ptr<clang::driver::Driver> Driver(
252 Driver->setCheckInputsExist(
false);
253 const std::unique_ptr<clang::driver::Compilation> Compilation(
254 Driver->BuildCompilation(llvm::makeArrayRef(Argv)));
256 &Diagnostics, Compilation.get());
260 std::unique_ptr<clang::CompilerInvocation> Invocation(
263 for (
const auto &It : MappedFileContents) {
265 std::unique_ptr<llvm::MemoryBuffer>
Input =
266 llvm::MemoryBuffer::getMemBuffer(It.getValue());
267 Invocation->getPreprocessorOpts().addRemappedFile(It.getKey(),
270 return runInvocation(BinaryName, Compilation.get(), Invocation.release(),
271 std::move(PCHContainerOps));
274 bool ToolInvocation::runInvocation(
277 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
280 llvm::errs() <<
"clang Invocation:\n";
282 llvm::errs() <<
"\n";
285 return Action->
runInvocation(Invocation, Files, std::move(PCHContainerOps),
291 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
301 std::unique_ptr<FrontendAction> ScopedToolAction(
create());
310 const bool Success = Compiler.
ExecuteAction(*ScopedToolAction);
318 std::shared_ptr<PCHContainerOperations> PCHContainerOps)
319 : Compilations(Compilations), SourcePaths(SourcePaths),
320 PCHContainerOps(std::move(PCHContainerOps)),
322 InMemoryFileSystem(new vfs::InMemoryFileSystem),
324 DiagConsumer(nullptr) {
325 OverlayFileSystem->pushOverlay(InMemoryFileSystem);
333 MappedFileContents.push_back(std::make_pair(FilePath, Content));
341 ArgsAdjuster = std::move(Adjuster);
345 ArgsAdjuster =
nullptr;
351 for (StringRef Arg : Args)
352 if (Arg.startswith(
"-resource-dir"))
356 Args.push_back(
"-resource-dir=" +
363 static int StaticSymbol;
366 if (std::error_code EC = llvm::sys::fs::current_path(InitialDirectory))
367 llvm::report_fatal_error(
"Cannot detect current path: " +
368 Twine(EC.message()));
372 if (SeenWorkingDirectories.insert(
"/").second)
373 for (
const auto &MappedFile : MappedFileContents)
374 if (llvm::sys::path::is_absolute(MappedFile.first))
375 InMemoryFileSystem->addFile(
377 llvm::MemoryBuffer::getMemBuffer(MappedFile.second));
379 bool ProcessingFailed =
false;
380 for (
const auto &SourcePath : SourcePaths) {
390 std::vector<CompileCommand> CompileCommandsForFile =
392 if (CompileCommandsForFile.empty()) {
398 llvm::errs() <<
"Skipping " << File <<
". Compile command not found.\n";
409 if (OverlayFileSystem->setCurrentWorkingDirectory(
411 llvm::report_fatal_error(
"Cannot chdir into \"" +
418 for (
const auto &MappedFile : MappedFileContents)
419 if (!llvm::sys::path::is_absolute(MappedFile.first))
420 InMemoryFileSystem->addFile(
422 llvm::MemoryBuffer::getMemBuffer(MappedFile.second));
427 assert(!CommandLine.empty());
441 DEBUG({ llvm::dbgs() <<
"Processing: " << File <<
".\n"; });
442 ToolInvocation Invocation(std::move(CommandLine), Action, Files.get(),
444 Invocation.setDiagnosticConsumer(DiagConsumer);
446 if (!Invocation.run()) {
448 llvm::errs() <<
"Error while processing " << File <<
".\n";
449 ProcessingFailed =
true;
453 if (OverlayFileSystem->setCurrentWorkingDirectory(InitialDirectory.c_str()))
454 llvm::report_fatal_error(
"Cannot chdir into \"" +
455 Twine(InitialDirectory) +
"\n!");
458 return ProcessingFailed ? 1 : 0;
464 std::vector<std::unique_ptr<ASTUnit>> &
ASTs;
467 ASTBuilderAction(std::vector<std::unique_ptr<ASTUnit>> &
ASTs) :
ASTs(
ASTs) {}
469 bool runInvocation(CompilerInvocation *Invocation, FileManager *Files,
470 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
471 DiagnosticConsumer *DiagConsumer)
override {
472 std::unique_ptr<ASTUnit> AST = ASTUnit::LoadFromCompilerInvocation(
473 Invocation, std::move(PCHContainerOps),
481 ASTs.push_back(std::move(AST));
492 std::unique_ptr<ASTUnit>
494 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
496 "clang-tool", std::move(PCHContainerOps));
500 const Twine &Code,
const std::vector<std::string> &Args,
501 const Twine &
FileName,
const Twine &ToolName,
502 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
504 StringRef FileNameRef = FileName.toNullTerminatedStringRef(FileNameStorage);
506 std::vector<std::unique_ptr<ASTUnit>>
ASTs;
507 ASTBuilderAction
Action(ASTs);
512 OverlayFileSystem->pushOverlay(InMemoryFileSystem);
516 &Action, Files.get(), std::move(PCHContainerOps));
519 InMemoryFileSystem->addFile(FileNameRef, 0,
520 llvm::MemoryBuffer::getMemBuffer(
521 Code.toNullTerminatedStringRef(CodeStorage)));
522 if (!Invocation.run())
525 assert(ASTs.size() == 1);
526 return std::move(ASTs[0]);
HeaderSearchOptions & getHeaderSearchOpts()
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.
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.
void setInvocation(CompilerInvocation *Value)
setInvocation - Replace the current invocation.
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.
DependencyOutputOptions - Options for controlling the compiler dependency file generation.
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.