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
19using namespace clang;
20using namespace tooling;
21using namespace dependencies;
22
23void ModuleDeps::forEachFileDep(llvm::function_ref<void(StringRef)> Cb) const {
24 SmallString<0> PathBuf;
25 PathBuf.reserve(N: 256);
26 for (StringRef FileDep : FileDeps) {
27 auto ResolvedFileDep =
28 ASTReader::ResolveImportedPath(Buf&: PathBuf, Path: FileDep, Prefix: FileDepsBaseDir);
29 Cb(*ResolvedFileDep);
30 }
31}
32
33const 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>(ptr: &BuildInfo))
39 BuildInfo = CI->getCC1CommandLine();
40 return std::get<std::vector<std::string>>(v&: BuildInfo);
41}
42
43void 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
53static void
54optimizeHeaderSearchOpts(HeaderSearchOptions &Opts, ASTReader &Reader,
55 const serialization::ModuleFile &MF,
56 const PrebuiltModulesAttrsMap &PrebuiltModulesASTMap,
57 ScanningOptimizations OptimizeArgs) {
58 if (any(Val: OptimizeArgs & ScanningOptimizations::HeaderSearch)) {
59 // Only preserve search paths that were used during the dependency scan.
60 std::vector<HeaderSearchOptions::Entry> Entries;
61 std::swap(x&: Opts.UserEntries, y&: 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(V: MF);
69 for (const serialization::ModuleFile *Import : MF->Imports)
70 if (!Visited.contains(V: Import))
71 VisitMF(Import);
72 };
73 VisitMF(&MF);
74
75 if (SearchPathUsage.size() != Entries.size())
76 llvm::report_fatal_error(
77 reason: "Inconsistent search path options between modules detected");
78
79 for (auto Idx : SearchPathUsage.set_bits())
80 Opts.UserEntries.push_back(x: std::move(Entries[Idx]));
81 }
82 if (any(Val: OptimizeArgs & ScanningOptimizations::VFS)) {
83 std::vector<std::string> VFSOverlayFiles;
84 std::swap(x&: Opts.VFSOverlayFiles, y&: 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(V: 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(V: 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(Key: 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 key: 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 reason: "Inconsistent -ivfsoverlay options between modules detected");
117
118 for (auto Idx : VFSUsage.set_bits())
119 Opts.VFSOverlayFiles.push_back(x: std::move(VFSOverlayFiles[Idx]));
120 }
121}
122
123static 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(Prefix: "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
145static 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(path: CWD);
170 }
171}
172
173static std::vector<std::string> splitString(std::string S, char Separator) {
174 SmallVector<StringRef> Segments;
175 StringRef(S).split(A&: Segments, Separator, /*MaxSplit=*/-1, /*KeepEmpty=*/false);
176 std::vector<std::string> Result;
177 Result.reserve(n: Segments.size());
178 for (StringRef Segment : Segments)
179 Result.push_back(x: Segment.str());
180 return Result;
181}
182
183void ModuleDepCollector::addOutputPaths(CowCompilerInvocation &CI,
184 ModuleDeps &Deps) {
185 CI.getMutFrontendOpts().OutputFile =
186 Controller.lookupModuleOutput(MD: Deps, Kind: ModuleOutputKind::ModuleFile);
187 if (!CI.getDiagnosticOpts().DiagnosticSerializationFile.empty())
188 CI.getMutDiagnosticOpts().DiagnosticSerializationFile =
189 Controller.lookupModuleOutput(
190 MD: Deps, Kind: ModuleOutputKind::DiagnosticSerializationFile);
191 if (!CI.getDependencyOutputOpts().OutputFile.empty()) {
192 CI.getMutDependencyOutputOpts().OutputFile =
193 Controller.lookupModuleOutput(MD: Deps, Kind: ModuleOutputKind::DependencyFile);
194 CI.getMutDependencyOutputOpts().Targets =
195 splitString(S: Controller.lookupModuleOutput(
196 MD: Deps, Kind: ModuleOutputKind::DependencyTargets),
197 Separator: '\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(Target: CI.getFrontendOpts().OutputFile, Res&: Target);
203 CI.getMutDependencyOutputOpts().Targets.push_back(x: std::string(Target));
204 }
205 }
206}
207
208void 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
228bool dependencies::isPathInStableDir(const ArrayRef<StringRef> Directories,
229 const StringRef Input) {
230 using namespace llvm::sys;
231
232 if (!path::is_absolute(path: Input))
233 return false;
234
235 auto PathStartsWith = [](StringRef Prefix, StringRef Path) {
236 auto PrefixIt = path::begin(path: Prefix), PrefixEnd = path::end(path: Prefix);
237 for (auto PathIt = path::begin(path: Path), PathEnd = path::end(path: 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(Range: Directories, P: [&](StringRef Dir) {
246 return !Dir.empty() && PathStartsWith(Dir, Input);
247 });
248}
249
250bool 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, Input: Entry.Path))
262 return false;
263 }
264
265 for (const auto &SysPrefix : HSOpts.SystemHeaderPrefixes) {
266 if (!isPathInStableDir(Directories, Input: SysPrefix.Prefix))
267 return false;
268 }
269
270 return true;
271}
272
273static CowCompilerInvocation
274makeCommonInvocationForModuleBuild(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(ProgramAction: frontend::GenerateModule, LangOpts: CI.getLangOpts(),
292 CGOpts&: 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 C&: CI.getPreprocessorOpts().Macros,
309 P: [&CI](const std::pair<std::string, bool> &Def) {
310 StringRef MacroDef = Def.first;
311 return CI.getHeaderSearchOpts().ModulesIgnoreMacros.contains(
312 key: llvm::CachedHashString(MacroDef.split(Separator: '=').first));
313 });
314 // Remove the now unused option.
315 CI.getHeaderSearchOpts().ModulesIgnoreMacros.clear();
316 }
317
318 return CI;
319}
320
321CowCompilerInvocation
322ModuleDepCollector::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(Args: Deps.ClangModuleMapFile,
334 Args&: ModuleMapInputKind);
335
336 auto CurrentModuleMapEntry =
337 ScanInstance.getFileManager().getOptionalFileRef(Filename: 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(ClangModuleDeps: 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(Filename: 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(V: *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(V: *ModuleMapEntry))
364 continue;
365
366 CI.getMutFrontendOpts().ModuleMapFiles.emplace_back(args&: ModuleMapFile);
367 }
368
369 // Report the prebuilt modules this module uses.
370 for (const auto &PrebuiltModule : Deps.PrebuiltModuleDeps)
371 CI.getMutFrontendOpts().ModuleFiles.push_back(x: PrebuiltModule.PCMFile);
372
373 // Add module file inputs from dependencies.
374 addModuleFiles(CI, ClangModuleDeps: Deps.ClangModuleDeps);
375
376 if (!CI.getDiagnosticOpts().SystemHeaderWarningsModules.empty()) {
377 // Apply -Wsystem-headers-in-module for the current module.
378 if (llvm::is_contained(Range: CI.getDiagnosticOpts().SystemHeaderWarningsModules,
379 Element: Deps.ID.ModuleName))
380 CI.getMutDiagnosticOpts().Warnings.push_back(x: "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
390llvm::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(Val: MID);
395 assert(MD && "Inconsistent dependency info");
396 // TODO: Track ClangModuleMapFile as `FileEntryRef`.
397 auto FE = ScanInstance.getFileManager().getOptionalFileRef(
398 Filename: MD->ClangModuleMapFile);
399 assert(FE && "Missing module map file that was previously found");
400 ModuleMapFiles.insert(V: *FE);
401 }
402 return ModuleMapFiles;
403}
404
405void 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(Val: MID);
412 assert(MD && "Inconsistent dependency info");
413 CI.getFrontendOpts().ModuleMapFiles.push_back(x: MD->ClangModuleMapFile);
414 }
415}
416
417void ModuleDepCollector::addModuleFiles(
418 CompilerInvocation &CI, ArrayRef<ModuleID> ClangModuleDeps) const {
419 for (const ModuleID &MID : ClangModuleDeps) {
420 ModuleDeps *MD = ModuleDepsByID.lookup(Val: MID);
421 std::string PCMPath =
422 Controller.lookupModuleOutput(MD: *MD, Kind: ModuleOutputKind::ModuleFile);
423
424 if (Service.shouldEagerLoadModules())
425 CI.getFrontendOpts().ModuleFiles.push_back(x: std::move(PCMPath));
426 else
427 CI.getHeaderSearchOpts().PrebuiltModuleFiles.insert(
428 x: {MID.ModuleName, std::move(PCMPath)});
429 }
430}
431
432void ModuleDepCollector::addModuleFiles(
433 CowCompilerInvocation &CI, ArrayRef<ModuleID> ClangModuleDeps) const {
434 for (const ModuleID &MID : ClangModuleDeps) {
435 ModuleDeps *MD = ModuleDepsByID.lookup(Val: MID);
436 std::string PCMPath =
437 Controller.lookupModuleOutput(MD: *MD, Kind: ModuleOutputKind::ModuleFile);
438
439 if (Service.shouldEagerLoadModules())
440 CI.getMutFrontendOpts().ModuleFiles.push_back(x: std::move(PCMPath));
441 else
442 CI.getMutHeaderSearchOpts().PrebuiltModuleFiles.insert(
443 x: {MID.ModuleName, std::move(PCMPath)});
444 }
445}
446
447static 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
458void ModuleDepCollector::applyDiscoveredDependencies(CompilerInvocation &CI) {
459 CI.clearImplicitModuleBuildOptions();
460 resetBenignCodeGenOptions(ProgramAction: CI.getFrontendOpts().ProgramAction,
461 LangOpts: CI.getLangOpts(), CGOpts&: CI.getCodeGenOpts());
462
463 if (llvm::any_of(Range&: CI.getFrontendOpts().Inputs, P: needsModules)) {
464 Preprocessor &PP = ScanInstance.getPreprocessor();
465 if (Module *CurrentModule = PP.getCurrentModuleImplementation())
466 if (OptionalFileEntryRef CurrentModuleMap =
467 PP.getHeaderSearchInfo()
468 .getModuleMap()
469 .getModuleMapFileForUniquing(M: CurrentModule))
470 CI.getFrontendOpts().ModuleMapFiles.emplace_back(
471 args: CurrentModuleMap->getNameAsRequested());
472
473 SmallVector<ModuleID> DirectDeps;
474 for (const auto &KV : ModularDeps)
475 if (DirectModularDeps.contains(key: KV.first))
476 DirectDeps.push_back(Elt: KV.second->ID);
477
478 // TODO: Report module maps the same way it's done for modular dependencies.
479 addModuleMapFiles(CI, ClangModuleDeps: DirectDeps);
480
481 addModuleFiles(CI, ClangModuleDeps: DirectDeps);
482
483 for (const auto &KV : DirectPrebuiltModularDeps)
484 CI.getFrontendOpts().ModuleFiles.push_back(x: KV.second.PCMFile);
485 }
486}
487
488static 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
570static 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(Value: getClangFullRepositoryVersion());
580 HashBuilder.add(Args: serialization::VERSION_MAJOR, Args: serialization::VERSION_MINOR);
581 llvm::ErrorOr<std::string> CWD = VFS.getCurrentWorkingDirectory();
582 if (CWD && !IgnoreCWD)
583 HashBuilder.add(Value: *CWD);
584
585 // Hash the BuildInvocation without any input files.
586 SmallString<0> ArgVec;
587 ArgVec.reserve(N: 4096);
588 CI.generateCC1CommandLine(Consumer: [&](const Twine &Arg) {
589 Arg.toVector(Out&: ArgVec);
590 ArgVec.push_back(Elt: '\0');
591 });
592 HashBuilder.add(Value: 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(Value: ID.ModuleName);
601 HashBuilder.add(Value: ID.ContextHash);
602 }
603
604 HashBuilder.add(Value: 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(dest: Words.data(), src: Hash.data(), n: sizeof(Hash));
610 return toString(I: llvm::APInt(sizeof(Words) * 8, Words), Radix: 36, /*Signed=*/false);
611}
612
613void ModuleDepCollector::associateWithContextHash(
614 const CowCompilerInvocation &CI, bool IgnoreCWD, ModuleDeps &Deps) {
615 Deps.ID.ContextHash =
616 getModuleContextHash(MD: Deps, CI, EagerLoadModules: Service.shouldEagerLoadModules(),
617 IgnoreCWD, VFS&: ScanInstance.getVirtualFileSystem());
618 bool Inserted = ModuleDepsByID.insert(KV: {Deps.ID, &Deps}).second;
619 (void)Inserted;
620 assert(Inserted && "duplicate module mapping");
621}
622
623void 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(Path: llvm::sys::path::remove_leading_dotslash(path: *Filename));
638}
639
640void 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(Path: FileName);
649 }
650 handleImport(Imported: SuggestedModule);
651}
652
653void 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(x: std::move(RequiredModule));
661 return;
662 }
663
664 handleImport(Imported);
665}
666
667void ModuleDepCollectorPP::handleImport(const Module *Imported) {
668 if (!Imported)
669 return;
670
671 const Module *TopLevelModule = Imported->getTopLevelModule();
672
673 if (MDC.isPrebuiltModule(M: TopLevelModule))
674 MDC.DirectPrebuiltModularDeps.insert(
675 KV: {TopLevelModule, PrebuiltModuleDep{TopLevelModule}});
676 else
677 MDC.DirectModularDeps.insert(X: TopLevelModule);
678}
679
680void ModuleDepCollectorPP::EndOfMainFile() {
681 FileID MainFileID = MDC.ScanInstance.getSourceManager().getMainFileID();
682 MDC.MainFile = std::string(MDC.ScanInstance.getSourceManager()
683 .getFileEntryRefForID(FID: MainFileID)
684 ->getName());
685
686 auto &PP = MDC.ScanInstance.getPreprocessor();
687 if (PP.isInNamedModule()) {
688 P1689ModuleInfo ProvidedModule;
689 ProvidedModule.ModuleName = PP.getNamedModuleName();
690 ProvidedModule.Type = P1689ModuleInfo::ModuleType::NamedCXXModule;
691 ProvidedModule.IsStdCXXModuleInterface = PP.isInNamedInterfaceUnit();
692 // Don't put implementation (non partition) unit as Provide.
693 // Put the module as required instead. Since the implementation
694 // unit will import the primary module implicitly.
695 if (PP.isInImplementationUnit())
696 MDC.RequiredStdCXXModules.push_back(x: ProvidedModule);
697 else
698 MDC.ProvidedStdCXXModule = ProvidedModule;
699 }
700
701 if (!MDC.ScanInstance.getPreprocessorOpts().ImplicitPCHInclude.empty())
702 MDC.addFileDep(Path: MDC.ScanInstance.getPreprocessorOpts().ImplicitPCHInclude);
703
704 for (const Module *M :
705 MDC.ScanInstance.getPreprocessor().getAffectingClangModules())
706 if (!MDC.isPrebuiltModule(M))
707 MDC.DirectModularDeps.insert(X: M);
708
709 for (const Module *M : MDC.DirectModularDeps)
710 handleTopLevelModule(M);
711
712 MDC.Consumer.handleContextHash(
713 Hash: MDC.ScanInstance.getInvocation().getModuleHash());
714
715 MDC.Consumer.handleDependencyOutputOpts(Opts: *MDC.Opts);
716
717 MDC.Consumer.handleProvidedAndRequiredStdCXXModules(
718 Provided: MDC.ProvidedStdCXXModule, Requires: MDC.RequiredStdCXXModules);
719
720 for (auto &&I : MDC.ModularDeps)
721 MDC.Consumer.handleModuleDependency(MD: *I.second);
722
723 for (const Module *M : MDC.DirectModularDeps) {
724 auto It = MDC.ModularDeps.find(Key: M);
725 // Only report direct dependencies that were successfully handled.
726 if (It != MDC.ModularDeps.end())
727 MDC.Consumer.handleDirectModuleDependency(MD: It->second->ID);
728 }
729
730 for (auto &&I : MDC.FileDeps)
731 MDC.Consumer.handleFileDependency(Filename: I);
732
733 for (auto &&I : MDC.DirectPrebuiltModularDeps)
734 MDC.Consumer.handlePrebuiltModuleDependency(PMD: I.second);
735}
736
737std::optional<ModuleID>
738ModuleDepCollectorPP::handleTopLevelModule(const Module *M) {
739 assert(M == M->getTopLevelModule() && "Expected top level module!");
740
741 // A top-level module might not be actually imported as a module when
742 // -fmodule-name is used to compile a translation unit that imports this
743 // module. In that case it can be skipped. The appropriate header
744 // dependencies will still be reported as expected.
745 if (!M->getASTFile())
746 return {};
747
748 // If this module has been handled already, just return its ID.
749 if (auto ModI = MDC.ModularDeps.find(Key: M); ModI != MDC.ModularDeps.end())
750 return ModI->second->ID;
751
752 auto OwnedMD = std::make_unique<ModuleDeps>();
753 ModuleDeps &MD = *OwnedMD;
754
755 MD.ID.ModuleName = M->getFullModuleName();
756 MD.IsSystem = M->IsSystem;
757
758 // Start off with the assumption that this module is shareable when there
759 // are stable directories. As more dependencies are discovered, check if those
760 // come from the provided directories.
761 MD.IsInStableDirectories = !MDC.StableDirs.empty();
762
763 // For modules which use export_as link name, the linked product that of the
764 // corresponding export_as-named module.
765 if (!M->UseExportAsModuleLinkName)
766 MD.LinkLibraries = M->LinkLibraries;
767
768 ModuleMap &ModMapInfo =
769 MDC.ScanInstance.getPreprocessor().getHeaderSearchInfo().getModuleMap();
770
771 if (auto ModuleMap = ModMapInfo.getModuleMapFileForUniquing(M)) {
772 SmallString<128> Path = ModuleMap->getNameAsRequested();
773 ModMapInfo.canonicalizeModuleMapPath(Path);
774 MD.ClangModuleMapFile = std::string(Path);
775 }
776
777 serialization::ModuleFile *MF =
778 MDC.ScanInstance.getASTReader()->getModuleManager().lookup(
779 File: *M->getASTFile());
780 MD.FileDepsBaseDir = MF->BaseDirectory;
781 MDC.ScanInstance.getASTReader()->visitInputFileInfos(
782 MF&: *MF, /*IncludeSystem=*/true,
783 Visitor: [&](const serialization::InputFileInfo &IFI, bool IsSystem) {
784 // The __inferred_module.map file is an insignificant implementation
785 // detail of implicitly-built modules. The PCM will also report the
786 // actual on-disk module map file that allowed inferring the module,
787 // which is what we need for building the module explicitly
788 // Let's ignore this file.
789 if (IFI.UnresolvedImportedFilename.ends_with(Suffix: "__inferred_module.map"))
790 return;
791 MDC.addFileDep(MD, Path: IFI.UnresolvedImportedFilename);
792 });
793
794 llvm::DenseSet<const Module *> SeenDeps;
795 addAllSubmodulePrebuiltDeps(M, MD, SeenSubmodules&: SeenDeps);
796 addAllSubmoduleDeps(M, MD, AddedModules&: SeenDeps);
797 addAllAffectingClangModules(M, MD, AddedModules&: SeenDeps);
798
799 SmallString<0> PathBuf;
800 PathBuf.reserve(N: 256);
801 MDC.ScanInstance.getASTReader()->visitInputFileInfos(
802 MF&: *MF, /*IncludeSystem=*/true,
803 Visitor: [&](const serialization::InputFileInfo &IFI, bool IsSystem) {
804 if (MD.IsInStableDirectories) {
805 auto FullFilePath = ASTReader::ResolveImportedPath(
806 Buf&: PathBuf, Path: IFI.UnresolvedImportedFilename, Prefix: MF->BaseDirectory);
807 MD.IsInStableDirectories =
808 isPathInStableDir(Directories: MDC.StableDirs, Input: *FullFilePath);
809 }
810 if (!(IFI.TopLevel && IFI.ModuleMap))
811 return;
812 if (IFI.UnresolvedImportedFilenameAsRequested.ends_with(
813 Suffix: "__inferred_module.map"))
814 return;
815 auto ResolvedFilenameAsRequested = ASTReader::ResolveImportedPath(
816 Buf&: PathBuf, Path: IFI.UnresolvedImportedFilenameAsRequested,
817 Prefix: MF->BaseDirectory);
818 MD.ModuleMapFileDeps.emplace_back(args: *ResolvedFilenameAsRequested);
819 });
820
821 bool IgnoreCWD = false;
822 CowCompilerInvocation CI =
823 MDC.getInvocationAdjustedForModuleBuildWithoutOutputs(
824 Deps: MD, Optimize: [&](CowCompilerInvocation &BuildInvocation) {
825 if (any(Val: MDC.Service.getOptimizeArgs() &
826 (ScanningOptimizations::HeaderSearch |
827 ScanningOptimizations::VFS)))
828 optimizeHeaderSearchOpts(Opts&: BuildInvocation.getMutHeaderSearchOpts(),
829 Reader&: *MDC.ScanInstance.getASTReader(), MF: *MF,
830 PrebuiltModulesASTMap: MDC.PrebuiltModulesASTMap,
831 OptimizeArgs: MDC.Service.getOptimizeArgs());
832
833 if (any(Val: MDC.Service.getOptimizeArgs() &
834 ScanningOptimizations::SystemWarnings))
835 optimizeDiagnosticOpts(
836 Opts&: BuildInvocation.getMutDiagnosticOpts(),
837 IsSystemModule: BuildInvocation.getFrontendOpts().IsSystemModule);
838
839 IgnoreCWD = any(Val: MDC.Service.getOptimizeArgs() &
840 ScanningOptimizations::IgnoreCWD) &&
841 isSafeToIgnoreCWD(CI: BuildInvocation);
842 if (IgnoreCWD) {
843 llvm::ErrorOr<std::string> CWD =
844 MDC.ScanInstance.getVirtualFileSystem()
845 .getCurrentWorkingDirectory();
846 if (CWD)
847 optimizeCWD(BuildInvocation, CWD: *CWD);
848 }
849 });
850
851 // Check provided input paths from the invocation for determining
852 // IsInStableDirectories.
853 if (MD.IsInStableDirectories)
854 MD.IsInStableDirectories =
855 areOptionsInStableDir(Directories: MDC.StableDirs, HSOpts: CI.getHeaderSearchOpts());
856
857 MDC.associateWithContextHash(CI, IgnoreCWD, Deps&: MD);
858
859 // Finish the compiler invocation. Requires dependencies and the context hash.
860 MDC.addOutputPaths(CI, Deps&: MD);
861
862 MD.BuildInfo = std::move(CI);
863
864 MDC.ModularDeps.insert(KV: {M, std::move(OwnedMD)});
865
866 return MD.ID;
867}
868
869static void forEachSubmoduleSorted(const Module *M,
870 llvm::function_ref<void(const Module *)> F) {
871 // Submodule order depends on order of header includes for inferred submodules
872 // we don't care about the exact order, so sort so that it's consistent across
873 // TUs to improve sharing.
874 SmallVector<const Module *> Submodules(M->submodules());
875 llvm::stable_sort(Range&: Submodules, C: [](const Module *A, const Module *B) {
876 return A->Name < B->Name;
877 });
878 for (const Module *SubM : Submodules)
879 F(SubM);
880}
881
882void ModuleDepCollectorPP::addAllSubmodulePrebuiltDeps(
883 const Module *M, ModuleDeps &MD,
884 llvm::DenseSet<const Module *> &SeenSubmodules) {
885 addModulePrebuiltDeps(M, MD, SeenSubmodules);
886
887 forEachSubmoduleSorted(M, F: [&](const Module *SubM) {
888 addAllSubmodulePrebuiltDeps(M: SubM, MD, SeenSubmodules);
889 });
890}
891
892void ModuleDepCollectorPP::addModulePrebuiltDeps(
893 const Module *M, ModuleDeps &MD,
894 llvm::DenseSet<const Module *> &SeenSubmodules) {
895 for (const Module *Import : M->Imports)
896 if (Import->getTopLevelModule() != M->getTopLevelModule())
897 if (MDC.isPrebuiltModule(M: Import->getTopLevelModule()))
898 if (SeenSubmodules.insert(V: Import->getTopLevelModule()).second) {
899 MD.PrebuiltModuleDeps.emplace_back(args: Import->getTopLevelModule());
900 if (MD.IsInStableDirectories) {
901 auto PrebuiltModulePropIt = MDC.PrebuiltModulesASTMap.find(
902 Key: MD.PrebuiltModuleDeps.back().PCMFile);
903 MD.IsInStableDirectories =
904 (PrebuiltModulePropIt != MDC.PrebuiltModulesASTMap.end()) &&
905 PrebuiltModulePropIt->second.isInStableDir();
906 }
907 }
908}
909
910void ModuleDepCollectorPP::addAllSubmoduleDeps(
911 const Module *M, ModuleDeps &MD,
912 llvm::DenseSet<const Module *> &AddedModules) {
913 addModuleDep(M, MD, AddedModules);
914
915 forEachSubmoduleSorted(M, F: [&](const Module *SubM) {
916 addAllSubmoduleDeps(M: SubM, MD, AddedModules);
917 });
918}
919
920void ModuleDepCollectorPP::addOneModuleDep(const Module *M, const ModuleID ID,
921 ModuleDeps &MD) {
922 MD.ClangModuleDeps.push_back(x: std::move(ID));
923 if (MD.IsInStableDirectories)
924 MD.IsInStableDirectories = MDC.ModularDeps[M]->IsInStableDirectories;
925}
926
927void ModuleDepCollectorPP::addModuleDep(
928 const Module *M, ModuleDeps &MD,
929 llvm::DenseSet<const Module *> &AddedModules) {
930 for (const Module *Import : M->Imports) {
931 if (Import->getTopLevelModule() != M->getTopLevelModule() &&
932 !MDC.isPrebuiltModule(M: Import)) {
933 if (auto ImportID = handleTopLevelModule(M: Import->getTopLevelModule()))
934 if (AddedModules.insert(V: Import->getTopLevelModule()).second)
935 addOneModuleDep(M: Import->getTopLevelModule(), ID: *ImportID, MD);
936 }
937 }
938}
939
940void ModuleDepCollectorPP::addAllAffectingClangModules(
941 const Module *M, ModuleDeps &MD,
942 llvm::DenseSet<const Module *> &AddedModules) {
943 addAffectingClangModule(M, MD, AddedModules);
944
945 for (const Module *SubM : M->submodules())
946 addAllAffectingClangModules(M: SubM, MD, AddedModules);
947}
948
949void ModuleDepCollectorPP::addAffectingClangModule(
950 const Module *M, ModuleDeps &MD,
951 llvm::DenseSet<const Module *> &AddedModules) {
952 for (const Module *Affecting : M->AffectingClangModules) {
953 assert(Affecting == Affecting->getTopLevelModule() &&
954 "Not quite import not top-level module");
955 if (Affecting != M->getTopLevelModule() &&
956 !MDC.isPrebuiltModule(M: Affecting)) {
957 if (auto ImportID = handleTopLevelModule(M: Affecting))
958 if (AddedModules.insert(V: Affecting).second)
959 addOneModuleDep(M: Affecting, ID: *ImportID, MD);
960 }
961 }
962}
963
964ModuleDepCollector::ModuleDepCollector(
965 DependencyScanningService &Service,
966 std::unique_ptr<DependencyOutputOptions> Opts,
967 CompilerInstance &ScanInstance, DependencyConsumer &C,
968 DependencyActionController &Controller, CompilerInvocation OriginalCI,
969 const PrebuiltModulesAttrsMap PrebuiltModulesASTMap,
970 const ArrayRef<StringRef> StableDirs)
971 : Service(Service), ScanInstance(ScanInstance), Consumer(C),
972 Controller(Controller),
973 PrebuiltModulesASTMap(std::move(PrebuiltModulesASTMap)),
974 StableDirs(StableDirs), Opts(std::move(Opts)),
975 CommonInvocation(
976 makeCommonInvocationForModuleBuild(CI: std::move(OriginalCI))) {}
977
978void ModuleDepCollector::attachToPreprocessor(Preprocessor &PP) {
979 PP.addPPCallbacks(C: std::make_unique<ModuleDepCollectorPP>(args&: *this));
980}
981
982void ModuleDepCollector::attachToASTReader(ASTReader &R) {}
983
984bool ModuleDepCollector::isPrebuiltModule(const Module *M) {
985 std::string Name(M->getTopLevelModuleName());
986 const auto &PrebuiltModuleFiles =
987 ScanInstance.getHeaderSearchOpts().PrebuiltModuleFiles;
988 auto PrebuiltModuleFileIt = PrebuiltModuleFiles.find(x: Name);
989 if (PrebuiltModuleFileIt == PrebuiltModuleFiles.end())
990 return false;
991 assert("Prebuilt module came from the expected AST file" &&
992 PrebuiltModuleFileIt->second == M->getASTFile()->getName());
993 return true;
994}
995
996static StringRef makeAbsoluteAndPreferred(CompilerInstance &CI, StringRef Path,
997 SmallVectorImpl<char> &Storage) {
998 if (llvm::sys::path::is_absolute(path: Path) &&
999 !llvm::sys::path::is_style_windows(S: llvm::sys::path::Style::native))
1000 return Path;
1001 Storage.assign(in_start: Path.begin(), in_end: Path.end());
1002 CI.getFileManager().makeAbsolutePath(Path&: Storage);
1003 llvm::sys::path::make_preferred(path&: Storage);
1004 return StringRef(Storage.data(), Storage.size());
1005}
1006
1007void ModuleDepCollector::addFileDep(StringRef Path) {
1008 if (Service.getFormat() == ScanningOutputFormat::P1689) {
1009 // Within P1689 format, we don't want all the paths to be absolute path
1010 // since it may violate the traditional make style dependencies info.
1011 FileDeps.emplace_back(args&: Path);
1012 return;
1013 }
1014
1015 llvm::SmallString<256> Storage;
1016 Path = makeAbsoluteAndPreferred(CI&: ScanInstance, Path, Storage);
1017 FileDeps.emplace_back(args&: Path);
1018}
1019
1020void ModuleDepCollector::addFileDep(ModuleDeps &MD, StringRef Path) {
1021 MD.FileDeps.emplace_back(args&: Path);
1022}
1023