1//===- ModuleDepCollector.cpp - Callbacks to collect deps -------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "clang/Tooling/DependencyScanning/ModuleDepCollector.h"
10
11#include "clang/Basic/MakeSupport.h"
12#include "clang/Frontend/CompilerInstance.h"
13#include "clang/Lex/Preprocessor.h"
14#include "clang/Tooling/DependencyScanning/DependencyScanningWorker.h"
15#include "llvm/ADT/STLExtras.h"
16#include "llvm/Support/BLAKE3.h"
17#include "llvm/Support/StringSaver.h"
18#include <optional>
19
20using namespace clang;
21using namespace tooling;
22using namespace dependencies;
23
24const std::vector<std::string> &ModuleDeps::getBuildArguments() {
25 assert(!std::holds_alternative<std::monostate>(BuildInfo) &&
26 "Using uninitialized ModuleDeps");
27 if (const auto *CI = std::get_if<CowCompilerInvocation>(ptr: &BuildInfo))
28 BuildInfo = CI->getCC1CommandLine();
29 return std::get<std::vector<std::string>>(v&: BuildInfo);
30}
31
32static void
33optimizeHeaderSearchOpts(HeaderSearchOptions &Opts, ASTReader &Reader,
34 const serialization::ModuleFile &MF,
35 const PrebuiltModuleVFSMapT &PrebuiltModuleVFSMap,
36 ScanningOptimizations OptimizeArgs) {
37 if (any(Val: OptimizeArgs & ScanningOptimizations::HeaderSearch)) {
38 // Only preserve search paths that were used during the dependency scan.
39 std::vector<HeaderSearchOptions::Entry> Entries;
40 std::swap(x&: Opts.UserEntries, y&: Entries);
41
42 llvm::BitVector SearchPathUsage(Entries.size());
43 llvm::DenseSet<const serialization::ModuleFile *> Visited;
44 std::function<void(const serialization::ModuleFile *)> VisitMF =
45 [&](const serialization::ModuleFile *MF) {
46 SearchPathUsage |= MF->SearchPathUsage;
47 Visited.insert(V: MF);
48 for (const serialization::ModuleFile *Import : MF->Imports)
49 if (!Visited.contains(V: Import))
50 VisitMF(Import);
51 };
52 VisitMF(&MF);
53
54 if (SearchPathUsage.size() != Entries.size())
55 llvm::report_fatal_error(
56 reason: "Inconsistent search path options between modules detected");
57
58 for (auto Idx : SearchPathUsage.set_bits())
59 Opts.UserEntries.push_back(x: std::move(Entries[Idx]));
60 }
61 if (any(Val: OptimizeArgs & ScanningOptimizations::VFS)) {
62 std::vector<std::string> VFSOverlayFiles;
63 std::swap(x&: Opts.VFSOverlayFiles, y&: VFSOverlayFiles);
64
65 llvm::BitVector VFSUsage(VFSOverlayFiles.size());
66 llvm::DenseSet<const serialization::ModuleFile *> Visited;
67 std::function<void(const serialization::ModuleFile *)> VisitMF =
68 [&](const serialization::ModuleFile *MF) {
69 Visited.insert(V: MF);
70 if (MF->Kind == serialization::MK_ImplicitModule) {
71 VFSUsage |= MF->VFSUsage;
72 // We only need to recurse into implicit modules. Other module types
73 // will have the correct set of VFSs for anything they depend on.
74 for (const serialization::ModuleFile *Import : MF->Imports)
75 if (!Visited.contains(V: Import))
76 VisitMF(Import);
77 } else {
78 // This is not an implicitly built module, so it may have different
79 // VFS options. Fall back to a string comparison instead.
80 auto VFSMap = PrebuiltModuleVFSMap.find(Key: MF->FileName);
81 if (VFSMap == PrebuiltModuleVFSMap.end())
82 return;
83 for (std::size_t I = 0, E = VFSOverlayFiles.size(); I != E; ++I) {
84 if (VFSMap->second.contains(key: VFSOverlayFiles[I]))
85 VFSUsage[I] = true;
86 }
87 }
88 };
89 VisitMF(&MF);
90
91 if (VFSUsage.size() != VFSOverlayFiles.size())
92 llvm::report_fatal_error(
93 reason: "Inconsistent -ivfsoverlay options between modules detected");
94
95 for (auto Idx : VFSUsage.set_bits())
96 Opts.VFSOverlayFiles.push_back(x: std::move(VFSOverlayFiles[Idx]));
97 }
98}
99
100static void optimizeDiagnosticOpts(DiagnosticOptions &Opts,
101 bool IsSystemModule) {
102 // If this is not a system module or -Wsystem-headers was passed, don't
103 // optimize.
104 if (!IsSystemModule)
105 return;
106 bool Wsystem_headers = false;
107 for (StringRef Opt : Opts.Warnings) {
108 bool isPositive = !Opt.consume_front(Prefix: "no-");
109 if (Opt == "system-headers")
110 Wsystem_headers = isPositive;
111 }
112 if (Wsystem_headers)
113 return;
114
115 // Remove all warning flags. System modules suppress most, but not all,
116 // warnings.
117 Opts.Warnings.clear();
118 Opts.UndefPrefixes.clear();
119 Opts.Remarks.clear();
120}
121
122static std::vector<std::string> splitString(std::string S, char Separator) {
123 SmallVector<StringRef> Segments;
124 StringRef(S).split(A&: Segments, Separator, /*MaxSplit=*/-1, /*KeepEmpty=*/false);
125 std::vector<std::string> Result;
126 Result.reserve(n: Segments.size());
127 for (StringRef Segment : Segments)
128 Result.push_back(x: Segment.str());
129 return Result;
130}
131
132void ModuleDepCollector::addOutputPaths(CowCompilerInvocation &CI,
133 ModuleDeps &Deps) {
134 CI.getMutFrontendOpts().OutputFile =
135 Controller.lookupModuleOutput(ID: Deps.ID, Kind: ModuleOutputKind::ModuleFile);
136 if (!CI.getDiagnosticOpts().DiagnosticSerializationFile.empty())
137 CI.getMutDiagnosticOpts().DiagnosticSerializationFile =
138 Controller.lookupModuleOutput(
139 ID: Deps.ID, Kind: ModuleOutputKind::DiagnosticSerializationFile);
140 if (!CI.getDependencyOutputOpts().OutputFile.empty()) {
141 CI.getMutDependencyOutputOpts().OutputFile = Controller.lookupModuleOutput(
142 ID: Deps.ID, Kind: ModuleOutputKind::DependencyFile);
143 CI.getMutDependencyOutputOpts().Targets =
144 splitString(S: Controller.lookupModuleOutput(
145 ID: Deps.ID, Kind: ModuleOutputKind::DependencyTargets),
146 Separator: '\0');
147 if (!CI.getDependencyOutputOpts().OutputFile.empty() &&
148 CI.getDependencyOutputOpts().Targets.empty()) {
149 // Fallback to -o as dependency target, as in the driver.
150 SmallString<128> Target;
151 quoteMakeTarget(Target: CI.getFrontendOpts().OutputFile, Res&: Target);
152 CI.getMutDependencyOutputOpts().Targets.push_back(x: std::string(Target));
153 }
154 }
155}
156
157void dependencies::resetBenignCodeGenOptions(frontend::ActionKind ProgramAction,
158 const LangOptions &LangOpts,
159 CodeGenOptions &CGOpts) {
160 // TODO: Figure out better way to set options to their default value.
161 if (ProgramAction == frontend::GenerateModule) {
162 CGOpts.MainFileName.clear();
163 CGOpts.DwarfDebugFlags.clear();
164 }
165 if (ProgramAction == frontend::GeneratePCH ||
166 (ProgramAction == frontend::GenerateModule && !LangOpts.ModulesCodegen)) {
167 CGOpts.DebugCompilationDir.clear();
168 CGOpts.CoverageCompilationDir.clear();
169 CGOpts.CoverageDataFile.clear();
170 CGOpts.CoverageNotesFile.clear();
171 CGOpts.ProfileInstrumentUsePath.clear();
172 CGOpts.SampleProfileFile.clear();
173 CGOpts.ProfileRemappingFile.clear();
174 }
175}
176
177static CowCompilerInvocation
178makeCommonInvocationForModuleBuild(CompilerInvocation CI) {
179 CI.resetNonModularOptions();
180 CI.clearImplicitModuleBuildOptions();
181
182 // The scanner takes care to avoid passing non-affecting module maps to the
183 // explicit compiles. No need to do extra work just to find out there are no
184 // module map files to prune.
185 CI.getHeaderSearchOpts().ModulesPruneNonAffectingModuleMaps = false;
186
187 // Remove options incompatible with explicit module build or are likely to
188 // differ between identical modules discovered from different translation
189 // units.
190 CI.getFrontendOpts().Inputs.clear();
191 CI.getFrontendOpts().OutputFile.clear();
192 // LLVM options are not going to affect the AST
193 CI.getFrontendOpts().LLVMArgs.clear();
194
195 resetBenignCodeGenOptions(ProgramAction: frontend::GenerateModule, LangOpts: CI.getLangOpts(),
196 CGOpts&: CI.getCodeGenOpts());
197
198 // Map output paths that affect behaviour to "-" so their existence is in the
199 // context hash. The final path will be computed in addOutputPaths.
200 if (!CI.getDiagnosticOpts().DiagnosticSerializationFile.empty())
201 CI.getDiagnosticOpts().DiagnosticSerializationFile = "-";
202 if (!CI.getDependencyOutputOpts().OutputFile.empty())
203 CI.getDependencyOutputOpts().OutputFile = "-";
204 CI.getDependencyOutputOpts().Targets.clear();
205
206 CI.getFrontendOpts().ProgramAction = frontend::GenerateModule;
207 CI.getFrontendOpts().ARCMTAction = FrontendOptions::ARCMT_None;
208 CI.getFrontendOpts().ObjCMTAction = FrontendOptions::ObjCMT_None;
209 CI.getFrontendOpts().MTMigrateDir.clear();
210 CI.getLangOpts().ModuleName.clear();
211
212 // Remove any macro definitions that are explicitly ignored.
213 if (!CI.getHeaderSearchOpts().ModulesIgnoreMacros.empty()) {
214 llvm::erase_if(
215 C&: CI.getPreprocessorOpts().Macros,
216 P: [&CI](const std::pair<std::string, bool> &Def) {
217 StringRef MacroDef = Def.first;
218 return CI.getHeaderSearchOpts().ModulesIgnoreMacros.contains(
219 key: llvm::CachedHashString(MacroDef.split(Separator: '=').first));
220 });
221 // Remove the now unused option.
222 CI.getHeaderSearchOpts().ModulesIgnoreMacros.clear();
223 }
224
225 return CI;
226}
227
228CowCompilerInvocation
229ModuleDepCollector::getInvocationAdjustedForModuleBuildWithoutOutputs(
230 const ModuleDeps &Deps,
231 llvm::function_ref<void(CowCompilerInvocation &)> Optimize) const {
232 CowCompilerInvocation CI = CommonInvocation;
233
234 CI.getMutLangOpts().ModuleName = Deps.ID.ModuleName;
235 CI.getMutFrontendOpts().IsSystemModule = Deps.IsSystem;
236
237 // Inputs
238 InputKind ModuleMapInputKind(CI.getFrontendOpts().DashX.getLanguage(),
239 InputKind::Format::ModuleMap);
240 CI.getMutFrontendOpts().Inputs.emplace_back(Args: Deps.ClangModuleMapFile,
241 Args&: ModuleMapInputKind);
242
243 auto CurrentModuleMapEntry =
244 ScanInstance.getFileManager().getFile(Filename: Deps.ClangModuleMapFile);
245 assert(CurrentModuleMapEntry && "module map file entry not found");
246
247 // Remove directly passed modulemap files. They will get added back if they
248 // were actually used.
249 CI.getMutFrontendOpts().ModuleMapFiles.clear();
250
251 auto DepModuleMapFiles = collectModuleMapFiles(ClangModuleDeps: Deps.ClangModuleDeps);
252 for (StringRef ModuleMapFile : Deps.ModuleMapFileDeps) {
253 // TODO: Track these as `FileEntryRef` to simplify the equality check below.
254 auto ModuleMapEntry = ScanInstance.getFileManager().getFile(Filename: ModuleMapFile);
255 assert(ModuleMapEntry && "module map file entry not found");
256
257 // Don't report module maps describing eagerly-loaded dependency. This
258 // information will be deserialized from the PCM.
259 // TODO: Verify this works fine when modulemap for module A is eagerly
260 // loaded from A.pcm, and module map passed on the command line contains
261 // definition of a submodule: "explicit module A.Private { ... }".
262 if (EagerLoadModules && DepModuleMapFiles.contains(V: *ModuleMapEntry))
263 continue;
264
265 // Don't report module map file of the current module unless it also
266 // describes a dependency (for symmetry).
267 if (*ModuleMapEntry == *CurrentModuleMapEntry &&
268 !DepModuleMapFiles.contains(V: *ModuleMapEntry))
269 continue;
270
271 CI.getMutFrontendOpts().ModuleMapFiles.emplace_back(args&: ModuleMapFile);
272 }
273
274 // Report the prebuilt modules this module uses.
275 for (const auto &PrebuiltModule : Deps.PrebuiltModuleDeps)
276 CI.getMutFrontendOpts().ModuleFiles.push_back(x: PrebuiltModule.PCMFile);
277
278 // Add module file inputs from dependencies.
279 addModuleFiles(CI, ClangModuleDeps: Deps.ClangModuleDeps);
280
281 if (!CI.getDiagnosticOpts().SystemHeaderWarningsModules.empty()) {
282 // Apply -Wsystem-headers-in-module for the current module.
283 if (llvm::is_contained(Range: CI.getDiagnosticOpts().SystemHeaderWarningsModules,
284 Element: Deps.ID.ModuleName))
285 CI.getMutDiagnosticOpts().Warnings.push_back(x: "system-headers");
286 // Remove the now unused option(s).
287 CI.getMutDiagnosticOpts().SystemHeaderWarningsModules.clear();
288 }
289
290 Optimize(CI);
291
292 return CI;
293}
294
295llvm::DenseSet<const FileEntry *> ModuleDepCollector::collectModuleMapFiles(
296 ArrayRef<ModuleID> ClangModuleDeps) const {
297 llvm::DenseSet<const FileEntry *> ModuleMapFiles;
298 for (const ModuleID &MID : ClangModuleDeps) {
299 ModuleDeps *MD = ModuleDepsByID.lookup(Val: MID);
300 assert(MD && "Inconsistent dependency info");
301 // TODO: Track ClangModuleMapFile as `FileEntryRef`.
302 auto FE = ScanInstance.getFileManager().getFile(Filename: MD->ClangModuleMapFile);
303 assert(FE && "Missing module map file that was previously found");
304 ModuleMapFiles.insert(V: *FE);
305 }
306 return ModuleMapFiles;
307}
308
309void ModuleDepCollector::addModuleMapFiles(
310 CompilerInvocation &CI, ArrayRef<ModuleID> ClangModuleDeps) const {
311 if (EagerLoadModules)
312 return; // Only pcm is needed for eager load.
313
314 for (const ModuleID &MID : ClangModuleDeps) {
315 ModuleDeps *MD = ModuleDepsByID.lookup(Val: MID);
316 assert(MD && "Inconsistent dependency info");
317 CI.getFrontendOpts().ModuleMapFiles.push_back(x: MD->ClangModuleMapFile);
318 }
319}
320
321void ModuleDepCollector::addModuleFiles(
322 CompilerInvocation &CI, ArrayRef<ModuleID> ClangModuleDeps) const {
323 for (const ModuleID &MID : ClangModuleDeps) {
324 std::string PCMPath =
325 Controller.lookupModuleOutput(ID: MID, Kind: ModuleOutputKind::ModuleFile);
326 if (EagerLoadModules)
327 CI.getFrontendOpts().ModuleFiles.push_back(x: std::move(PCMPath));
328 else
329 CI.getHeaderSearchOpts().PrebuiltModuleFiles.insert(
330 x: {MID.ModuleName, std::move(PCMPath)});
331 }
332}
333
334void ModuleDepCollector::addModuleFiles(
335 CowCompilerInvocation &CI, ArrayRef<ModuleID> ClangModuleDeps) const {
336 for (const ModuleID &MID : ClangModuleDeps) {
337 std::string PCMPath =
338 Controller.lookupModuleOutput(ID: MID, Kind: ModuleOutputKind::ModuleFile);
339 if (EagerLoadModules)
340 CI.getMutFrontendOpts().ModuleFiles.push_back(x: std::move(PCMPath));
341 else
342 CI.getMutHeaderSearchOpts().PrebuiltModuleFiles.insert(
343 x: {MID.ModuleName, std::move(PCMPath)});
344 }
345}
346
347static bool needsModules(FrontendInputFile FIF) {
348 switch (FIF.getKind().getLanguage()) {
349 case Language::Unknown:
350 case Language::Asm:
351 case Language::LLVM_IR:
352 return false;
353 default:
354 return true;
355 }
356}
357
358void ModuleDepCollector::applyDiscoveredDependencies(CompilerInvocation &CI) {
359 CI.clearImplicitModuleBuildOptions();
360 resetBenignCodeGenOptions(ProgramAction: CI.getFrontendOpts().ProgramAction,
361 LangOpts: CI.getLangOpts(), CGOpts&: CI.getCodeGenOpts());
362
363 if (llvm::any_of(Range&: CI.getFrontendOpts().Inputs, P: needsModules)) {
364 Preprocessor &PP = ScanInstance.getPreprocessor();
365 if (Module *CurrentModule = PP.getCurrentModuleImplementation())
366 if (OptionalFileEntryRef CurrentModuleMap =
367 PP.getHeaderSearchInfo()
368 .getModuleMap()
369 .getModuleMapFileForUniquing(M: CurrentModule))
370 CI.getFrontendOpts().ModuleMapFiles.emplace_back(
371 args: CurrentModuleMap->getNameAsRequested());
372
373 SmallVector<ModuleID> DirectDeps;
374 for (const auto &KV : ModularDeps)
375 if (DirectModularDeps.contains(key: KV.first))
376 DirectDeps.push_back(Elt: KV.second->ID);
377
378 // TODO: Report module maps the same way it's done for modular dependencies.
379 addModuleMapFiles(CI, ClangModuleDeps: DirectDeps);
380
381 addModuleFiles(CI, ClangModuleDeps: DirectDeps);
382
383 for (const auto &KV : DirectPrebuiltModularDeps)
384 CI.getFrontendOpts().ModuleFiles.push_back(x: KV.second.PCMFile);
385 }
386}
387
388static std::string getModuleContextHash(const ModuleDeps &MD,
389 const CowCompilerInvocation &CI,
390 bool EagerLoadModules,
391 llvm::vfs::FileSystem &VFS) {
392 llvm::HashBuilder<llvm::TruncatedBLAKE3<16>, llvm::endianness::native>
393 HashBuilder;
394 SmallString<32> Scratch;
395
396 // Hash the compiler version and serialization version to ensure the module
397 // will be readable.
398 HashBuilder.add(Value: getClangFullRepositoryVersion());
399 HashBuilder.add(Args: serialization::VERSION_MAJOR, Args: serialization::VERSION_MINOR);
400 llvm::ErrorOr<std::string> CWD = VFS.getCurrentWorkingDirectory();
401 if (CWD)
402 HashBuilder.add(Value: *CWD);
403
404 // Hash the BuildInvocation without any input files.
405 SmallString<0> ArgVec;
406 ArgVec.reserve(N: 4096);
407 CI.generateCC1CommandLine(Consumer: [&](const Twine &Arg) {
408 Arg.toVector(Out&: ArgVec);
409 ArgVec.push_back(Elt: '\0');
410 });
411 HashBuilder.add(Value: ArgVec);
412
413 // Hash the module dependencies. These paths may differ even if the invocation
414 // is identical if they depend on the contents of the files in the TU -- for
415 // example, case-insensitive paths to modulemap files. Usually such a case
416 // would indicate a missed optimization to canonicalize, but it may be
417 // difficult to canonicalize all cases when there is a VFS.
418 for (const auto &ID : MD.ClangModuleDeps) {
419 HashBuilder.add(Value: ID.ModuleName);
420 HashBuilder.add(Value: ID.ContextHash);
421 }
422
423 HashBuilder.add(Value: EagerLoadModules);
424
425 llvm::BLAKE3Result<16> Hash = HashBuilder.final();
426 std::array<uint64_t, 2> Words;
427 static_assert(sizeof(Hash) == sizeof(Words), "Hash must match Words");
428 std::memcpy(dest: Words.data(), src: Hash.data(), n: sizeof(Hash));
429 return toString(I: llvm::APInt(sizeof(Words) * 8, Words), Radix: 36, /*Signed=*/false);
430}
431
432void ModuleDepCollector::associateWithContextHash(
433 const CowCompilerInvocation &CI, ModuleDeps &Deps) {
434 Deps.ID.ContextHash = getModuleContextHash(
435 MD: Deps, CI, EagerLoadModules, VFS&: ScanInstance.getVirtualFileSystem());
436 bool Inserted = ModuleDepsByID.insert(KV: {Deps.ID, &Deps}).second;
437 (void)Inserted;
438 assert(Inserted && "duplicate module mapping");
439}
440
441void ModuleDepCollectorPP::LexedFileChanged(FileID FID,
442 LexedFileChangeReason Reason,
443 SrcMgr::CharacteristicKind FileType,
444 FileID PrevFID,
445 SourceLocation Loc) {
446 if (Reason != LexedFileChangeReason::EnterFile)
447 return;
448
449 // This has to be delayed as the context hash can change at the start of
450 // `CompilerInstance::ExecuteAction`.
451 if (MDC.ContextHash.empty()) {
452 MDC.ContextHash = MDC.ScanInstance.getInvocation().getModuleHash();
453 MDC.Consumer.handleContextHash(Hash: MDC.ContextHash);
454 }
455
456 SourceManager &SM = MDC.ScanInstance.getSourceManager();
457
458 // Dependency generation really does want to go all the way to the
459 // file entry for a source location to find out what is depended on.
460 // We do not want #line markers to affect dependency generation!
461 if (std::optional<StringRef> Filename = SM.getNonBuiltinFilenameForID(FID))
462 MDC.addFileDep(Path: llvm::sys::path::remove_leading_dotslash(path: *Filename));
463}
464
465void ModuleDepCollectorPP::InclusionDirective(
466 SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName,
467 bool IsAngled, CharSourceRange FilenameRange, OptionalFileEntryRef File,
468 StringRef SearchPath, StringRef RelativePath, const Module *SuggestedModule,
469 bool ModuleImported, SrcMgr::CharacteristicKind FileType) {
470 if (!File && !ModuleImported) {
471 // This is a non-modular include that HeaderSearch failed to find. Add it
472 // here as `FileChanged` will never see it.
473 MDC.addFileDep(Path: FileName);
474 }
475 handleImport(Imported: SuggestedModule);
476}
477
478void ModuleDepCollectorPP::moduleImport(SourceLocation ImportLoc,
479 ModuleIdPath Path,
480 const Module *Imported) {
481 if (MDC.ScanInstance.getPreprocessor().isInImportingCXXNamedModules()) {
482 P1689ModuleInfo RequiredModule;
483 RequiredModule.ModuleName = Path[0].first->getName().str();
484 RequiredModule.Type = P1689ModuleInfo::ModuleType::NamedCXXModule;
485 MDC.RequiredStdCXXModules.push_back(x: RequiredModule);
486 return;
487 }
488
489 handleImport(Imported);
490}
491
492void ModuleDepCollectorPP::handleImport(const Module *Imported) {
493 if (!Imported)
494 return;
495
496 const Module *TopLevelModule = Imported->getTopLevelModule();
497
498 if (MDC.isPrebuiltModule(M: TopLevelModule))
499 MDC.DirectPrebuiltModularDeps.insert(
500 KV: {TopLevelModule, PrebuiltModuleDep{TopLevelModule}});
501 else
502 MDC.DirectModularDeps.insert(X: TopLevelModule);
503}
504
505void ModuleDepCollectorPP::EndOfMainFile() {
506 FileID MainFileID = MDC.ScanInstance.getSourceManager().getMainFileID();
507 MDC.MainFile = std::string(MDC.ScanInstance.getSourceManager()
508 .getFileEntryRefForID(FID: MainFileID)
509 ->getName());
510
511 auto &PP = MDC.ScanInstance.getPreprocessor();
512 if (PP.isInNamedModule()) {
513 P1689ModuleInfo ProvidedModule;
514 ProvidedModule.ModuleName = PP.getNamedModuleName();
515 ProvidedModule.Type = P1689ModuleInfo::ModuleType::NamedCXXModule;
516 ProvidedModule.IsStdCXXModuleInterface = PP.isInNamedInterfaceUnit();
517 // Don't put implementation (non partition) unit as Provide.
518 // Put the module as required instead. Since the implementation
519 // unit will import the primary module implicitly.
520 if (PP.isInImplementationUnit())
521 MDC.RequiredStdCXXModules.push_back(x: ProvidedModule);
522 else
523 MDC.ProvidedStdCXXModule = ProvidedModule;
524 }
525
526 if (!MDC.ScanInstance.getPreprocessorOpts().ImplicitPCHInclude.empty())
527 MDC.addFileDep(Path: MDC.ScanInstance.getPreprocessorOpts().ImplicitPCHInclude);
528
529 for (const Module *M :
530 MDC.ScanInstance.getPreprocessor().getAffectingClangModules())
531 if (!MDC.isPrebuiltModule(M))
532 MDC.DirectModularDeps.insert(X: M);
533
534 for (const Module *M : MDC.DirectModularDeps)
535 handleTopLevelModule(M);
536
537 MDC.Consumer.handleDependencyOutputOpts(Opts: *MDC.Opts);
538
539 if (MDC.IsStdModuleP1689Format)
540 MDC.Consumer.handleProvidedAndRequiredStdCXXModules(
541 Provided: MDC.ProvidedStdCXXModule, Requires: MDC.RequiredStdCXXModules);
542
543 for (auto &&I : MDC.ModularDeps)
544 MDC.Consumer.handleModuleDependency(MD: *I.second);
545
546 for (const Module *M : MDC.DirectModularDeps) {
547 auto It = MDC.ModularDeps.find(Key: M);
548 // Only report direct dependencies that were successfully handled.
549 if (It != MDC.ModularDeps.end())
550 MDC.Consumer.handleDirectModuleDependency(MD: MDC.ModularDeps[M]->ID);
551 }
552
553 for (auto &&I : MDC.FileDeps)
554 MDC.Consumer.handleFileDependency(Filename: I);
555
556 for (auto &&I : MDC.DirectPrebuiltModularDeps)
557 MDC.Consumer.handlePrebuiltModuleDependency(PMD: I.second);
558}
559
560std::optional<ModuleID>
561ModuleDepCollectorPP::handleTopLevelModule(const Module *M) {
562 assert(M == M->getTopLevelModule() && "Expected top level module!");
563
564 // A top-level module might not be actually imported as a module when
565 // -fmodule-name is used to compile a translation unit that imports this
566 // module. In that case it can be skipped. The appropriate header
567 // dependencies will still be reported as expected.
568 if (!M->getASTFile())
569 return {};
570
571 // If this module has been handled already, just return its ID.
572 auto ModI = MDC.ModularDeps.insert(KV: {M, nullptr});
573 if (!ModI.second)
574 return ModI.first->second->ID;
575
576 ModI.first->second = std::make_unique<ModuleDeps>();
577 ModuleDeps &MD = *ModI.first->second;
578
579 MD.ID.ModuleName = M->getFullModuleName();
580 MD.IsSystem = M->IsSystem;
581
582 ModuleMap &ModMapInfo =
583 MDC.ScanInstance.getPreprocessor().getHeaderSearchInfo().getModuleMap();
584
585 OptionalFileEntryRef ModuleMap = ModMapInfo.getModuleMapFileForUniquing(M);
586
587 if (ModuleMap) {
588 SmallString<128> Path = ModuleMap->getNameAsRequested();
589 ModMapInfo.canonicalizeModuleMapPath(Path);
590 MD.ClangModuleMapFile = std::string(Path);
591 }
592
593 serialization::ModuleFile *MF =
594 MDC.ScanInstance.getASTReader()->getModuleManager().lookup(
595 File: *M->getASTFile());
596 MDC.ScanInstance.getASTReader()->visitInputFileInfos(
597 MF&: *MF, /*IncludeSystem=*/true,
598 Visitor: [&](const serialization::InputFileInfo &IFI, bool IsSystem) {
599 // __inferred_module.map is the result of the way in which an implicit
600 // module build handles inferred modules. It adds an overlay VFS with
601 // this file in the proper directory and relies on the rest of Clang to
602 // handle it like normal. With explicitly built modules we don't need
603 // to play VFS tricks, so replace it with the correct module map.
604 if (StringRef(IFI.Filename).ends_with(Suffix: "__inferred_module.map")) {
605 MDC.addFileDep(MD, Path: ModuleMap->getName());
606 return;
607 }
608 MDC.addFileDep(MD, Path: IFI.Filename);
609 });
610
611 llvm::DenseSet<const Module *> SeenDeps;
612 addAllSubmodulePrebuiltDeps(M, MD, SeenSubmodules&: SeenDeps);
613 addAllSubmoduleDeps(M, MD, AddedModules&: SeenDeps);
614 addAllAffectingClangModules(M, MD, AddedModules&: SeenDeps);
615
616 MDC.ScanInstance.getASTReader()->visitInputFileInfos(
617 MF&: *MF, /*IncludeSystem=*/true,
618 Visitor: [&](const serialization::InputFileInfo &IFI, bool IsSystem) {
619 if (!(IFI.TopLevel && IFI.ModuleMap))
620 return;
621 if (StringRef(IFI.FilenameAsRequested)
622 .ends_with(Suffix: "__inferred_module.map"))
623 return;
624 MD.ModuleMapFileDeps.emplace_back(args: IFI.FilenameAsRequested);
625 });
626
627 CowCompilerInvocation CI =
628 MDC.getInvocationAdjustedForModuleBuildWithoutOutputs(
629 Deps: MD, Optimize: [&](CowCompilerInvocation &BuildInvocation) {
630 if (any(Val: MDC.OptimizeArgs & (ScanningOptimizations::HeaderSearch |
631 ScanningOptimizations::VFS)))
632 optimizeHeaderSearchOpts(Opts&: BuildInvocation.getMutHeaderSearchOpts(),
633 Reader&: *MDC.ScanInstance.getASTReader(), MF: *MF,
634 PrebuiltModuleVFSMap: MDC.PrebuiltModuleVFSMap,
635 OptimizeArgs: MDC.OptimizeArgs);
636 if (any(Val: MDC.OptimizeArgs & ScanningOptimizations::SystemWarnings))
637 optimizeDiagnosticOpts(
638 Opts&: BuildInvocation.getMutDiagnosticOpts(),
639 IsSystemModule: BuildInvocation.getFrontendOpts().IsSystemModule);
640 });
641
642 MDC.associateWithContextHash(CI, Deps&: MD);
643
644 // Finish the compiler invocation. Requires dependencies and the context hash.
645 MDC.addOutputPaths(CI, Deps&: MD);
646
647 MD.BuildInfo = std::move(CI);
648
649 return MD.ID;
650}
651
652static void forEachSubmoduleSorted(const Module *M,
653 llvm::function_ref<void(const Module *)> F) {
654 // Submodule order depends on order of header includes for inferred submodules
655 // we don't care about the exact order, so sort so that it's consistent across
656 // TUs to improve sharing.
657 SmallVector<const Module *> Submodules(M->submodules());
658 llvm::stable_sort(Range&: Submodules, C: [](const Module *A, const Module *B) {
659 return A->Name < B->Name;
660 });
661 for (const Module *SubM : Submodules)
662 F(SubM);
663}
664
665void ModuleDepCollectorPP::addAllSubmodulePrebuiltDeps(
666 const Module *M, ModuleDeps &MD,
667 llvm::DenseSet<const Module *> &SeenSubmodules) {
668 addModulePrebuiltDeps(M, MD, SeenSubmodules);
669
670 forEachSubmoduleSorted(M, F: [&](const Module *SubM) {
671 addAllSubmodulePrebuiltDeps(M: SubM, MD, SeenSubmodules);
672 });
673}
674
675void ModuleDepCollectorPP::addModulePrebuiltDeps(
676 const Module *M, ModuleDeps &MD,
677 llvm::DenseSet<const Module *> &SeenSubmodules) {
678 for (const Module *Import : M->Imports)
679 if (Import->getTopLevelModule() != M->getTopLevelModule())
680 if (MDC.isPrebuiltModule(M: Import->getTopLevelModule()))
681 if (SeenSubmodules.insert(V: Import->getTopLevelModule()).second)
682 MD.PrebuiltModuleDeps.emplace_back(args: Import->getTopLevelModule());
683}
684
685void ModuleDepCollectorPP::addAllSubmoduleDeps(
686 const Module *M, ModuleDeps &MD,
687 llvm::DenseSet<const Module *> &AddedModules) {
688 addModuleDep(M, MD, AddedModules);
689
690 forEachSubmoduleSorted(M, F: [&](const Module *SubM) {
691 addAllSubmoduleDeps(M: SubM, MD, AddedModules);
692 });
693}
694
695void ModuleDepCollectorPP::addModuleDep(
696 const Module *M, ModuleDeps &MD,
697 llvm::DenseSet<const Module *> &AddedModules) {
698 for (const Module *Import : M->Imports) {
699 if (Import->getTopLevelModule() != M->getTopLevelModule() &&
700 !MDC.isPrebuiltModule(M: Import)) {
701 if (auto ImportID = handleTopLevelModule(M: Import->getTopLevelModule()))
702 if (AddedModules.insert(V: Import->getTopLevelModule()).second)
703 MD.ClangModuleDeps.push_back(x: *ImportID);
704 }
705 }
706}
707
708void ModuleDepCollectorPP::addAllAffectingClangModules(
709 const Module *M, ModuleDeps &MD,
710 llvm::DenseSet<const Module *> &AddedModules) {
711 addAffectingClangModule(M, MD, AddedModules);
712
713 for (const Module *SubM : M->submodules())
714 addAllAffectingClangModules(M: SubM, MD, AddedModules);
715}
716
717void ModuleDepCollectorPP::addAffectingClangModule(
718 const Module *M, ModuleDeps &MD,
719 llvm::DenseSet<const Module *> &AddedModules) {
720 for (const Module *Affecting : M->AffectingClangModules) {
721 assert(Affecting == Affecting->getTopLevelModule() &&
722 "Not quite import not top-level module");
723 if (Affecting != M->getTopLevelModule() &&
724 !MDC.isPrebuiltModule(M: Affecting)) {
725 if (auto ImportID = handleTopLevelModule(M: Affecting))
726 if (AddedModules.insert(V: Affecting).second)
727 MD.ClangModuleDeps.push_back(x: *ImportID);
728 }
729 }
730}
731
732ModuleDepCollector::ModuleDepCollector(
733 std::unique_ptr<DependencyOutputOptions> Opts,
734 CompilerInstance &ScanInstance, DependencyConsumer &C,
735 DependencyActionController &Controller, CompilerInvocation OriginalCI,
736 PrebuiltModuleVFSMapT PrebuiltModuleVFSMap,
737 ScanningOptimizations OptimizeArgs, bool EagerLoadModules,
738 bool IsStdModuleP1689Format)
739 : ScanInstance(ScanInstance), Consumer(C), Controller(Controller),
740 PrebuiltModuleVFSMap(std::move(PrebuiltModuleVFSMap)),
741 Opts(std::move(Opts)),
742 CommonInvocation(
743 makeCommonInvocationForModuleBuild(CI: std::move(OriginalCI))),
744 OptimizeArgs(OptimizeArgs), EagerLoadModules(EagerLoadModules),
745 IsStdModuleP1689Format(IsStdModuleP1689Format) {}
746
747void ModuleDepCollector::attachToPreprocessor(Preprocessor &PP) {
748 PP.addPPCallbacks(C: std::make_unique<ModuleDepCollectorPP>(args&: *this));
749}
750
751void ModuleDepCollector::attachToASTReader(ASTReader &R) {}
752
753bool ModuleDepCollector::isPrebuiltModule(const Module *M) {
754 std::string Name(M->getTopLevelModuleName());
755 const auto &PrebuiltModuleFiles =
756 ScanInstance.getHeaderSearchOpts().PrebuiltModuleFiles;
757 auto PrebuiltModuleFileIt = PrebuiltModuleFiles.find(x: Name);
758 if (PrebuiltModuleFileIt == PrebuiltModuleFiles.end())
759 return false;
760 assert("Prebuilt module came from the expected AST file" &&
761 PrebuiltModuleFileIt->second == M->getASTFile()->getName());
762 return true;
763}
764
765static StringRef makeAbsoluteAndPreferred(CompilerInstance &CI, StringRef Path,
766 SmallVectorImpl<char> &Storage) {
767 if (llvm::sys::path::is_absolute(path: Path) &&
768 !llvm::sys::path::is_style_windows(S: llvm::sys::path::Style::native))
769 return Path;
770 Storage.assign(in_start: Path.begin(), in_end: Path.end());
771 CI.getFileManager().makeAbsolutePath(Path&: Storage);
772 llvm::sys::path::make_preferred(path&: Storage);
773 return StringRef(Storage.data(), Storage.size());
774}
775
776void ModuleDepCollector::addFileDep(StringRef Path) {
777 if (IsStdModuleP1689Format) {
778 // Within P1689 format, we don't want all the paths to be absolute path
779 // since it may violate the tranditional make style dependencies info.
780 FileDeps.push_back(x: std::string(Path));
781 return;
782 }
783
784 llvm::SmallString<256> Storage;
785 Path = makeAbsoluteAndPreferred(CI&: ScanInstance, Path, Storage);
786 FileDeps.push_back(x: std::string(Path));
787}
788
789void ModuleDepCollector::addFileDep(ModuleDeps &MD, StringRef Path) {
790 if (IsStdModuleP1689Format) {
791 MD.FileDeps.insert(key: Path);
792 return;
793 }
794
795 llvm::SmallString<256> Storage;
796 Path = makeAbsoluteAndPreferred(CI&: ScanInstance, Path, Storage);
797 MD.FileDeps.insert(key: Path);
798}
799

source code of clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp