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/DependencyScanning/ModuleDepCollector.h"
10
11#include "clang/Basic/MakeSupport.h"
12#include "clang/DependencyScanning/DependencyActionController.h"
13#include "clang/DependencyScanning/DependencyConsumer.h"
14#include "clang/DependencyScanning/DependencyScanningWorker.h"
15#include "clang/Frontend/CompilerInstance.h"
16#include "clang/Lex/Preprocessor.h"
17#include "llvm/ADT/STLExtras.h"
18#include "llvm/Support/BLAKE3.h"
19#include <optional>
20
21using namespace clang;
22using namespace dependencies;
23
24static PrebuiltModuleDep
25createPrebuiltModuleDep(const serialization::ModuleFile *MF) {
26 PrebuiltModuleDep Dep;
27 Dep.ModuleName = MF->ModuleName;
28 Dep.PCMFile = MF->FileName.str();
29 Dep.ModuleMapFile = MF->ModuleMapPath;
30 return Dep;
31}
32
33void PrebuiltModuleASTAttrs::updateDependentsNotInStableDirs(
34 PrebuiltModulesAttrsMap &PrebuiltModulesMap) {
35 setInStableDir();
36 for (const auto Dep : ModuleFileDependents) {
37 if (!PrebuiltModulesMap[Dep].isInStableDir())
38 return;
39 PrebuiltModulesMap[Dep].updateDependentsNotInStableDirs(PrebuiltModulesMap);
40 }
41}
42
43static void
44optimizeHeaderSearchOpts(HeaderSearchOptions &Opts, ASTReader &Reader,
45 const serialization::ModuleFile &MF,
46 const PrebuiltModulesAttrsMap &PrebuiltModulesASTMap,
47 ScanningOptimizations OptimizeArgs) {
48 if (any(Val: OptimizeArgs & ScanningOptimizations::HeaderSearch)) {
49 // Only preserve search paths that were used during the dependency scan.
50 std::vector<HeaderSearchOptions::Entry> Entries;
51 std::swap(x&: Opts.UserEntries, y&: Entries);
52
53 llvm::BitVector SearchPathUsage(Entries.size());
54 llvm::DenseSet<const serialization::ModuleFile *> Visited;
55 std::function<void(const serialization::ModuleFile *)> VisitMF =
56 [&](const serialization::ModuleFile *MF) {
57 SearchPathUsage |= MF->SearchPathUsage;
58 Visited.insert(V: MF);
59 for (const serialization::ModuleFile *Import : MF->Imports)
60 if (!Visited.contains(V: Import))
61 VisitMF(Import);
62 };
63 VisitMF(&MF);
64
65 if (SearchPathUsage.size() != Entries.size())
66 llvm::report_fatal_error(
67 reason: "Inconsistent search path options between modules detected");
68
69 for (auto Idx : SearchPathUsage.set_bits())
70 Opts.UserEntries.push_back(x: std::move(Entries[Idx]));
71 }
72 if (any(Val: OptimizeArgs & ScanningOptimizations::VFS)) {
73 std::vector<std::string> VFSOverlayFiles;
74 std::swap(x&: Opts.VFSOverlayFiles, y&: VFSOverlayFiles);
75
76 llvm::BitVector VFSUsage(VFSOverlayFiles.size());
77 llvm::DenseSet<const serialization::ModuleFile *> Visited;
78 std::function<void(const serialization::ModuleFile *)> VisitMF =
79 [&](const serialization::ModuleFile *MF) {
80 Visited.insert(V: MF);
81 if (MF->Kind == serialization::MK_ImplicitModule) {
82 VFSUsage |= MF->VFSUsage;
83 // We only need to recurse into implicit modules. Other module types
84 // will have the correct set of VFSs for anything they depend on.
85 for (const serialization::ModuleFile *Import : MF->Imports)
86 if (!Visited.contains(V: Import))
87 VisitMF(Import);
88 } else {
89 // This is not an implicitly built module, so it may have different
90 // VFS options. Fall back to a string comparison instead.
91 auto PrebuiltModulePropIt =
92 PrebuiltModulesASTMap.find(Key: MF->FileName);
93 if (PrebuiltModulePropIt == PrebuiltModulesASTMap.end())
94 return;
95 for (std::size_t I = 0, E = VFSOverlayFiles.size(); I != E; ++I) {
96 if (PrebuiltModulePropIt->second.getVFS().contains(
97 key: VFSOverlayFiles[I]))
98 VFSUsage[I] = true;
99 }
100 }
101 };
102 VisitMF(&MF);
103
104 if (VFSUsage.size() != VFSOverlayFiles.size())
105 llvm::report_fatal_error(
106 reason: "Inconsistent -ivfsoverlay options between modules detected");
107
108 for (auto Idx : VFSUsage.set_bits())
109 Opts.VFSOverlayFiles.push_back(x: std::move(VFSOverlayFiles[Idx]));
110 }
111}
112
113static void optimizeDiagnosticOpts(DiagnosticOptions &Opts,
114 bool IsSystemModule) {
115 // If this is not a system module or -Wsystem-headers was passed, don't
116 // optimize.
117 if (!IsSystemModule)
118 return;
119 bool Wsystem_headers = false;
120 for (StringRef Opt : Opts.Warnings) {
121 bool isPositive = !Opt.consume_front(Prefix: "no-");
122 if (Opt == "system-headers")
123 Wsystem_headers = isPositive;
124 }
125 if (Wsystem_headers)
126 return;
127
128 // Remove all warning flags. System modules suppress most, but not all,
129 // warnings.
130 Opts.Warnings.clear();
131 Opts.UndefPrefixes.clear();
132 Opts.Remarks.clear();
133}
134
135static void optimizeCWD(CowCompilerInvocation &BuildInvocation, StringRef CWD) {
136 BuildInvocation.getMutFileSystemOpts().WorkingDir.clear();
137 BuildInvocation.getMutCodeGenOpts().DebugCompilationDir.clear();
138 BuildInvocation.getMutCodeGenOpts().CoverageCompilationDir.clear();
139}
140
141static std::vector<std::string> splitString(std::string S, char Separator) {
142 SmallVector<StringRef> Segments;
143 StringRef(S).split(A&: Segments, Separator, /*MaxSplit=*/-1, /*KeepEmpty=*/false);
144 std::vector<std::string> Result;
145 Result.reserve(n: Segments.size());
146 for (StringRef Segment : Segments)
147 Result.push_back(x: Segment.str());
148 return Result;
149}
150
151void ModuleDepCollector::addOutputPaths(CowCompilerInvocation &CI,
152 ModuleDeps &Deps) {
153 CI.getMutFrontendOpts().OutputFile =
154 Controller.lookupModuleOutput(MD: Deps, Kind: ModuleOutputKind::ModuleFile);
155 if (!CI.getDiagnosticOpts().DiagnosticSerializationFile.empty())
156 CI.getMutDiagnosticOpts().DiagnosticSerializationFile =
157 Controller.lookupModuleOutput(
158 MD: Deps, Kind: ModuleOutputKind::DiagnosticSerializationFile);
159 if (!CI.getDependencyOutputOpts().OutputFile.empty()) {
160 CI.getMutDependencyOutputOpts().OutputFile =
161 Controller.lookupModuleOutput(MD: Deps, Kind: ModuleOutputKind::DependencyFile);
162 CI.getMutDependencyOutputOpts().Targets =
163 splitString(S: Controller.lookupModuleOutput(
164 MD: Deps, Kind: ModuleOutputKind::DependencyTargets),
165 Separator: '\0');
166 if (!CI.getDependencyOutputOpts().OutputFile.empty() &&
167 CI.getDependencyOutputOpts().Targets.empty()) {
168 // Fallback to -o as dependency target, as in the driver.
169 SmallString<128> Target;
170 quoteMakeTarget(Target: CI.getFrontendOpts().OutputFile, Res&: Target);
171 CI.getMutDependencyOutputOpts().Targets.push_back(x: std::string(Target));
172 }
173 }
174}
175
176void dependencies::resetBenignCodeGenOptions(frontend::ActionKind ProgramAction,
177 const LangOptions &LangOpts,
178 CodeGenOptions &CGOpts) {
179 // TODO: Figure out better way to set options to their default value.
180 if (ProgramAction == frontend::GenerateModule) {
181 CGOpts.MainFileName.clear();
182 CGOpts.DwarfDebugFlags.clear();
183 }
184 if (ProgramAction == frontend::GeneratePCH ||
185 (ProgramAction == frontend::GenerateModule && !LangOpts.ModulesCodegen)) {
186 CGOpts.DebugCompilationDir.clear();
187 CGOpts.CoverageCompilationDir.clear();
188 CGOpts.CoverageDataFile.clear();
189 CGOpts.CoverageNotesFile.clear();
190 CGOpts.ProfileInstrumentUsePath.clear();
191 CGOpts.SampleProfileFile.clear();
192 CGOpts.ProfileRemappingFile.clear();
193 }
194}
195
196bool dependencies::isPathInStableDir(const ArrayRef<StringRef> Directories,
197 const StringRef Input) {
198 using namespace llvm::sys;
199
200 if (!path::is_absolute(path: Input))
201 return false;
202
203 auto PathStartsWith = [](StringRef Prefix, StringRef Path) {
204 auto PrefixIt = path::begin(path: Prefix), PrefixEnd = path::end(path: Prefix);
205 for (auto PathIt = path::begin(path: Path), PathEnd = path::end(path: Path);
206 PrefixIt != PrefixEnd && PathIt != PathEnd; ++PrefixIt, ++PathIt) {
207 if (*PrefixIt != *PathIt)
208 return false;
209 }
210 return PrefixIt == PrefixEnd;
211 };
212
213 return any_of(Range: Directories, P: [&](StringRef Dir) {
214 return !Dir.empty() && PathStartsWith(Dir, Input);
215 });
216}
217
218bool dependencies::areOptionsInStableDir(const ArrayRef<StringRef> Directories,
219 const HeaderSearchOptions &HSOpts) {
220 assert(isPathInStableDir(Directories, HSOpts.Sysroot) &&
221 "Sysroots differ between module dependencies and current TU");
222
223 assert(isPathInStableDir(Directories, HSOpts.ResourceDir) &&
224 "ResourceDirs differ between module dependencies and current TU");
225
226 for (const auto &Entry : HSOpts.UserEntries) {
227 if (!Entry.IgnoreSysRoot)
228 continue;
229 if (!isPathInStableDir(Directories, Input: Entry.Path))
230 return false;
231 }
232
233 for (const auto &SysPrefix : HSOpts.SystemHeaderPrefixes) {
234 if (!isPathInStableDir(Directories, Input: SysPrefix.Prefix))
235 return false;
236 }
237
238 return true;
239}
240
241static CowCompilerInvocation
242makeCommonInvocationForModuleBuild(CompilerInvocation CI) {
243 CI.resetNonModularOptions();
244 CI.clearImplicitModuleBuildOptions();
245
246 // The scanner takes care to avoid passing non-affecting module maps to the
247 // explicit compiles. No need to do extra work just to find out there are no
248 // module map files to prune.
249 CI.getHeaderSearchOpts().ModulesPruneNonAffectingModuleMaps = false;
250
251 // Remove options incompatible with explicit module build or are likely to
252 // differ between identical modules discovered from different translation
253 // units.
254 CI.getFrontendOpts().Inputs.clear();
255 CI.getFrontendOpts().OutputFile.clear();
256 CI.getFrontendOpts().GenReducedBMI = false;
257 CI.getFrontendOpts().ModuleOutputPath.clear();
258 CI.getHeaderSearchOpts().ModulesSkipHeaderSearchPaths = false;
259 CI.getHeaderSearchOpts().ModulesSkipDiagnosticOptions = false;
260 // LLVM options are not going to affect the AST
261 CI.getFrontendOpts().LLVMArgs.clear();
262
263 resetBenignCodeGenOptions(ProgramAction: frontend::GenerateModule, LangOpts: CI.getLangOpts(),
264 CGOpts&: CI.getCodeGenOpts());
265
266 // Erase the command-line arguments. These don't make it into the final set of
267 // compilation arguments and will be re-populated during compilation itself.
268 // Keeping them around would make copies of the invocation expensive.
269 CI.getCodeGenOpts().Argv0 = nullptr;
270 CI.getCodeGenOpts().CommandLineArgs.clear();
271
272 // Map output paths that affect behaviour to "-" so their existence is in the
273 // context hash. The final path will be computed in addOutputPaths.
274 if (!CI.getDiagnosticOpts().DiagnosticSerializationFile.empty())
275 CI.getDiagnosticOpts().DiagnosticSerializationFile = "-";
276 if (!CI.getDependencyOutputOpts().OutputFile.empty())
277 CI.getDependencyOutputOpts().OutputFile = "-";
278 CI.getDependencyOutputOpts().Targets.clear();
279 CI.getDependencyOutputOpts().IncludeModuleFiles = MFDK_Direct;
280
281 CI.getFrontendOpts().ProgramAction = frontend::GenerateModule;
282 CI.getLangOpts().ModuleName.clear();
283
284 // Remove any macro definitions that are explicitly ignored.
285 if (!CI.getHeaderSearchOpts().ModulesIgnoreMacros.empty()) {
286 llvm::erase_if(
287 C&: CI.getPreprocessorOpts().Macros,
288 P: [&CI](const std::pair<std::string, bool> &Def) {
289 StringRef MacroDef = Def.first;
290 return CI.getHeaderSearchOpts().ModulesIgnoreMacros.contains(
291 key: llvm::CachedHashString(MacroDef.split(Separator: '=').first));
292 });
293 // Remove the now unused option.
294 CI.getHeaderSearchOpts().ModulesIgnoreMacros.clear();
295 }
296
297 return CI;
298}
299
300CowCompilerInvocation
301ModuleDepCollector::getInvocationAdjustedForModuleBuildWithoutOutputs(
302 const ModuleDeps &Deps,
303 llvm::function_ref<void(CowCompilerInvocation &)> Optimize) const {
304 CowCompilerInvocation CI = CommonInvocation;
305
306 CI.getMutLangOpts().ModuleName = Deps.ID.ModuleName;
307 CI.getMutFrontendOpts().IsSystemModule = Deps.IsSystem;
308
309 // Inputs
310 InputKind ModuleMapInputKind(CI.getFrontendOpts().DashX.getLanguage(),
311 InputKind::Format::ModuleMap);
312 CI.getMutFrontendOpts().Inputs.emplace_back(Args: Deps.ClangModuleMapFile,
313 Args&: ModuleMapInputKind);
314
315 auto CurrentModuleMapEntry =
316 ScanInstance.getFileManager().getOptionalFileRef(Filename: Deps.ClangModuleMapFile);
317 assert(CurrentModuleMapEntry && "module map file entry not found");
318
319 // Remove directly passed modulemap files. They will get added back if they
320 // were actually used.
321 CI.getMutFrontendOpts().ModuleMapFiles.clear();
322
323 auto DepModuleMapFiles = collectModuleMapFiles(ClangModuleDeps: Deps.ClangModuleDeps);
324 for (StringRef ModuleMapFile : Deps.ModuleMapFileDeps) {
325 // TODO: Track these as `FileEntryRef` to simplify the equality check below.
326 auto ModuleMapEntry =
327 ScanInstance.getFileManager().getOptionalFileRef(Filename: ModuleMapFile);
328 assert(ModuleMapEntry && "module map file entry not found");
329
330 // Don't report module maps describing eagerly-loaded dependency. This
331 // information will be deserialized from the PCM.
332 // TODO: Verify this works fine when modulemap for module A is eagerly
333 // loaded from A.pcm, and module map passed on the command line contains
334 // definition of a submodule: "explicit module A.Private { ... }".
335 if (Service.getOpts().EagerLoadModules &&
336 DepModuleMapFiles.contains(V: *ModuleMapEntry))
337 continue;
338
339 // Don't report module map file of the current module unless it also
340 // describes a dependency (for symmetry).
341 if (*ModuleMapEntry == *CurrentModuleMapEntry &&
342 !DepModuleMapFiles.contains(V: *ModuleMapEntry))
343 continue;
344
345 CI.getMutFrontendOpts().ModuleMapFiles.emplace_back(args&: ModuleMapFile);
346 }
347
348 // Report the prebuilt modules this module uses.
349 for (const auto &PrebuiltModule : Deps.PrebuiltModuleDeps)
350 CI.getMutFrontendOpts().ModuleFiles.push_back(x: PrebuiltModule.PCMFile);
351
352 // Add module file inputs from dependencies.
353 addModuleFiles(CI, ClangModuleDeps: Deps.ClangModuleDeps);
354
355 if (!CI.getDiagnosticOpts().SystemHeaderWarningsModules.empty()) {
356 // Apply -Wsystem-headers-in-module for the current module.
357 if (llvm::is_contained(Range: CI.getDiagnosticOpts().SystemHeaderWarningsModules,
358 Element: Deps.ID.ModuleName))
359 CI.getMutDiagnosticOpts().Warnings.push_back(x: "system-headers");
360 // Remove the now unused option(s).
361 CI.getMutDiagnosticOpts().SystemHeaderWarningsModules.clear();
362 }
363
364 Optimize(CI);
365
366 return CI;
367}
368
369llvm::DenseSet<const FileEntry *> ModuleDepCollector::collectModuleMapFiles(
370 ArrayRef<ModuleID> ClangModuleDeps) const {
371 llvm::DenseSet<const FileEntry *> ModuleMapFiles;
372 for (const ModuleID &MID : ClangModuleDeps) {
373 ModuleDeps *MD = ModuleDepsByID.lookup(Val: MID);
374 assert(MD && "Inconsistent dependency info");
375 // TODO: Track ClangModuleMapFile as `FileEntryRef`.
376 auto FE = ScanInstance.getFileManager().getOptionalFileRef(
377 Filename: MD->ClangModuleMapFile);
378 assert(FE && "Missing module map file that was previously found");
379 ModuleMapFiles.insert(V: *FE);
380 }
381 return ModuleMapFiles;
382}
383
384void ModuleDepCollector::addModuleMapFiles(
385 CompilerInvocation &CI, ArrayRef<ModuleID> ClangModuleDeps) const {
386 if (Service.getOpts().EagerLoadModules)
387 return; // Only pcm is needed for eager load.
388
389 for (const ModuleID &MID : ClangModuleDeps) {
390 ModuleDeps *MD = ModuleDepsByID.lookup(Val: MID);
391 assert(MD && "Inconsistent dependency info");
392 CI.getFrontendOpts().ModuleMapFiles.push_back(x: MD->ClangModuleMapFile);
393 }
394}
395
396void ModuleDepCollector::addModuleFiles(
397 CompilerInvocation &CI, ArrayRef<ModuleID> ClangModuleDeps) const {
398 for (const ModuleID &MID : ClangModuleDeps) {
399 ModuleDeps *MD = ModuleDepsByID.lookup(Val: MID);
400 std::string PCMPath =
401 Controller.lookupModuleOutput(MD: *MD, Kind: ModuleOutputKind::ModuleFile);
402
403 if (Service.getOpts().EagerLoadModules)
404 CI.getFrontendOpts().ModuleFiles.push_back(x: std::move(PCMPath));
405 else
406 CI.getHeaderSearchOpts().PrebuiltModuleFiles.insert(
407 x: {MID.ModuleName, std::move(PCMPath)});
408 }
409}
410
411void ModuleDepCollector::addModuleFiles(
412 CowCompilerInvocation &CI, ArrayRef<ModuleID> ClangModuleDeps) const {
413 for (const ModuleID &MID : ClangModuleDeps) {
414 ModuleDeps *MD = ModuleDepsByID.lookup(Val: MID);
415 std::string PCMPath =
416 Controller.lookupModuleOutput(MD: *MD, Kind: ModuleOutputKind::ModuleFile);
417
418 if (Service.getOpts().EagerLoadModules)
419 CI.getMutFrontendOpts().ModuleFiles.push_back(x: std::move(PCMPath));
420 else
421 CI.getMutHeaderSearchOpts().PrebuiltModuleFiles.insert(
422 x: {MID.ModuleName, std::move(PCMPath)});
423 }
424}
425
426static bool needsModules(FrontendInputFile FIF) {
427 switch (FIF.getKind().getLanguage()) {
428 case Language::Unknown:
429 case Language::Asm:
430 case Language::LLVM_IR:
431 return false;
432 default:
433 return true;
434 }
435}
436
437void ModuleDepCollector::applyDiscoveredDependencies(CompilerInvocation &CI) {
438 CI.clearImplicitModuleBuildOptions();
439 resetBenignCodeGenOptions(ProgramAction: CI.getFrontendOpts().ProgramAction,
440 LangOpts: CI.getLangOpts(), CGOpts&: CI.getCodeGenOpts());
441 CI.getDependencyOutputOpts().IncludeModuleFiles = MFDK_Direct;
442
443 if (llvm::any_of(Range&: CI.getFrontendOpts().Inputs, P: needsModules)) {
444 Preprocessor &PP = ScanInstance.getPreprocessor();
445 if (Module *CurrentModule = PP.getCurrentModuleImplementation())
446 if (OptionalFileEntryRef CurrentModuleMap =
447 PP.getHeaderSearchInfo()
448 .getModuleMap()
449 .getModuleMapFileForUniquing(M: CurrentModule))
450 CI.getFrontendOpts().ModuleMapFiles.emplace_back(
451 args: CurrentModuleMap->getNameAsRequested());
452
453 SmallVector<ModuleID> DirectDeps;
454 for (const auto &KV : ModularDeps)
455 if (DirectModularDeps.contains(key: KV.first))
456 DirectDeps.push_back(Elt: KV.second->ID);
457
458 // TODO: Report module maps the same way it's done for modular dependencies.
459 addModuleMapFiles(CI, ClangModuleDeps: DirectDeps);
460
461 addModuleFiles(CI, ClangModuleDeps: DirectDeps);
462
463 for (const auto &KV : DirectPrebuiltModularDeps)
464 CI.getFrontendOpts().ModuleFiles.push_back(x: KV.second.PCMFile);
465 }
466}
467
468static bool isSafeToIgnoreCWD(const CowCompilerInvocation &CI) {
469 // Check if the command line input uses relative paths.
470 // It is not safe to ignore the current working directory if any of the
471 // command line inputs use relative paths.
472 bool AnyRelative = false;
473 CI.visitPaths(Cb: [&](StringRef Path) {
474 assert(!AnyRelative && "Continuing path visitation despite relative path");
475 AnyRelative |= !Path.empty() && !llvm::sys::path::is_absolute(path: Path);
476 return CowCompilerInvocation::VisitConstResult{/*Terminate=*/AnyRelative};
477 });
478 return !AnyRelative;
479}
480
481static std::string getModuleContextHash(const ModuleDeps &MD,
482 const CowCompilerInvocation &CI,
483 bool EagerLoadModules,
484 llvm::vfs::FileSystem &VFS) {
485 llvm::HashBuilder<llvm::TruncatedBLAKE3<16>, llvm::endianness::native>
486 HashBuilder;
487
488 // Hash the compiler version and serialization version to ensure the module
489 // will be readable.
490 HashBuilder.add(Value: getClangFullRepositoryVersion());
491 HashBuilder.add(Args: serialization::VERSION_MAJOR, Args: serialization::VERSION_MINOR);
492 llvm::ErrorOr<std::string> CWD = VFS.getCurrentWorkingDirectory();
493 if (CWD && !MD.IgnoreCWD)
494 HashBuilder.add(Value: *CWD);
495
496 // Hash the BuildInvocation without any input files.
497 SmallString<0> ArgVec;
498 ArgVec.reserve(N: 4096);
499 CI.generateCC1CommandLine(Consumer: [&](const Twine &Arg) {
500 Arg.toVector(Out&: ArgVec);
501 ArgVec.push_back(Elt: '\0');
502 });
503 HashBuilder.add(Value: ArgVec);
504
505 // Hash the module dependencies. These paths may differ even if the invocation
506 // is identical if they depend on the contents of the files in the TU -- for
507 // example, case-insensitive paths to modulemap files. Usually such a case
508 // would indicate a missed optimization to canonicalize, but it may be
509 // difficult to canonicalize all cases when there is a VFS.
510 for (const auto &ID : MD.ClangModuleDeps) {
511 HashBuilder.add(Value: ID.ModuleName);
512 HashBuilder.add(Value: ID.ContextHash);
513 }
514
515 HashBuilder.add(Value: EagerLoadModules);
516
517 llvm::BLAKE3Result<16> Hash = HashBuilder.final();
518 std::array<uint64_t, 2> Words;
519 static_assert(sizeof(Hash) == sizeof(Words), "Hash must match Words");
520 std::memcpy(dest: Words.data(), src: Hash.data(), n: sizeof(Hash));
521 return toString(I: llvm::APInt(sizeof(Words) * 8, Words), Radix: 36, /*Signed=*/false);
522}
523
524void ModuleDepCollector::associateWithContextHash(
525 const CowCompilerInvocation &CI, ModuleDeps &Deps) {
526 Deps.ID.ContextHash =
527 getModuleContextHash(MD: Deps, CI, EagerLoadModules: Service.getOpts().EagerLoadModules,
528 VFS&: ScanInstance.getVirtualFileSystem());
529 bool Inserted = ModuleDepsByID.insert(KV: {Deps.ID, &Deps}).second;
530 (void)Inserted;
531 assert(Inserted && "duplicate module mapping");
532}
533
534/// Callback that records textual includes and direct modular includes/imports
535/// during preprocessing.
536class ModuleDepCollector::ModuleDepCollectorPP final : public PPCallbacks {
537 /// The parent dependency collector.
538 ModuleDepCollector &MDC;
539
540public:
541 ModuleDepCollectorPP(ModuleDepCollector &MDC) : MDC(MDC) {}
542
543 void LexedFileChanged(FileID FID, LexedFileChangeReason Reason,
544 SrcMgr::CharacteristicKind FileType, FileID PrevFID,
545 SourceLocation Loc) override {
546 if (Reason != LexedFileChangeReason::EnterFile)
547 return;
548
549 SourceManager &SM = MDC.ScanInstance.getSourceManager();
550
551 // Dependency generation really does want to go all the way to the
552 // file entry for a source location to find out what is depended on.
553 // We do not want #line markers to affect dependency generation!
554 if (std::optional<StringRef> Filename = SM.getNonBuiltinFilenameForID(FID))
555 MDC.addFileDep(Path: llvm::sys::path::remove_leading_dotslash(path: *Filename));
556 }
557
558 void HasInclude(SourceLocation Loc, StringRef FileName, bool IsAngled,
559 OptionalFileEntryRef File,
560 SrcMgr::CharacteristicKind FileType) override {
561 if (File)
562 MDC.addFileDep(Path: File->getName());
563 }
564
565 void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
566 StringRef FileName, bool IsAngled,
567 CharSourceRange FilenameRange,
568 OptionalFileEntryRef File, StringRef SearchPath,
569 StringRef RelativePath, const Module *SuggestedModule,
570 bool ModuleImported,
571 SrcMgr::CharacteristicKind FileType) override {
572 if (!File && !ModuleImported) {
573 // This is a non-modular include that HeaderSearch failed to find. Add it
574 // here as `FileChanged` will never see it.
575 MDC.addFileDep(Path: FileName);
576 }
577 MDC.handleImport(Imported: SuggestedModule);
578 }
579
580 void moduleImport(SourceLocation ImportLoc, ModuleIdPath Path,
581 const Module *Imported) override {
582 auto &PP = MDC.ScanInstance.getPreprocessor();
583 if (PP.getLangOpts().CPlusPlusModules && PP.isImportingCXXNamedModules()) {
584 P1689ModuleInfo RequiredModule;
585 RequiredModule.ModuleName = Path[0].getIdentifierInfo()->getName().str();
586 RequiredModule.Type = P1689ModuleInfo::ModuleType::NamedCXXModule;
587 MDC.RequiredStdCXXModules.push_back(x: std::move(RequiredModule));
588 return;
589 }
590
591 MDC.handleImport(Imported);
592 }
593};
594
595void ModuleDepCollector::handleImport(const Module *Imported) {
596 auto &MDC = *this;
597
598 if (!Imported)
599 return;
600
601 const Module *TopLevelModule = Imported->getTopLevelModule();
602 const ModuleFileKey *MFKey = TopLevelModule->getASTFileKey();
603 if (!MFKey)
604 return;
605 serialization::ModuleFile *MF =
606 MDC.ScanInstance.getASTReader()->getModuleManager().lookup(Key: *MFKey);
607
608 if (MDC.isPrebuiltModule(MF))
609 MDC.DirectPrebuiltModularDeps.insert(KV: {MF, createPrebuiltModuleDep(MF)});
610 else {
611 MDC.DirectModularDeps.insert(X: MF);
612 MDC.DirectImports.insert(Ptr: Imported);
613 }
614}
615
616void ModuleDepCollector::run(DependencyConsumer &Consumer) {
617 auto &MDC = *this;
618
619 FileID MainFileID = MDC.ScanInstance.getSourceManager().getMainFileID();
620 MDC.MainFile = std::string(MDC.ScanInstance.getSourceManager()
621 .getFileEntryRefForID(FID: MainFileID)
622 ->getName());
623
624 auto &PP = MDC.ScanInstance.getPreprocessor();
625 if (PP.isInNamedModule()) {
626 P1689ModuleInfo ProvidedModule;
627 ProvidedModule.ModuleName = PP.getNamedModuleName();
628 ProvidedModule.Type = P1689ModuleInfo::ModuleType::NamedCXXModule;
629 ProvidedModule.IsStdCXXModuleInterface = PP.isInNamedInterfaceUnit();
630 // Don't put implementation (non partition) unit as Provide.
631 // Put the module as required instead. Since the implementation
632 // unit will import the primary module implicitly.
633 if (PP.isInImplementationUnit())
634 MDC.RequiredStdCXXModules.push_back(x: ProvidedModule);
635 else
636 MDC.ProvidedStdCXXModule = ProvidedModule;
637 }
638
639 if (!MDC.ScanInstance.getPreprocessorOpts().ImplicitPCHInclude.empty())
640 MDC.addFileDep(Path: MDC.ScanInstance.getPreprocessorOpts().ImplicitPCHInclude);
641
642 for (StringRef VFS : MDC.ScanInstance.getHeaderSearchOpts().VFSOverlayFiles)
643 MDC.addFileDep(Path: VFS);
644
645 if (Module *CurrentModule = PP.getCurrentModuleImplementation()) {
646 if (OptionalFileEntryRef CurrentModuleMap =
647 PP.getHeaderSearchInfo().getModuleMap().getModuleMapFileForUniquing(
648 M: CurrentModule))
649 MDC.addFileDep(Path: CurrentModuleMap->getName());
650 }
651
652 for (const Module *M :
653 MDC.ScanInstance.getPreprocessor().getAffectingClangModules()) {
654 serialization::ModuleFile *MF =
655 MDC.ScanInstance.getASTReader()->getModuleManager().lookup(
656 Key: *M->getASTFileKey());
657 if (!MDC.isPrebuiltModule(MF))
658 MDC.DirectModularDeps.insert(X: MF);
659 }
660
661 if (MDC.Service.getOpts().ReportVisibleModules)
662 MDC.addVisibleModules();
663
664 for (serialization::ModuleFile *MF : MDC.DirectModularDeps)
665 handleTopLevelModule(MF);
666
667 Consumer.handleContextHash(
668 Hash: MDC.ScanInstance.getInvocation().computeContextHash());
669
670 Consumer.handleDependencyOutputOpts(Opts: *MDC.Opts);
671
672 Consumer.handleProvidedAndRequiredStdCXXModules(Provided: MDC.ProvidedStdCXXModule,
673 Requires: MDC.RequiredStdCXXModules);
674
675 for (auto &&I : MDC.ModularDeps)
676 Consumer.handleModuleDependency(MD: *I.second);
677
678 for (serialization::ModuleFile *MF : MDC.DirectModularDeps) {
679 auto It = MDC.ModularDeps.find(Key: MF);
680 // Only report direct dependencies that were successfully handled.
681 if (It != MDC.ModularDeps.end())
682 Consumer.handleDirectModuleDependency(MD: It->second->ID);
683 }
684
685 for (auto &&I : MDC.VisibleModules)
686 Consumer.handleVisibleModule(ModuleName: std::string(I.getKey()));
687
688 for (auto &&I : MDC.FileDeps)
689 Consumer.handleFileDependency(Filename: I);
690
691 for (auto &&I : MDC.DirectPrebuiltModularDeps)
692 Consumer.handlePrebuiltModuleDependency(PMD: I.second);
693}
694
695static StringRef makeAbsoluteAndCanonicalize(CompilerInstance &CI,
696 StringRef Path,
697 SmallVectorImpl<char> &Storage) {
698 // FIXME: Consider skipping if path is already absolute & canonicalized.
699
700 Storage.assign(in_start: Path.begin(), in_end: Path.end());
701 CI.getFileManager().makeAbsolutePath(Path&: Storage, /*Canonicalize=*/true);
702 return StringRef(Storage.data(), Storage.size());
703}
704
705std::optional<ModuleID>
706ModuleDepCollector::handleTopLevelModule(serialization::ModuleFile *MF) {
707 auto &MDC = *this;
708
709 // If this module has been handled already, just return its ID.
710 if (auto ModI = MDC.ModularDeps.find(Key: MF); ModI != MDC.ModularDeps.end())
711 return ModI->second->ID;
712
713 Module *M = MDC.ScanInstance.getPreprocessor()
714 .getHeaderSearchInfo()
715 .getModuleMap()
716 .findModule(Name: MF->ModuleName);
717 assert(M && M == M->getTopLevelModule() &&
718 "ModuleFile without top-level Module");
719
720 auto OwnedMD = std::make_unique<ModuleDeps>();
721 ModuleDeps &MD = *OwnedMD;
722
723 MD.ID.ModuleName = MF->ModuleName;
724 MD.IsSystem = M->IsSystem;
725
726 // Start off with the assumption that this module is shareable when there
727 // are stable directories. As more dependencies are discovered, check if those
728 // come from the provided directories.
729 MD.IsInStableDirectories = !MDC.StableDirs.empty();
730
731 // For modules which use export_as link name, the linked product that of the
732 // corresponding export_as-named module.
733 if (!M->UseExportAsModuleLinkName)
734 MD.LinkLibraries = M->LinkLibraries;
735
736 ModuleMap &ModMapInfo =
737 MDC.ScanInstance.getPreprocessor().getHeaderSearchInfo().getModuleMap();
738
739 if (auto ModuleMap = ModMapInfo.getModuleMapFileForUniquing(M)) {
740 SmallString<128> Path = ModuleMap->getNameAsRequested();
741 ModMapInfo.canonicalizeModuleMapPath(Path);
742 MD.ClangModuleMapFile = std::string(Path);
743 }
744
745 llvm::SmallString<256> Storage;
746 MD.FileDepsBaseDir =
747 makeAbsoluteAndCanonicalize(CI&: MDC.ScanInstance, Path: MF->BaseDirectory, Storage);
748 MDC.ScanInstance.getASTReader()->visitInputFileInfos(
749 MF&: *MF, /*IncludeSystem=*/true,
750 Visitor: [&](const serialization::InputFileInfo &IFI, bool IsSystem) {
751 // The __inferred_module.map file is an insignificant implementation
752 // detail of implicitly-built modules. The PCM will also report the
753 // actual on-disk module map file that allowed inferring the module,
754 // which is what we need for building the module explicitly
755 // Let's ignore this file.
756 if (IFI.UnresolvedImportedFilename.ends_with(Suffix: "__inferred_module.map"))
757 return;
758 MDC.addFileDep(MD, Path: IFI.UnresolvedImportedFilename);
759 });
760
761 addAllModuleDeps(MF&: *MF, MD);
762
763 SmallString<0> PathBuf;
764 PathBuf.reserve(N: 256);
765 MDC.ScanInstance.getASTReader()->visitInputFileInfos(
766 MF&: *MF, /*IncludeSystem=*/true,
767 Visitor: [&](const serialization::InputFileInfo &IFI, bool IsSystem) {
768 if (MD.IsInStableDirectories) {
769 auto FullFilePath = ASTReader::ResolveImportedPath(
770 Buf&: PathBuf, Path: IFI.UnresolvedImportedFilename, Prefix: MF->BaseDirectory);
771 MD.IsInStableDirectories =
772 isPathInStableDir(Directories: MDC.StableDirs, Input: *FullFilePath);
773 }
774 if (!(IFI.TopLevel && IFI.ModuleMap))
775 return;
776 if (IFI.UnresolvedImportedFilenameAsRequested.ends_with(
777 Suffix: "__inferred_module.map"))
778 return;
779 auto ResolvedFilenameAsRequested = ASTReader::ResolveImportedPath(
780 Buf&: PathBuf, Path: IFI.UnresolvedImportedFilenameAsRequested,
781 Prefix: MF->BaseDirectory);
782 MD.ModuleMapFileDeps.emplace_back(args: *ResolvedFilenameAsRequested);
783 });
784
785 bool IgnoreCWD = false;
786 CowCompilerInvocation CI =
787 MDC.getInvocationAdjustedForModuleBuildWithoutOutputs(
788 Deps: MD, Optimize: [&](CowCompilerInvocation &BuildInvocation) {
789 if (any(Val: MDC.Service.getOpts().OptimizeArgs &
790 (ScanningOptimizations::HeaderSearch |
791 ScanningOptimizations::VFS)))
792 optimizeHeaderSearchOpts(Opts&: BuildInvocation.getMutHeaderSearchOpts(),
793 Reader&: *MDC.ScanInstance.getASTReader(), MF: *MF,
794 PrebuiltModulesASTMap: MDC.PrebuiltModulesASTMap,
795 OptimizeArgs: MDC.Service.getOpts().OptimizeArgs);
796
797 if (any(Val: MDC.Service.getOpts().OptimizeArgs &
798 ScanningOptimizations::SystemWarnings))
799 optimizeDiagnosticOpts(
800 Opts&: BuildInvocation.getMutDiagnosticOpts(),
801 IsSystemModule: BuildInvocation.getFrontendOpts().IsSystemModule);
802
803 IgnoreCWD = any(Val: MDC.Service.getOpts().OptimizeArgs &
804 ScanningOptimizations::IgnoreCWD) &&
805 isSafeToIgnoreCWD(CI: BuildInvocation);
806 if (IgnoreCWD) {
807 llvm::ErrorOr<std::string> CWD =
808 MDC.ScanInstance.getVirtualFileSystem()
809 .getCurrentWorkingDirectory();
810 if (CWD)
811 optimizeCWD(BuildInvocation, CWD: *CWD);
812 }
813 });
814
815 // FIXME: Propagate errors up.
816 (void)MDC.Controller.finalizeModuleInvocation(ScanInstance&: MDC.ScanInstance, CI, MD);
817
818 // Check provided input paths from the invocation for determining
819 // IsInStableDirectories.
820 if (MD.IsInStableDirectories)
821 MD.IsInStableDirectories =
822 areOptionsInStableDir(Directories: MDC.StableDirs, HSOpts: CI.getHeaderSearchOpts());
823
824 MD.IgnoreCWD = IgnoreCWD;
825 MDC.associateWithContextHash(CI, Deps&: MD);
826
827 // Finish the compiler invocation. Requires dependencies and the context hash.
828 MDC.addOutputPaths(CI, Deps&: MD);
829
830 MD.BuildInfo = std::move(CI);
831
832 MDC.ModularDeps.insert(KV: {MF, std::move(OwnedMD)});
833
834 return MD.ID;
835}
836
837void ModuleDepCollector::addAllModuleDeps(serialization::ModuleFile &MF,
838 ModuleDeps &MD) {
839 auto &MDC = *this;
840
841 llvm::DenseSet<const Module *> Seen;
842 for (serialization::ModuleFile *Import : MF.Imports) {
843 if (MDC.isPrebuiltModule(MF: Import)) {
844 MD.PrebuiltModuleDeps.push_back(x: createPrebuiltModuleDep(MF: Import));
845 if (MD.IsInStableDirectories) {
846 auto It = MDC.PrebuiltModulesASTMap.find(
847 Key: MD.PrebuiltModuleDeps.back().PCMFile);
848 MD.IsInStableDirectories =
849 It != MDC.PrebuiltModulesASTMap.end() && It->second.isInStableDir();
850 }
851 } else {
852 if (auto ID = handleTopLevelModule(MF: Import)) {
853 MD.ClangModuleDeps.push_back(x: std::move(*ID));
854 if (MD.IsInStableDirectories)
855 MD.IsInStableDirectories =
856 MDC.ModularDeps[Import]->IsInStableDirectories;
857 }
858 }
859 }
860}
861
862ModuleDepCollector::ModuleDepCollector(
863 DependencyScanningService &Service,
864 std::unique_ptr<DependencyOutputOptions> Opts,
865 CompilerInstance &ScanInstance, DependencyActionController &Controller,
866 CompilerInvocation OriginalCI,
867 const PrebuiltModulesAttrsMap PrebuiltModulesASTMap,
868 const ArrayRef<StringRef> StableDirs)
869 : Service(Service), ScanInstance(ScanInstance), Controller(Controller),
870 PrebuiltModulesASTMap(std::move(PrebuiltModulesASTMap)),
871 StableDirs(StableDirs), Opts(std::move(Opts)),
872 CommonInvocation(
873 makeCommonInvocationForModuleBuild(CI: std::move(OriginalCI))) {}
874
875void ModuleDepCollector::attachToPreprocessor(Preprocessor &PP) {
876 auto CollectorPP = std::make_unique<ModuleDepCollectorPP>(args&: *this);
877 CollectorPPPtr = CollectorPP.get();
878 PP.addPPCallbacks(C: std::move(CollectorPP));
879}
880
881void ModuleDepCollector::attachToASTReader(ASTReader &R) {}
882
883bool ModuleDepCollector::isPrebuiltModule(const serialization::ModuleFile *MF) {
884 const auto &PrebuiltModuleFiles =
885 ScanInstance.getHeaderSearchOpts().PrebuiltModuleFiles;
886 auto PrebuiltModuleFileIt = PrebuiltModuleFiles.find(x: MF->ModuleName);
887 if (PrebuiltModuleFileIt == PrebuiltModuleFiles.end())
888 return false;
889 assert("Prebuilt module came from the expected AST file" &&
890 PrebuiltModuleFileIt->second == MF->FileName.str());
891 return true;
892}
893
894void ModuleDepCollector::addVisibleModules() {
895 llvm::DenseSet<const Module *> ImportedModules;
896 auto InsertVisibleModules = [&](const Module *M) {
897 if (ImportedModules.contains(V: M))
898 return;
899
900 VisibleModules.insert(key: M->getTopLevelModuleName());
901 SmallVector<Module *> Stack;
902 M->getExportedModules(Exported&: Stack);
903 while (!Stack.empty()) {
904 const Module *CurrModule = Stack.pop_back_val();
905 if (ImportedModules.contains(V: CurrModule))
906 continue;
907 ImportedModules.insert(V: CurrModule);
908 VisibleModules.insert(key: CurrModule->getTopLevelModuleName());
909 CurrModule->getExportedModules(Exported&: Stack);
910 }
911 };
912
913 for (const Module *Import : DirectImports)
914 InsertVisibleModules(Import);
915}
916
917void ModuleDepCollector::addFileDep(StringRef Path) {
918 if (!Service.getOpts().ReportAbsolutePaths) {
919 FileDeps.emplace_back(args&: Path);
920 return;
921 }
922
923 llvm::SmallString<256> Storage;
924 Path = makeAbsoluteAndCanonicalize(CI&: ScanInstance, Path, Storage);
925 FileDeps.emplace_back(args&: Path);
926}
927
928void ModuleDepCollector::addFileDep(ModuleDeps &MD, StringRef Path) {
929 MD.FileDeps.emplace_back(args&: Path);
930}
931