clang: lib/Tooling/Tooling.cpp Source File (original) (raw)
1
2
3
4
5
6
7
8
9
10
11
12
13
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/CommandLine.h"
47#include "llvm/Support/Debug.h"
48#include "llvm/Support/ErrorHandling.h"
49#include "llvm/Support/FileSystem.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"
54#include "llvm/TargetParser/Host.h"
55#include
56#include
57#include
58#include
59#include <system_error>
60#include
61#include
62
63#define DEBUG_TYPE "clang-tooling"
64
65using namespace clang;
66using namespace tooling;
67
69
71
72
73
74
75
76
81 new driver::Driver(BinaryName, llvm::sys::getDefaultTargetTriple(),
82 *Diagnostics, "clang LLVM compiler", std::move(VFS));
83 CompilerDriver->setTitle("clang_based_tool");
84 return CompilerDriver;
85}
86
87
91
92 bool OffloadCompilation = false;
93
94
95
96
97 for (const auto &Job : Jobs)
98 if (StringRef(Job.getExecutable()) == "clang-offload-bundler")
99 OffloadCompilation = true;
100
101 if (Jobs.size() > 1) {
102 for (auto *A : Actions){
103
104 if (isadriver::BindArchAction(A))
105 A = *A->input_begin();
106 if (isadriver::OffloadAction(A)) {
107
108
109
110
111
112
113 assert(Actions.size() > 1);
114 assert(
115 isadriver::CompileJobAction(Actions.front()) ||
116
117
118 (isadriver::BindArchAction(Actions.front()) &&
119 isadriver::CompileJobAction(*Actions.front()->input_begin())));
120 OffloadCompilation = true;
121 break;
122 }
123 }
124 }
125
126 return OffloadCompilation;
127}
128
130namespace tooling {
131
132const llvm::opt::ArgStringList *
136
138 return StringRef(Cmd.getCreator().getName()) == "clang";
139 };
140
142 return isSrcFile(II.getType());
143 };
144
147 if (IsCC1Command(Job) && llvm::all_of(Job.getInputInfos(), IsSrcFile))
148 CC1Jobs.push_back(&Job);
149
150
151
152 if (CC1Jobs.empty())
154 if (IsCC1Command(Job))
155 CC1Jobs.push_back(&Job);
156
157 if (CC1Jobs.empty() ||
160 llvm::raw_svector_ostream error_stream(error_msg);
161 Jobs.Print(error_stream, "; ", true);
162 Diagnostics->Report(diag::err_fe_expected_compiler_job)
163 << error_stream.str();
164 return nullptr;
165 }
166
167 return &CC1Jobs[0]->getArguments();
168}
169
170
173 const char *const BinaryName) {
174 assert(!CC1Args.empty() && "Must at least contain the program name!");
177 BinaryName);
180 return Invocation;
181}
182
184 const Twine &Code, const Twine &FileName,
185 std::shared_ptr PCHContainerOps) {
187 std::vectorstd::string(), FileName,
188 "clang-tool", std::move(PCHContainerOps));
189}
190
191}
192}
193
194static std::vectorstd::string
196 const std::vectorstd::string &ExtraArgs,
198 std::vectorstd::string Args;
199 Args.push_back(ToolName.str());
200 Args.push_back("-fsyntax-only");
201 Args.insert(Args.end(), ExtraArgs.begin(), ExtraArgs.end());
202 Args.push_back(FileName.str());
203 return Args;
204}
205
207namespace tooling {
208
210 std::unique_ptr ToolAction, const Twine &Code,
212 const std::vectorstd::string &Args, const Twine &FileName,
213 const Twine &ToolName,
214 std::shared_ptr PCHContainerOps) {
216 StringRef FileNameRef = FileName.toNullTerminatedStringRef(FileNameStorage);
217
223 std::move(ToolAction), Files.get(), std::move(PCHContainerOps));
224 return Invocation.run();
225}
226
228 std::unique_ptr ToolAction, const Twine &Code,
229 const std::vectorstd::string &Args, const Twine &FileName,
230 const Twine &ToolName,
231 std::shared_ptr PCHContainerOps,
234 new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
236 new llvm::vfs::InMemoryFileSystem);
237 OverlayFileSystem->pushOverlay(InMemoryFileSystem);
238
240 InMemoryFileSystem->addFile(FileName, 0,
241 llvm::MemoryBuffer::getMemBuffer(
242 Code.toNullTerminatedStringRef(CodeStorage)));
243
244 for (auto &FilenameWithContent : VirtualMappedFiles) {
245 InMemoryFileSystem->addFile(
246 FilenameWithContent.first, 0,
247 llvm::MemoryBuffer::getMemBuffer(FilenameWithContent.second));
248 }
249
252}
253
255 StringRef File) {
256 StringRef RelativePath(File);
257
258 RelativePath.consume_front("./");
259
261 if (auto EC = FS.makeAbsolute(AbsolutePath))
262 return llvm::errorCodeToError(EC);
263 llvm::sys::path::native(AbsolutePath);
264 return std::string(AbsolutePath);
265}
266
268 return llvm::cantFail(getAbsolutePath(*llvm::vfs::getRealFileSystem(), File));
269}
270
272 StringRef InvokedAs) {
273 if (CommandLine.empty() || InvokedAs.empty())
274 return;
276
277 StringRef TargetOPT =
278 Table.getOption(driver::options::OPT_target).getPrefixedName();
279
280 StringRef TargetOPTLegacy =
281 Table.getOption(driver::options::OPT_target_legacy_spelling)
282 .getPrefixedName();
283
284 StringRef DriverModeOPT =
285 Table.getOption(driver::options::OPT_driver_mode).getPrefixedName();
286 auto TargetMode =
288
289 bool ShouldAddTarget = TargetMode.TargetIsValid;
290 bool ShouldAddMode = TargetMode.DriverMode != nullptr;
291
292 for (auto Token = ++CommandLine.begin(); Token != CommandLine.end();
294 StringRef TokenRef(*Token);
295 ShouldAddTarget = ShouldAddTarget && !TokenRef.starts_with(TargetOPT) &&
296 TokenRef != TargetOPTLegacy;
297 ShouldAddMode = ShouldAddMode && !TokenRef.starts_with(DriverModeOPT);
298 }
299 if (ShouldAddMode) {
300 CommandLine.insert(++CommandLine.begin(), TargetMode.DriverMode);
301 }
302 if (ShouldAddTarget) {
303 CommandLine.insert(++CommandLine.begin(),
304 (TargetOPT + TargetMode.TargetPrefix).str());
305 }
306}
307
309 llvm::StringRef WorkingDir,
310 llvm:🆑:TokenizerCallback Tokenizer,
311 llvm::vfs::FileSystem &FS) {
312 bool SeenRSPFile = false;
314 Argv.reserve(CommandLine.size());
315 for (auto &Arg : CommandLine) {
316 Argv.push_back(Arg.c_str());
317 if (!Arg.empty())
318 SeenRSPFile |= Arg.front() == '@';
319 }
320 if (!SeenRSPFile)
321 return;
322 llvm::BumpPtrAllocator Alloc;
323 llvm:🆑:ExpansionContext ECtx(Alloc, Tokenizer);
324 llvm::Error Err =
325 ECtx.setVFS(&FS).setCurrentDir(WorkingDir).expandResponseFiles(Argv);
326 if (Err)
327 llvm::errs() << Err;
328
329 std::vectorstd::string ExpandedArgv(Argv.begin(), Argv.end());
330 CommandLine = std::move(ExpandedArgv);
331}
332
333}
334}
335
336namespace {
337
339 std::unique_ptr Action;
340
341public:
342 SingleFrontendActionFactory(std::unique_ptr Action)
343 : Action(std::move(Action)) {}
344
345 std::unique_ptr create() override {
346 return std::move(Action);
347 }
348};
349
350}
351
353 std::vectorstd::string CommandLine, ToolAction *Action,
354 FileManager *Files, std::shared_ptr PCHContainerOps)
355 : CommandLine(std::move(CommandLine)), Action(Action), OwnsAction(false),
356 Files(Files), PCHContainerOps(std::move(PCHContainerOps)) {}
357
359 std::vectorstd::string CommandLine,
360 std::unique_ptr FAction, FileManager *Files,
361 std::shared_ptr PCHContainerOps)
362 : CommandLine(std::move(CommandLine)),
363 Action(new SingleFrontendActionFactory(std::move(FAction))),
364 OwnsAction(true), Files(Files),
365 PCHContainerOps(std::move(PCHContainerOps)) {}
366
368 if (OwnsAction)
369 delete Action;
370}
371
373 llvm::opt::ArgStringList Argv;
374 for (const std::string &Str : CommandLine)
375 Argv.push_back(Str.c_str());
376 const char *const BinaryName = Argv[0];
377
378
379
382 if (!DiagOpts) {
384 DiagOpts = &*ParsedDiagOpts;
385 }
386
391 DiagConsumer ? DiagConsumer : &DiagnosticPrinter, false);
392
393
395 Diagnostics->setSourceManager(&SrcMgr);
396
397
398 if (CommandLine.size() >= 2 && CommandLine[1] == "-cc1") {
400 std::unique_ptr Invocation(
401 newInvocation(&*Diagnostics, CC1Args, BinaryName));
402 if (Diagnostics->hasErrorOccurred())
403 return false;
404 return Action->runInvocation(std::move(Invocation), Files,
405 std::move(PCHContainerOps), DiagConsumer);
406 }
407
408 const std::unique_ptrdriver::Driver Driver(
410
411
412
413
415 Driver->setCheckInputsExist(false);
416 const std::unique_ptrdriver::Compilation Compilation(
418 if (!Compilation)
419 return false;
420 const llvm::opt::ArgStringList *const CC1Args = getCC1Arguments(
421 &*Diagnostics, Compilation.get());
422 if (!CC1Args)
423 return false;
424 std::unique_ptr Invocation(
425 newInvocation(&*Diagnostics, *CC1Args, BinaryName));
426 return runInvocation(BinaryName, Compilation.get(), std::move(Invocation),
427 std::move(PCHContainerOps));
428}
429
430bool ToolInvocation::runInvocation(
432 std::shared_ptr Invocation,
433 std::shared_ptr PCHContainerOps) {
434
435 if (Invocation->getHeaderSearchOpts().Verbose) {
436 llvm::errs() << "clang Invocation:\n";
437 Compilation->getJobs().Print(llvm::errs(), "\n", true);
438 llvm::errs() << "\n";
439 }
440
441 return Action->runInvocation(std::move(Invocation), Files,
442 std::move(PCHContainerOps), DiagConsumer);
443}
444
446 std::shared_ptr Invocation, FileManager *Files,
447 std::shared_ptr PCHContainerOps,
449
453
454
455
456
457 std::unique_ptr ScopedToolAction(create());
458
459
461 false);
463 return false;
464
466
468
471}
472
475 std::shared_ptr PCHContainerOps,
478 : Compilations(Compilations), SourcePaths(SourcePaths),
479 PCHContainerOps(std::move(PCHContainerOps)),
480 OverlayFileSystem(new llvm::vfs::OverlayFileSystem(std::move(BaseFS))),
481 InMemoryFileSystem(new llvm::vfs::InMemoryFileSystem),
482 Files(Files ? Files
484 OverlayFileSystem->pushOverlay(InMemoryFileSystem);
488 if (Files)
489 Files->setVirtualFileSystem(OverlayFileSystem);
490}
491
493
495 MappedFileContents.push_back(std::make_pair(FilePath, Content));
496}
497
499 ArgsAdjuster = combineAdjusters(std::move(ArgsAdjuster), std::move(Adjuster));
500}
501
503 ArgsAdjuster = nullptr;
504}
505
507 void *MainAddr) {
508
509 for (StringRef Arg : Args)
510 if (Arg.starts_with("-resource-dir"))
511 return;
512
513
516 .c_str())(Args, "");
517}
518
520
521
522 static int StaticSymbol;
523
524
525
526 if (SeenWorkingDirectories.insert("/").second)
527 for (const auto &MappedFile : MappedFileContents)
528 if (llvm::sys::path::is_absolute(MappedFile.first))
529 InMemoryFileSystem->addFile(
530 MappedFile.first, 0,
531 llvm::MemoryBuffer::getMemBuffer(MappedFile.second));
532
533 bool ProcessingFailed = false;
534 bool FileSkipped = false;
535
536
537 std::vectorstd::string AbsolutePaths;
538 AbsolutePaths.reserve(SourcePaths.size());
539 for (const auto &SourcePath : SourcePaths) {
540 auto AbsPath = getAbsolutePath(*OverlayFileSystem, SourcePath);
541 if (!AbsPath) {
542 llvm::errs() << "Skipping " << SourcePath
543 << ". Error while getting an absolute path: "
544 << llvm::toString(AbsPath.takeError()) << "\n";
545 continue;
546 }
547 AbsolutePaths.push_back(std::move(*AbsPath));
548 }
549
550
551 std::string InitialWorkingDir;
552 if (auto CWD = OverlayFileSystem->getCurrentWorkingDirectory()) {
553 InitialWorkingDir = std::move(*CWD);
554 } else {
555 llvm::errs() << "Could not get working directory: "
556 << CWD.getError().message() << "\n";
557 }
558
559 size_t NumOfTotalFiles = AbsolutePaths.size();
560 unsigned ProcessedFileCounter = 0;
561 for (llvm::StringRef File : AbsolutePaths) {
562
563
564
565
566
567
568
569 std::vector CompileCommandsForFile =
571 if (CompileCommandsForFile.empty()) {
572 llvm::errs() << "Skipping " << File << ". Compile command not found.\n";
573 FileSkipped = true;
574 continue;
575 }
577
578
579
580
581
582
583
584 if (OverlayFileSystem->setCurrentWorkingDirectory(
586 llvm::report_fatal_error("Cannot chdir into \"" +
588
589
590
591
593 for (const auto &MappedFile : MappedFileContents)
594 if (!llvm::sys::path::is_absolute(MappedFile.first))
595 InMemoryFileSystem->addFile(
596 MappedFile.first, 0,
597 llvm::MemoryBuffer::getMemBuffer(MappedFile.second));
598
600 if (ArgsAdjuster)
602 assert(!CommandLine.empty());
603
604
605
606
607
608
609
610
611
613
614
615
616 if (NumOfTotalFiles > 1)
617 llvm::errs() << "[" + std::to_string(++ProcessedFileCounter) + "/" +
618 std::to_string(NumOfTotalFiles) +
619 "] Processing file " + File
620 << ".\n";
621 ToolInvocation Invocation(std::move(CommandLine), Action, Files.get(),
622 PCHContainerOps);
624
625 if (!Invocation.run()) {
626
627 if (PrintErrorMessage)
628 llvm::errs() << "Error while processing " << File << ".\n";
629 ProcessingFailed = true;
630 }
631 }
632 }
633
634 if (!InitialWorkingDir.empty()) {
635 if (auto EC =
636 OverlayFileSystem->setCurrentWorkingDirectory(InitialWorkingDir))
637 llvm::errs() << "Error when trying to restore working dir: "
638 << EC.message() << "\n";
639 }
640 return ProcessingFailed ? 1 : (FileSkipped ? 2 : 0);
641}
642
643namespace {
644
645class ASTBuilderAction : public ToolAction {
646 std::vector<std::unique_ptr> &ASTs;
647
648public:
649 ASTBuilderAction(std::vector<std::unique_ptr> &ASTs) : ASTs(ASTs) {}
650
651 bool runInvocation(std::shared_ptr Invocation,
653 std::shared_ptr PCHContainerOps,
655 std::unique_ptr AST = ASTUnit::LoadFromCompilerInvocation(
656 Invocation, std::move(PCHContainerOps),
658 &Invocation->getDiagnosticOpts(),
659 DiagConsumer,
660 false),
661 Files);
662 if (!AST)
663 return false;
664
665 ASTs.push_back(std::move(AST));
666 return true;
667 }
668};
669
670}
671
673 ASTBuilderAction Action(ASTs);
674 return run(&Action);
675}
676
678 this->PrintErrorMessage = PrintErrorMessage;
679}
680
682namespace tooling {
683
684std::unique_ptr
686 std::shared_ptr PCHContainerOps) {
688 "clang-tool", std::move(PCHContainerOps));
689}
690
692 StringRef Code, const std::vectorstd::string &Args, StringRef FileName,
693 StringRef ToolName, std::shared_ptr PCHContainerOps,
697 std::vector<std::unique_ptr> ASTs;
698 ASTBuilderAction Action(ASTs);
700 new llvm::vfs::OverlayFileSystem(std::move(BaseFS)));
702 new llvm::vfs::InMemoryFileSystem);
703 OverlayFileSystem->pushOverlay(InMemoryFileSystem);
706
709 &Action, Files.get(), std::move(PCHContainerOps));
711
712 InMemoryFileSystem->addFile(FileName, 0,
713 llvm::MemoryBuffer::getMemBufferCopy(Code));
714 for (auto &FilenameWithContent : VirtualMappedFiles) {
715 InMemoryFileSystem->addFile(
716 FilenameWithContent.first, 0,
717 llvm::MemoryBuffer::getMemBuffer(FilenameWithContent.second));
718 }
719
720 if (!Invocation.run())
721 return nullptr;
722
723 assert(ASTs.size() == 1);
724 return std::move(ASTs[0]);
725}
726
727}
728}
Defines the Diagnostic-related interfaces.
Defines the Diagnostic IDs-related interfaces.
Defines the clang::FileManager interface and associated types.
Defines the clang::FileSystemOptions interface.
Forward-declares and imports various common LLVM datatypes that clang wants to use unqualified.
CompilerInstance - Helper class for managing a single instance of the Clang compiler.
void setFileManager(FileManager *Value)
Replace the current file manager and virtual file system.
void createSourceManager(FileManager &FileMgr)
Create the source manager and replace any existing one with it.
void createDiagnostics(llvm::vfs::FileSystem &VFS, DiagnosticConsumer *Client=nullptr, bool ShouldOwnClient=true)
Create the diagnostics engine using the invocation's diagnostic options and replace any existing one ...
void setInvocation(std::shared_ptr< CompilerInvocation > Value)
setInvocation - Replace the current invocation.
bool hasDiagnostics() const
bool ExecuteAction(FrontendAction &Act)
ExecuteAction - Execute the provided action against the compiler's CompilerInvocation object.
Helper class for holding the data necessary to invoke the compiler.
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...
static bool CreateFromArgs(CompilerInvocation &Res, ArrayRef< const char * > CommandLineArgs, DiagnosticsEngine &Diags, const char *Argv0=nullptr)
Create a compiler invocation from a list of input options.
FrontendOptions & getFrontendOpts()
CodeGenOptions & getCodeGenOpts()
Abstract interface, implemented by clients of the front-end, which formats and prints fully processed...
Options for controlling the compiler diagnostics engine.
Concrete class used by the front-end to report problems and issues.
DiagnosticBuilder Report(SourceLocation Loc, unsigned DiagID)
Issue the message to the client.
Implements support for file system lookup, file system caching, and directory search management.
void clearStatCache()
Removes the FileSystemStatCache object from the manager.
llvm::vfs::FileSystem & getVirtualFileSystem() const
FileSystemOptions & getFileSystemOpts()
Returns the current file system options.
Keeps track of options that affect how file operations are performed.
std::string WorkingDir
If set, paths are resolved as if the working directory was set to the value of WorkingDir.
unsigned DisableFree
Disable memory freeing on exit.
This class handles loading and caching of source files into memory.
Token - This structure provides full information about a lexed token.
Command - An executable path/name and argument vector to execute.
Compilation - A set of tasks to perform for a single driver invocation.
ActionList & getActions()
Driver - Encapsulate logic for constructing compilation processes from a set of gcc-driver-like comma...
void setTitle(std::string Value)
InputInfo - Wrapper for information about an input source.
JobList - A sequence of jobs to perform.
void Print(llvm::raw_ostream &OS, const char *Terminator, bool Quote, CrashReportInfo *CrashInfo=nullptr) const
const llvm::opt::OptTable & getDriverOptTable()
The JSON file list parser is used to communicate input to InstallAPI.
std::unique_ptr< DiagnosticOptions > CreateAndPopulateDiagOpts(ArrayRef< const char * > Argv)
@ Success
Template argument deduction was successful.
Diagnostic wrappers for TextAPI types for error reporting.