clang: lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp Source File (original) (raw)
1
2
3
4
5
6
7
8
27#include "llvm/ADT/ScopeExit.h"
28#include "llvm/Support/Allocator.h"
29#include "llvm/Support/Error.h"
30#include "llvm/TargetParser/Host.h"
31#include
32
33using namespace clang;
34using namespace tooling;
35using namespace dependencies;
36
37namespace {
38
39
41public:
42 DependencyConsumerForwarder(std::unique_ptr Opts,
45 Opts(std::move(Opts)), C(C) {}
46
48 C.handleDependencyOutputOpts(*Opts);
51 CanonPath = File;
52 llvm::sys::path::remove_dots(CanonPath, true);
53 llvm::sys::fs::make_absolute(WorkingDirectory, CanonPath);
54 C.handleFileDependency(CanonPath);
55 }
56 }
57
58private:
59 StringRef WorkingDirectory;
60 std::unique_ptr Opts;
62};
63
68 if (LangOpts.Modules) {
70 if (Diags) {
71 Diags->Report(diag::warn_pch_vfsoverlay_mismatch);
73 if (VFSOverlays.empty()) {
74 Diags->Report(diag::note_pch_vfsoverlay_empty) << Type;
75 } else {
76 std::string Files = llvm::join(VFSOverlays, "\n");
77 Diags->Report(diag::note_pch_vfsoverlay_files) << Type << Files;
78 }
79 };
82 }
83 }
84 }
85 return false;
86}
87
89
90
91
93public:
94 PrebuiltModuleListener(PrebuiltModuleFilesT &PrebuiltModuleFiles,
99 : PrebuiltModuleFiles(PrebuiltModuleFiles),
100 NewModuleFiles(NewModuleFiles),
101 PrebuiltModuleVFSMap(PrebuiltModuleVFSMap), ExistingHSOpts(HSOpts),
102 ExistingLangOpts(LangOpts), Diags(Diags) {}
103
105
107 if (PrebuiltModuleFiles.insert({ModuleName.str(), Filename.str()}).second)
108 NewModuleFiles.push_back(Filename.str());
109 }
110
114 }
115
117 bool Complain) override {
118 std::vectorstd::string VFSOverlayFiles = HSOpts.VFSOverlayFiles;
119 PrebuiltModuleVFSMap.insert(
120 {CurrentFile, llvm::StringSet<>(VFSOverlayFiles)});
121 return checkHeaderSearchPaths(
122 HSOpts, ExistingHSOpts, Complain ? &Diags : nullptr, ExistingLangOpts);
123 }
124
125private:
126 PrebuiltModuleFilesT &PrebuiltModuleFiles;
132 std::string CurrentFile;
133};
134
135
136
137static bool visitPrebuiltModule(StringRef PrebuiltModuleFilename,
139 PrebuiltModuleFilesT &ModuleFiles,
142
144 PrebuiltModuleListener Listener(ModuleFiles, Worklist, PrebuiltModuleVFSMap,
146 Diags);
147
148 Listener.visitModuleFile(PrebuiltModuleFilename,
153 false, Listener,
155 return true;
156
157 while (!Worklist.empty()) {
162 false, Listener,
163 false))
164 return true;
165 }
166 return false;
167}
168
169
170static std::string makeObjFileName(StringRef FileName) {
172 llvm::sys::path::replace_extension(ObjFileName, "o");
173 return std::string(ObjFileName);
174}
175
176
177static std::string
178deduceDepTarget(const std::string &OutputFile,
180 if (OutputFile != "-")
181 return OutputFile;
182
183 if (InputFiles.empty() || !InputFiles.front().isFile())
184 return "clang-scan-deps\\ dependency";
185
186 return makeObjFileName(InputFiles.front().getFile());
187}
188
189
191
192 DiagOpts.ShowCarets = false;
193
195
196
197
198
199
200
201
202 llvm::erase_if(DiagOpts.Warnings, [](StringRef Warning) {
203 return llvm::StringSwitch(Warning)
204 .Cases("pch-vfs-diff", "error=pch-vfs-diff", false)
205 .StartsWith("no-error=", false)
206 .Default(true);
207 });
208}
209
210
211
212
213
214
215
216
217
218
219
220static std::optional getSimpleMacroName(StringRef Macro) {
221 StringRef Name = Macro.split("=").first.ltrim(" \t");
222 std::size_t I = 0;
223
224 auto FinishName = [&]() -> std::optional {
225 StringRef SimpleName = Name.slice(0, I);
226 if (SimpleName.empty())
227 return std::nullopt;
228 return SimpleName;
229 };
230
231 for (; I != Name.size(); ++I) {
232 switch (Name[I]) {
233 case '(':
234 case ' ':
235 case '\t':
236 return FinishName();
237 case '_':
238 continue;
239 default:
240 if (llvm::isAlnum(Name[I]))
241 continue;
242 return std::nullopt;
243 }
244 }
245 return FinishName();
246}
247
249 using MacroOpt = std::pair<StringRef, std::size_t>;
250 std::vector SimpleNames;
251 SimpleNames.reserve(PPOpts.Macros.size());
252 std::size_t Index = 0;
253 for (const auto &M : PPOpts.Macros) {
254 auto SName = getSimpleMacroName(M.first);
255
256 if (!SName)
257 return;
258 SimpleNames.emplace_back(*SName, Index);
259 ++Index;
260 }
261
262 llvm::stable_sort(SimpleNames, llvm::less_first());
263
264 auto NewEnd = std::unique(
265 SimpleNames.rbegin(), SimpleNames.rend(),
266 [](const MacroOpt &A, const MacroOpt &B) { return A.first == B.first; });
267 SimpleNames.erase(SimpleNames.begin(), NewEnd.base());
268
269
270 decltype(PPOpts.Macros) NewMacros;
271 NewMacros.reserve(SimpleNames.size());
272 for (std::size_t I = 0, E = SimpleNames.size(); I != E; ++I) {
273 std::size_t OriginalIndex = SimpleNames[I].second;
274
275 NewMacros.push_back(std::move(PPOpts.Macros[OriginalIndex]));
276 }
277 std::swap(PPOpts.Macros, NewMacros);
278}
279
280
281
283public:
284 DependencyScanningAction(
289 bool EagerLoadModules, bool DisableFree,
290 std::optional ModuleName = std::nullopt)
291 : WorkingDirectory(WorkingDirectory), Consumer(Consumer),
292 Controller(Controller), DepFS(std::move(DepFS)), Format(Format),
293 OptimizeArgs(OptimizeArgs), EagerLoadModules(EagerLoadModules),
294 DisableFree(DisableFree), ModuleName(ModuleName) {}
295
296 bool runInvocation(std::shared_ptr Invocation,
298 std::shared_ptr PCHContainerOps,
300
302
303 OriginalInvocation.getFrontendOpts().DisableFree = DisableFree;
304 if (any(OptimizeArgs & ScanningOptimizations::Macros))
305 canonicalizeDefines(OriginalInvocation.getPreprocessorOpts());
306
307 if (Scanned) {
308
309
310
311
312 setLastCC1Arguments(std::move(OriginalInvocation));
313 return true;
314 }
315
316 Scanned = true;
317
318
319 ScanInstanceStorage.emplace(std::move(PCHContainerOps));
321 ScanInstance.setInvocation(std::move(Invocation));
322
323
326 DiagConsumer, false);
328 return false;
329
330
331 auto DiagConsumerFinisher =
332 llvm::make_scope_exit([DiagConsumer]() { DiagConsumer->finish(); });
333
335 true;
336
339
340
344 any(OptimizeArgs & ScanningOptimizations::VFS);
345
346
350
351
352 if (DepFS) {
353 StringRef ModulesCachePath =
355
356 DepFS->resetBypassedPathPrefix();
357 if (!ModulesCachePath.empty())
358 DepFS->setBypassedPathPrefix(ModulesCachePath);
359
363 if (llvm::ErrorOr Entry =
364 LocalDepFS->getOrCreateFileSystemEntry(File.getName()))
365 if (LocalDepFS->ensureDirectiveTokensArePopulated(*Entry))
366 return Entry->getDirectiveTokens();
367 return std::nullopt;
368 };
369 }
370
371
374
375
376
377
380 if (visitPrebuiltModule(
382 ScanInstance,
384 PrebuiltModuleVFSMap, ScanInstance.getDiagnostics()))
385 return false;
386
387
388
389
390
391
392
393
394 auto Opts = std::make_unique();
396
397
398 if (Opts->Targets.empty())
399 Opts->Targets = {
402 Opts->IncludeSystemHeaders = true;
403
404 switch (Format) {
405 case ScanningOutputFormat::Make:
407 std::make_shared(
408 std::move(Opts), WorkingDirectory, Consumer));
409 break;
410 case ScanningOutputFormat::P1689:
411 case ScanningOutputFormat::Full:
412 MDC = std::make_shared(
413 std::move(Opts), ScanInstance, Consumer, Controller,
414 OriginalInvocation, std::move(PrebuiltModuleVFSMap), OptimizeArgs,
415 EagerLoadModules, Format == ScanningOutputFormat::P1689);
417 break;
418 }
419
420
421
422
423
424
430 true;
431
432
434
435 std::unique_ptr Action;
436
437 if (Format == ScanningOutputFormat::P1689)
438 Action = std::make_unique();
439 else if (ModuleName)
440 Action = std::make_unique(*ModuleName);
441 else
442 Action = std::make_unique();
443
445 return false;
446
447
448 DiagConsumerFinisher.release();
449 const bool Result = ScanInstance.ExecuteAction(*Action);
450
451 if (Result)
452 setLastCC1Arguments(std::move(OriginalInvocation));
453
454
456
458 }
459
460 bool hasScanned() const { return Scanned; }
461
462
463
464
465 std::vectorstd::string takeLastCC1Arguments() {
466 std::vectorstd::string Result;
467 std::swap(Result, LastCC1Arguments);
469 }
470
471private:
473 if (MDC)
474 MDC->applyDiscoveredDependencies(CI);
475 LastCC1Arguments = CI.getCC1CommandLine();
476 }
477
478private:
479 StringRef WorkingDirectory;
485 bool EagerLoadModules;
486 bool DisableFree;
487 std::optional ModuleName;
488 std::optional ScanInstanceStorage;
489 std::shared_ptr MDC;
490 std::vectorstd::string LastCC1Arguments;
491 bool Scanned = false;
492};
493
494}
495
499 : Format(Service.getFormat()), OptimizeArgs(Service.getOptimizeArgs()),
500 EagerLoadModules(Service.shouldEagerLoadModules()) {
501 PCHContainerOps = std::make_shared();
502
503 PCHContainerOps->registerReader(
504 std::make_unique());
505
506 PCHContainerOps->registerWriter(std::make_unique());
507
509 FS = llvm::makeIntrusiveRefCntllvm::vfs::TracingFileSystem(std::move(FS));
510
511 switch (Service.getMode()) {
513 DepFS =
515 BaseFS = DepFS;
516 break;
518 DepFS = nullptr;
519 BaseFS = FS;
520 break;
521 }
522}
523
525 StringRef WorkingDirectory, const std::vectorstd::string &CommandLine,
527 std::optional ModuleName) {
528 std::vector<const char *> CLI;
529 for (const std::string &Arg : CommandLine)
530 CLI.push_back(Arg.c_str());
532 sanitizeDiagOpts(*DiagOpts);
533
534
535
536 std::string DiagnosticOutput;
537 llvm::raw_string_ostream DiagnosticsOS(DiagnosticOutput);
539
540 if (computeDependencies(WorkingDirectory, CommandLine, Consumer, Controller,
541 DiagPrinter, ModuleName))
542 return llvm::Error::success();
543 return llvm::make_errorllvm::StringError(DiagnosticsOS.str(),
544 llvm::inconvertibleErrorCode());
545}
546
551 Argv.reserve(ArgStrs.size());
552 for (const std::string &Arg : ArgStrs)
553 Argv.push_back(Arg.c_str());
554
556
557 std::unique_ptrdriver::Driver Driver = std::make_uniquedriver::Driver(
558 Argv[0], llvm::sys::getDefaultTargetTriple(), Diags,
559 "clang LLVM compiler", FS);
560 Driver->setTitle("clang_based_tool");
561
562 llvm::BumpPtrAllocator Alloc;
565
567 Diags.Report(diag::err_drv_expand_response_file)
568 << llvm::toString(std::move(E));
569 return false;
570 }
571
572 const std::unique_ptrdriver::Compilation Compilation(
574 if (!Compilation)
575 return false;
576
577 if (Compilation->containsError())
578 return false;
579
580 for (const driver::Command &Job : Compilation->getJobs()) {
581 if (!Callback(Job))
582 return false;
583 }
584 return true;
585}
586
588 std::vectorstd::string CommandLine, DependencyScanningAction &Action,
590 std::shared_ptrclang::PCHContainerOperations &PCHContainerOps,
592
593
594 std::string Executable = CommandLine[0];
595 ToolInvocation Invocation(std::move(CommandLine), &Action, &FM,
596 PCHContainerOps);
599 if (!Invocation.run())
600 return false;
601
602 std::vectorstd::string Args = Action.takeLastCC1Arguments();
603 Consumer.handleBuildCommand({std::move(Executable), std::move(Args)});
604 return true;
605}
606
608 StringRef WorkingDirectory, const std::vectorstd::string &CommandLine,
611
612 BaseFS->setCurrentWorkingDirectory(WorkingDirectory);
613
614 std::optional<std::vectorstd::string> ModifiedCommandLine;
616
617
618
619
620 if (ModuleName) {
621 auto OverlayFS =
622 llvm::makeIntrusiveRefCntllvm::vfs::OverlayFileSystem(BaseFS);
623 auto InMemoryFS =
624 llvm::makeIntrusiveRefCntllvm::vfs::InMemoryFileSystem();
625 InMemoryFS->setCurrentWorkingDirectory(WorkingDirectory);
626 OverlayFS->pushOverlay(InMemoryFS);
627 ModifiedFS = OverlayFS;
628
630
631 llvm::sys::fs::createUniquePath(*ModuleName + "-%%%%%%%%.input",
632 FakeInputPath,
633 false);
634 InMemoryFS->addFile(FakeInputPath, 0, llvm::MemoryBuffer::getMemBuffer(""));
635
636 ModifiedCommandLine = CommandLine;
637 ModifiedCommandLine->emplace_back(FakeInputPath);
638 }
639
640 const std::vectorstd::string &FinalCommandLine =
641 ModifiedCommandLine ? *ModifiedCommandLine : CommandLine;
642 auto &FinalFS = ModifiedFS ? ModifiedFS : BaseFS;
643
644 auto FileMgr =
645 llvm::makeIntrusiveRefCnt(FileSystemOptions{}, FinalFS);
646
647 std::vector<const char *> FinalCCommandLine(FinalCommandLine.size(), nullptr);
648 llvm::transform(FinalCommandLine, FinalCCommandLine.begin(),
649 [](const std::string &Str) { return Str.c_str(); });
650
652 sanitizeDiagOpts(*DiagOpts);
655 false);
656
657
658
660 Diags->setSourceManager(&SrcMgr);
661
662
663
664 bool DisableFree = true;
665 DependencyScanningAction Action(WorkingDirectory, Consumer, Controller, DepFS,
666 Format, OptimizeArgs, EagerLoadModules,
667 DisableFree, ModuleName);
668
670 if (FinalCommandLine[1] == "-cc1") {
672 PCHContainerOps, *Diags, Consumer);
673 } else {
675 FinalCommandLine, *Diags, *FileMgr, [&](const driver::Command &Cmd) {
676 if (StringRef(Cmd.getCreator().getName()) != "clang") {
677
678
679 Consumer.handleBuildCommand(
680 {Cmd.getExecutable(),
681 {Cmd.getArguments().begin(), Cmd.getArguments().end()}});
682 return true;
683 }
684
685
686 std::vectorstd::string Argv;
687 Argv.push_back(Cmd.getExecutable());
688 Argv.insert(Argv.end(), Cmd.getArguments().begin(),
689 Cmd.getArguments().end());
690
691
692
693
694
696 PCHContainerOps, *Diags, Consumer);
697 });
698 }
699
700 if (Success && !Action.hasScanned())
701 Diags->Report(diag::err_fe_expected_compiler_job)
702 << llvm::join(FinalCommandLine, " ");
703 return Success && Action.hasScanned();
704}
705
static bool forEachDriverJob(ArrayRef< std::string > ArgStrs, DiagnosticsEngine &Diags, FileManager &FM, llvm::function_ref< bool(const driver::Command &Cmd)> Callback)
static bool createAndRunToolInvocation(std::vector< std::string > CommandLine, DependencyScanningAction &Action, FileManager &FM, std::shared_ptr< clang::PCHContainerOperations > &PCHContainerOps, DiagnosticsEngine &Diags, DependencyConsumer &Consumer)
Abstract interface for callback invocations by the ASTReader.
virtual bool ReadHeaderSearchPaths(const HeaderSearchOptions &HSOpts, bool Complain)
Receives the header search paths.
virtual void visitImport(StringRef ModuleName, StringRef Filename)
If needsImportVisitation returns true, this is called for each AST file imported by this AST file.
virtual void visitModuleFile(StringRef Filename, serialization::ModuleKind Kind)
This is called for each AST file loaded.
virtual bool needsImportVisitation() const
Returns true if this ASTReaderListener wants to receive the imports of the AST file via visitImport,...
@ ARR_OutOfDate
The client can handle an AST file that cannot load because it is out-of-date relative to its input fi...
static bool readASTFileControlBlock(StringRef Filename, FileManager &FileMgr, const InMemoryModuleCache &ModuleCache, const PCHContainerReader &PCHContainerRdr, bool FindModuleFileExtensions, ASTReaderListener &Listener, bool ValidateDiagnosticOptions, unsigned ClientLoadCapabilities=ARR_ConfigurationMismatch|ARR_OutOfDate)
Read the control block for the named AST file.
CompilerInstance - Helper class for managing a single instance of the Clang compiler.
void createSourceManager(FileManager &FileMgr)
Create the source manager and replace any existing one with it.
FileManager * createFileManager(IntrusiveRefCntPtr< llvm::vfs::FileSystem > VFS=nullptr)
Create the file manager and replace any existing one with it.
const PCHContainerReader & getPCHContainerReader() const
Return the appropriate PCHContainerReader depending on the current CodeGenOptions.
DiagnosticsEngine & getDiagnostics() const
Get the current diagnostics engine.
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.
FileManager & getFileManager() const
Return the current file manager to the caller.
InMemoryModuleCache & getModuleCache() const
void addDependencyCollector(std::shared_ptr< DependencyCollector > Listener)
FrontendOptions & getFrontendOpts()
bool hasDiagnostics() const
HeaderSearchOptions & getHeaderSearchOpts()
CompilerInvocation & getInvocation()
PreprocessorOptions & getPreprocessorOpts()
bool ExecuteAction(FrontendAction &Act)
ExecuteAction - Execute the provided action against the compiler's CompilerInvocation object.
DiagnosticOptions & getDiagnosticOpts()
LangOptions & getLangOpts()
Helper class for holding the data necessary to invoke the compiler.
DependencyOutputOptions & getDependencyOutputOpts()
ArrayRef< std::string > getDependencies() const
Builds a dependency file when attached to a Preprocessor (for includes) and ASTReader (for module imp...
void finishedMainFile(DiagnosticsEngine &Diags) override
Called when the end of the main file is reached.
Abstract interface, implemented by clients of the front-end, which formats and prints fully processed...
virtual void finish()
Callback to inform the diagnostic client that processing of all source files has ended.
Options for controlling the compiler diagnostics engine.
std::vector< std::string > Warnings
The list of -W... options used to alter the diagnostic mappings, with the prefixes removed.
std::string DiagnosticSerializationFile
The file to serialize diagnostics to (non-appending).
Concrete class used by the front-end to report problems and issues.
DiagnosticBuilder Report(SourceLocation Loc, unsigned DiagID)
Issue the message to the client.
bool hasErrorOccurred() const
DiagnosticOptions & getDiagnosticOptions() const
Retrieve the diagnostic options.
DiagnosticConsumer * getClient()
A reference to a FileEntry that includes the name of the file as it was accessed by the FileManager's...
Implements support for file system lookup, file system caching, and directory search management.
void AddStats(const FileManager &Other)
Import statistics from a child FileManager and add them to this current FileManager.
llvm::vfs::FileSystem & getVirtualFileSystem() const
llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > getVirtualFileSystemPtr() const
Keeps track of options that affect how file operations are performed.
unsigned ModulesShareFileManager
Whether to share the FileManager when building modules.
std::string OutputFile
The output file, if any.
unsigned GenerateGlobalModuleIndex
Whether we can generate the global module index if needed.
SmallVector< FrontendInputFile, 0 > Inputs
The input files and their types.
unsigned UseGlobalModuleIndex
Whether we can use the global module index if available.
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
PreprocessorOptions - This class is used for passing the various options used in preprocessor initial...
bool ModulesCheckRelocated
Perform extra checks when loading PCM files for mutable file systems.
std::string ImplicitPCHInclude
The implicit PCH included at the start of the translation unit, or empty.
bool AllowPCHWithDifferentModulesCachePath
When true, a PCH with modules cache path different to the current compilation will not be rejected.
std::vector< std::pair< std::string, bool > > Macros
std::function< std::optional< ArrayRef< dependency_directives_scan::Directive > >(FileEntryRef)> DependencyDirectivesForFile
Function for getting the dependency preprocessor directives of a file.
This class handles loading and caching of source files into memory.
The base class of the type hierarchy.
Command - An executable path/name and argument vector to execute.
llvm::StringRef getDriverMode(StringRef ProgName, ArrayRef< const char * > Args)
Returns the driver mode option's value, i.e.
llvm::Error expandResponseFiles(SmallVectorImpl< const char * > &Args, bool ClangCLMode, llvm::BumpPtrAllocator &Alloc, llvm::vfs::FileSystem *FS=nullptr)
Expand response files from a clang driver or cc1 invocation.
bool IsClangCL(StringRef DriverMode)
Checks whether the value produced by getDriverMode is for CL mode.
ModuleKind
Specifies the kind of module that has been loaded.
@ MK_ExplicitModule
File is an explicitly-loaded module.
The JSON file list parser is used to communicate input to InstallAPI.
std::unique_ptr< DiagnosticOptions > CreateAndPopulateDiagOpts(ArrayRef< const char * > Argv)
@ Result
The result type of a method or function.
IntrusiveRefCntPtr< llvm::vfs::FileSystem > createVFSFromCompilerInvocation(const CompilerInvocation &CI, DiagnosticsEngine &Diags)
@ Success
Template argument deduction was successful.
int __ovld __cnfn any(char)
Returns 1 if the most significant bit in any component of x is set; otherwise returns 0.