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