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/Debug.h" 47 #include "llvm/Support/ErrorHandling.h" 48 #include "llvm/Support/FileSystem.h" 49 #include "llvm/Support/Host.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" 58 #include <system_error> 62 #define DEBUG_TYPE "clang-tooling" 64 using namespace clang;
65 using namespace tooling;
80 new driver::Driver(BinaryName, llvm::sys::getDefaultTargetTriple(),
81 *Diagnostics, std::move(VFS));
82 CompilerDriver->
setTitle(
"clang_based_tool");
83 return CompilerDriver;
94 if (Jobs.
size() != 1 || !isa<driver::Command>(*Jobs.
begin())) {
96 llvm::raw_svector_ostream error_stream(error_msg);
97 Jobs.
Print(error_stream,
"; ",
true);
98 Diagnostics->
Report(diag::err_fe_expected_compiler_job)
99 << error_stream.str();
104 const auto &
Cmd = cast<driver::Command>(*Jobs.
begin());
105 if (StringRef(
Cmd.getCreator().getName()) !=
"clang") {
106 Diagnostics->
Report(diag::err_fe_expected_clang_command);
110 return &
Cmd.getArguments();
119 assert(!CC1Args.empty() &&
"Must at least contain the program name!");
122 *Invocation, CC1Args.data() + 1, CC1Args.data() + CC1Args.size(),
124 Invocation->getFrontendOpts().DisableFree =
false;
125 Invocation->getCodeGenOpts().DisableFree =
false;
130 const Twine &FileName,
131 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
133 FileName,
"clang-tool",
134 std::move(PCHContainerOps));
140 static std::vector<std::string>
142 const std::vector<std::string> &ExtraArgs,
143 StringRef FileName) {
144 std::vector<std::string> Args;
145 Args.push_back(ToolName.str());
146 Args.push_back(
"-fsyntax-only");
147 Args.insert(Args.end(), ExtraArgs.begin(), ExtraArgs.end());
148 Args.push_back(FileName.str());
158 const std::vector<std::string> &Args,
const Twine &FileName,
159 const Twine &ToolName,
160 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
162 StringRef FileNameRef = FileName.toNullTerminatedStringRef(FileNameStorage);
169 ToolAction, Files.get(),
170 std::move(PCHContainerOps));
171 return Invocation.
run();
176 const std::vector<std::string> &Args,
const Twine &FileName,
177 const Twine &ToolName,
178 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
181 new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
183 new llvm::vfs::InMemoryFileSystem);
184 OverlayFileSystem->pushOverlay(InMemoryFileSystem);
187 InMemoryFileSystem->addFile(FileName, 0,
188 llvm::MemoryBuffer::getMemBuffer(
189 Code.toNullTerminatedStringRef(CodeStorage)));
191 for (
auto &FilenameWithContent : VirtualMappedFiles) {
192 InMemoryFileSystem->addFile(
193 FilenameWithContent.first, 0,
194 llvm::MemoryBuffer::getMemBuffer(FilenameWithContent.second));
203 StringRef RelativePath(File);
205 if (RelativePath.startswith(
"./")) {
206 RelativePath = RelativePath.substr(strlen(
"./"));
210 if (
auto EC = FS.makeAbsolute(AbsolutePath))
211 return llvm::errorCodeToError(EC);
212 llvm::sys::path::native(AbsolutePath);
213 return AbsolutePath.str();
217 return llvm::cantFail(
getAbsolutePath(*llvm::vfs::getRealFileSystem(), File));
221 StringRef InvokedAs) {
222 if (!CommandLine.empty() && !InvokedAs.empty()) {
223 bool AlreadyHasTarget =
false;
224 bool AlreadyHasMode =
false;
226 for (
auto Token = ++CommandLine.begin();
Token != CommandLine.end();
228 StringRef TokenRef(*
Token);
230 (TokenRef ==
"-target" || TokenRef.startswith(
"-target="));
231 AlreadyHasMode |= (TokenRef ==
"--driver-mode" ||
232 TokenRef.startswith(
"--driver-mode="));
236 if (!AlreadyHasMode && TargetMode.DriverMode) {
237 CommandLine.insert(++CommandLine.begin(), TargetMode.DriverMode);
239 if (!AlreadyHasTarget && TargetMode.TargetIsValid) {
240 CommandLine.insert(++CommandLine.begin(), {
"-target",
241 TargetMode.TargetPrefix});
255 SingleFrontendActionFactory(
FrontendAction *Action) : Action(Action) {}
263 std::vector<std::string> CommandLine,
ToolAction *Action,
264 FileManager *Files, std::shared_ptr<PCHContainerOperations> PCHContainerOps)
265 : CommandLine(
std::move(CommandLine)), Action(Action), OwnsAction(
false),
266 Files(Files), PCHContainerOps(
std::move(PCHContainerOps)) {}
270 FileManager *Files, std::shared_ptr<PCHContainerOperations> PCHContainerOps)
271 : CommandLine(
std::move(CommandLine)),
272 Action(new SingleFrontendActionFactory(FAction)), OwnsAction(
true),
273 Files(Files), PCHContainerOps(
std::move(PCHContainerOps)) {}
282 llvm::sys::path::native(FilePath, PathStorage);
283 MappedFileContents[PathStorage] = Content;
287 std::vector<const char*> Argv;
288 for (
const std::string &Str : CommandLine)
289 Argv.push_back(Str.c_str());
290 const char *
const BinaryName = Argv[0];
292 unsigned MissingArgIndex, MissingArgCount;
294 llvm::opt::InputArgList ParsedArgs = Opts->ParseArgs(
298 llvm::errs(), &*DiagOpts);
301 DiagConsumer ? DiagConsumer : &DiagnosticPrinter,
false);
303 const std::unique_ptr<driver::Driver> Driver(
310 Driver->setCheckInputsExist(
false);
311 const std::unique_ptr<driver::Compilation> Compilation(
312 Driver->BuildCompilation(llvm::makeArrayRef(Argv)));
316 &Diagnostics, Compilation.get());
319 std::unique_ptr<CompilerInvocation> Invocation(
322 for (
const auto &It : MappedFileContents) {
324 std::unique_ptr<llvm::MemoryBuffer> Input =
325 llvm::MemoryBuffer::getMemBuffer(It.getValue());
326 Invocation->getPreprocessorOpts().addRemappedFile(It.getKey(),
329 return runInvocation(BinaryName, Compilation.get(), std::move(Invocation),
330 std::move(PCHContainerOps));
333 bool ToolInvocation::runInvocation(
335 std::shared_ptr<CompilerInvocation> Invocation,
336 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
338 if (Invocation->getHeaderSearchOpts().Verbose) {
339 llvm::errs() <<
"clang Invocation:\n";
341 llvm::errs() <<
"\n";
345 std::move(PCHContainerOps), DiagConsumer);
349 std::shared_ptr<CompilerInvocation> Invocation,
FileManager *Files,
350 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
360 std::unique_ptr<FrontendAction> ScopedToolAction(
create());
369 const bool Success = Compiler.
ExecuteAction(*ScopedToolAction);
377 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
379 : Compilations(Compilations), SourcePaths(SourcePaths),
380 PCHContainerOps(
std::move(PCHContainerOps)),
381 OverlayFileSystem(new
llvm::vfs::OverlayFileSystem(
std::move(BaseFS))),
382 InMemoryFileSystem(new
llvm::vfs::InMemoryFileSystem),
384 OverlayFileSystem->pushOverlay(InMemoryFileSystem);
393 MappedFileContents.push_back(std::make_pair(FilePath, Content));
397 ArgsAdjuster =
combineAdjusters(std::move(ArgsAdjuster), std::move(Adjuster));
401 ArgsAdjuster =
nullptr;
407 for (StringRef Arg : Args)
408 if (Arg.startswith(
"-resource-dir"))
412 Args.push_back(
"-resource-dir=" +
419 static int StaticSymbol;
423 if (SeenWorkingDirectories.insert(
"/").second)
424 for (
const auto &MappedFile : MappedFileContents)
425 if (llvm::sys::path::is_absolute(MappedFile.first))
426 InMemoryFileSystem->addFile(
428 llvm::MemoryBuffer::getMemBuffer(MappedFile.second));
430 bool ProcessingFailed =
false;
431 bool FileSkipped =
false;
434 std::vector<std::string> AbsolutePaths;
435 AbsolutePaths.reserve(SourcePaths.size());
436 for (
const auto &SourcePath : SourcePaths) {
439 llvm::errs() <<
"Skipping " << SourcePath
440 <<
". Error while getting an absolute path: " 444 AbsolutePaths.push_back(std::move(*AbsPath));
448 std::string InitialWorkingDir;
450 if (
auto CWD = OverlayFileSystem->getCurrentWorkingDirectory()) {
451 InitialWorkingDir = std::move(*CWD);
453 llvm::errs() <<
"Could not get working directory: " 454 << CWD.getError().message() <<
"\n";
458 for (llvm::StringRef File : AbsolutePaths) {
466 std::vector<CompileCommand> CompileCommandsForFile =
468 if (CompileCommandsForFile.empty()) {
469 llvm::errs() <<
"Skipping " << File <<
". Compile command not found.\n";
481 if (OverlayFileSystem->setCurrentWorkingDirectory(
483 llvm::report_fatal_error(
"Cannot chdir into \"" +
490 for (
const auto &MappedFile : MappedFileContents)
491 if (!llvm::sys::path::is_absolute(MappedFile.first))
492 InMemoryFileSystem->addFile(
494 llvm::MemoryBuffer::getMemBuffer(MappedFile.second));
499 assert(!CommandLine.empty());
513 LLVM_DEBUG({ llvm::dbgs() <<
"Processing: " << File <<
".\n"; });
514 ToolInvocation Invocation(std::move(CommandLine), Action, Files.get(),
518 if (!Invocation.run()) {
520 if (PrintErrorMessage)
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;
574 this->PrintErrorMessage = PrintErrorMessage;
580 std::unique_ptr<ASTUnit>
582 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
584 "clang-tool", std::move(PCHContainerOps));
588 StringRef Code,
const std::vector<std::string> &Args, StringRef FileName,
589 StringRef ToolName, std::shared_ptr<PCHContainerOperations> PCHContainerOps,
591 std::vector<std::unique_ptr<ASTUnit>> ASTs;
592 ASTBuilderAction Action(ASTs);
594 new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
596 new llvm::vfs::InMemoryFileSystem);
597 OverlayFileSystem->pushOverlay(InMemoryFileSystem);
603 &Action, Files.get(), std::move(PCHContainerOps));
605 InMemoryFileSystem->addFile(FileName, 0,
606 llvm::MemoryBuffer::getMemBufferCopy(Code));
607 if (!Invocation.run())
610 assert(ASTs.size() == 1);
611 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.
Specialize PointerLikeTypeTraits to allow LazyGenerationalUpdatePtr to be placed into a PointerUnion...
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.
llvm::vfs::FileSystem & getVirtualFileSystem() const