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;
95 bool OffloadCompilation =
false;
96 if (Jobs.
size() > 1) {
97 for (
auto A : Actions){
99 if (isa<driver::BindArchAction>(A))
100 A = *A->input_begin();
101 if (isa<driver::OffloadAction>(A)) {
108 assert(Actions.size() > 1);
110 isa<driver::CompileJobAction>(Actions.front()) ||
113 (isa<driver::BindArchAction>(Actions.front()) &&
114 isa<driver::CompileJobAction>(*Actions.front()->input_begin())));
115 OffloadCompilation =
true;
120 if (Jobs.
size() == 0 || !isa<driver::Command>(*Jobs.
begin()) ||
121 (Jobs.
size() > 1 && !OffloadCompilation)) {
123 llvm::raw_svector_ostream error_stream(error_msg);
124 Jobs.
Print(error_stream,
"; ",
true);
125 Diagnostics->
Report(diag::err_fe_expected_compiler_job)
126 << error_stream.str();
131 const auto &
Cmd = cast<driver::Command>(*Jobs.
begin());
132 if (StringRef(
Cmd.getCreator().getName()) !=
"clang") {
133 Diagnostics->
Report(diag::err_fe_expected_clang_command);
137 return &
Cmd.getArguments();
146 assert(!CC1Args.empty() &&
"Must at least contain the program name!");
149 Invocation->getFrontendOpts().DisableFree =
false;
150 Invocation->getCodeGenOpts().DisableFree =
false;
155 const Twine &Code,
const Twine &FileName,
156 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
158 std::vector<std::string>(), FileName,
159 "clang-tool", std::move(PCHContainerOps));
165 static std::vector<std::string>
167 const std::vector<std::string> &ExtraArgs,
168 StringRef FileName) {
169 std::vector<std::string> Args;
170 Args.push_back(ToolName.str());
171 Args.push_back(
"-fsyntax-only");
172 Args.insert(Args.end(), ExtraArgs.begin(), ExtraArgs.end());
173 Args.push_back(FileName.str());
181 std::unique_ptr<FrontendAction>
ToolAction,
const Twine &Code,
183 const std::vector<std::string> &Args,
const Twine &FileName,
184 const Twine &ToolName,
185 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
187 StringRef FileNameRef = FileName.toNullTerminatedStringRef(FileNameStorage);
194 std::move(ToolAction), Files.get(), std::move(PCHContainerOps));
195 return Invocation.
run();
199 std::unique_ptr<FrontendAction>
ToolAction,
const Twine &Code,
200 const std::vector<std::string> &Args,
const Twine &FileName,
201 const Twine &ToolName,
202 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
205 new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
207 new llvm::vfs::InMemoryFileSystem);
208 OverlayFileSystem->pushOverlay(InMemoryFileSystem);
211 InMemoryFileSystem->addFile(FileName, 0,
212 llvm::MemoryBuffer::getMemBuffer(
213 Code.toNullTerminatedStringRef(CodeStorage)));
215 for (
auto &FilenameWithContent : VirtualMappedFiles) {
216 InMemoryFileSystem->addFile(
217 FilenameWithContent.first, 0,
218 llvm::MemoryBuffer::getMemBuffer(FilenameWithContent.second));
222 Args, FileName, ToolName);
227 StringRef RelativePath(File);
229 if (RelativePath.startswith(
"./")) {
230 RelativePath = RelativePath.substr(strlen(
"./"));
234 if (
auto EC = FS.makeAbsolute(AbsolutePath))
235 return llvm::errorCodeToError(EC);
236 llvm::sys::path::native(AbsolutePath);
237 return AbsolutePath.str();
241 return llvm::cantFail(
getAbsolutePath(*llvm::vfs::getRealFileSystem(), File));
245 StringRef InvokedAs) {
246 if (!CommandLine.empty() && !InvokedAs.empty()) {
247 bool AlreadyHasTarget =
false;
248 bool AlreadyHasMode =
false;
250 for (
auto Token = ++CommandLine.begin();
Token != CommandLine.end();
252 StringRef TokenRef(*
Token);
254 (TokenRef ==
"-target" || TokenRef.startswith(
"-target="));
255 AlreadyHasMode |= (TokenRef ==
"--driver-mode" ||
256 TokenRef.startswith(
"--driver-mode="));
260 if (!AlreadyHasMode && TargetMode.DriverMode) {
261 CommandLine.insert(++CommandLine.begin(), TargetMode.DriverMode);
263 if (!AlreadyHasTarget && TargetMode.TargetIsValid) {
264 CommandLine.insert(++CommandLine.begin(), {
"-target",
265 TargetMode.TargetPrefix});
276 std::unique_ptr<FrontendAction> Action;
279 SingleFrontendActionFactory(std::unique_ptr<FrontendAction> Action)
280 : Action(std::move(Action)) {}
282 std::unique_ptr<FrontendAction>
create()
override {
283 return std::move(Action);
290 std::vector<std::string> CommandLine,
ToolAction *Action,
291 FileManager *Files, std::shared_ptr<PCHContainerOperations> PCHContainerOps)
292 : CommandLine(
std::move(CommandLine)), Action(Action), OwnsAction(
false),
293 Files(Files), PCHContainerOps(
std::move(PCHContainerOps)) {}
296 std::vector<std::string> CommandLine,
297 std::unique_ptr<FrontendAction> FAction,
FileManager *Files,
298 std::shared_ptr<PCHContainerOperations> PCHContainerOps)
299 : CommandLine(
std::move(CommandLine)),
300 Action(new SingleFrontendActionFactory(
std::move(FAction))),
301 OwnsAction(
true), Files(Files),
302 PCHContainerOps(
std::move(PCHContainerOps)) {}
311 llvm::sys::path::native(FilePath, PathStorage);
312 MappedFileContents[PathStorage] = Content;
316 std::vector<const char*> Argv;
317 for (
const std::string &Str : CommandLine)
318 Argv.push_back(Str.c_str());
319 const char *
const BinaryName = Argv[0];
321 unsigned MissingArgIndex, MissingArgCount;
326 llvm::errs(), &*DiagOpts);
329 DiagConsumer ? DiagConsumer : &DiagnosticPrinter,
false);
331 const std::unique_ptr<driver::Driver> Driver(
338 Driver->setCheckInputsExist(
false);
339 const std::unique_ptr<driver::Compilation> Compilation(
340 Driver->BuildCompilation(llvm::makeArrayRef(Argv)));
344 &Diagnostics, Compilation.get());
347 std::unique_ptr<CompilerInvocation> Invocation(
350 for (
const auto &It : MappedFileContents) {
352 std::unique_ptr<llvm::MemoryBuffer> Input =
353 llvm::MemoryBuffer::getMemBuffer(It.getValue());
354 Invocation->getPreprocessorOpts().addRemappedFile(It.getKey(),
357 return runInvocation(BinaryName, Compilation.get(), std::move(Invocation),
358 std::move(PCHContainerOps));
361 bool ToolInvocation::runInvocation(
363 std::shared_ptr<CompilerInvocation> Invocation,
364 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
366 if (Invocation->getHeaderSearchOpts().Verbose) {
367 llvm::errs() <<
"clang Invocation:\n";
369 llvm::errs() <<
"\n";
373 std::move(PCHContainerOps), DiagConsumer);
377 std::shared_ptr<CompilerInvocation> Invocation,
FileManager *Files,
378 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
388 std::unique_ptr<FrontendAction> ScopedToolAction(
create());
397 const bool Success = Compiler.
ExecuteAction(*ScopedToolAction);
405 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
408 : Compilations(Compilations), SourcePaths(SourcePaths),
409 PCHContainerOps(
std::move(PCHContainerOps)),
410 OverlayFileSystem(new
llvm::vfs::OverlayFileSystem(
std::move(BaseFS))),
411 InMemoryFileSystem(new
llvm::vfs::InMemoryFileSystem),
414 OverlayFileSystem->pushOverlay(InMemoryFileSystem);
419 Files->setVirtualFileSystem(OverlayFileSystem);
425 MappedFileContents.push_back(std::make_pair(FilePath, Content));
429 ArgsAdjuster =
combineAdjusters(std::move(ArgsAdjuster), std::move(Adjuster));
433 ArgsAdjuster =
nullptr;
439 for (StringRef Arg : Args)
440 if (Arg.startswith(
"-resource-dir"))
444 Args.push_back(
"-resource-dir=" +
451 static int StaticSymbol;
455 if (SeenWorkingDirectories.insert(
"/").second)
456 for (
const auto &MappedFile : MappedFileContents)
457 if (llvm::sys::path::is_absolute(MappedFile.first))
458 InMemoryFileSystem->addFile(
460 llvm::MemoryBuffer::getMemBuffer(MappedFile.second));
462 bool ProcessingFailed =
false;
463 bool FileSkipped =
false;
466 std::vector<std::string> AbsolutePaths;
467 AbsolutePaths.reserve(SourcePaths.size());
468 for (
const auto &SourcePath : SourcePaths) {
471 llvm::errs() <<
"Skipping " << SourcePath
472 <<
". Error while getting an absolute path: " 476 AbsolutePaths.push_back(std::move(*AbsPath));
480 std::string InitialWorkingDir;
482 if (
auto CWD = OverlayFileSystem->getCurrentWorkingDirectory()) {
483 InitialWorkingDir = std::move(*CWD);
485 llvm::errs() <<
"Could not get working directory: " 486 << CWD.getError().message() <<
"\n";
490 for (llvm::StringRef File : AbsolutePaths) {
498 std::vector<CompileCommand> CompileCommandsForFile =
500 if (CompileCommandsForFile.empty()) {
501 llvm::errs() <<
"Skipping " << File <<
". Compile command not found.\n";
513 if (OverlayFileSystem->setCurrentWorkingDirectory(
515 llvm::report_fatal_error(
"Cannot chdir into \"" +
522 for (
const auto &MappedFile : MappedFileContents)
523 if (!llvm::sys::path::is_absolute(MappedFile.first))
524 InMemoryFileSystem->addFile(
526 llvm::MemoryBuffer::getMemBuffer(MappedFile.second));
531 assert(!CommandLine.empty());
545 LLVM_DEBUG({ llvm::dbgs() <<
"Processing: " << File <<
".\n"; });
546 ToolInvocation Invocation(std::move(CommandLine), Action, Files.get(),
550 if (!Invocation.run()) {
552 if (PrintErrorMessage)
553 llvm::errs() <<
"Error while processing " << File <<
".\n";
554 ProcessingFailed =
true;
559 if (!InitialWorkingDir.empty()) {
561 OverlayFileSystem->setCurrentWorkingDirectory(InitialWorkingDir))
562 llvm::errs() <<
"Error when trying to restore working dir: " 563 << EC.message() <<
"\n";
565 return ProcessingFailed ? 1 : (FileSkipped ? 2 : 0);
571 std::vector<std::unique_ptr<ASTUnit>> &ASTs;
574 ASTBuilderAction(std::vector<std::unique_ptr<ASTUnit>> &ASTs) : ASTs(ASTs) {}
576 bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
578 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
580 std::unique_ptr<ASTUnit> AST = ASTUnit::LoadFromCompilerInvocation(
581 Invocation, std::move(PCHContainerOps),
589 ASTs.push_back(std::move(AST));
597 ASTBuilderAction Action(ASTs);
602 this->RestoreCWD = RestoreCWD;
606 this->PrintErrorMessage = PrintErrorMessage;
612 std::unique_ptr<ASTUnit>
614 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
616 "clang-tool", std::move(PCHContainerOps));
620 StringRef Code,
const std::vector<std::string> &Args, StringRef FileName,
621 StringRef ToolName, std::shared_ptr<PCHContainerOperations> PCHContainerOps,
623 std::vector<std::unique_ptr<ASTUnit>> ASTs;
624 ASTBuilderAction Action(ASTs);
626 new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
628 new llvm::vfs::InMemoryFileSystem);
629 OverlayFileSystem->pushOverlay(InMemoryFileSystem);
635 &Action, Files.get(), std::move(PCHContainerOps));
637 InMemoryFileSystem->addFile(FileName, 0,
638 llvm::MemoryBuffer::getMemBufferCopy(Code));
639 for (
auto &FilenameWithContent : VirtualMappedFiles) {
640 InMemoryFileSystem->addFile(
641 FilenameWithContent.first, 0,
642 llvm::MemoryBuffer::getMemBuffer(FilenameWithContent.second));
645 if (!Invocation.run())
648 assert(ASTs.size() == 1);
649 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 ...
DiagnosticBuilder Report(SourceLocation Loc, unsigned DiagID)
Issue the message to the client.
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.
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.
const llvm::opt::OptTable & getDriverOptTable()
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.
static bool CreateFromArgs(CompilerInvocation &Res, ArrayRef< const char *> CommandLineArgs, DiagnosticsEngine &Diags)
Create a compiler invocation from a list of input options.
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.
ActionList & getActions()
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