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 if (MDC.Service.getFormat() == ScanningOutputFormat::P1689)
718 MDC.Consumer.handleProvidedAndRequiredStdCXXModules(
719 Provided: MDC.ProvidedStdCXXModule, Requires: MDC.RequiredStdCXXModules);
720
721 for (auto &&I : MDC.ModularDeps)
722 MDC.Consumer.handleModuleDependency(MD: *I.second);
723
724 for (const Module *M : MDC.DirectModularDeps) {
725 auto It = MDC.ModularDeps.find(Key: M);
726 // Only report direct dependencies that were successfully handled.
727 if (It != MDC.ModularDeps.end())
728 MDC.Consumer.handleDirectModuleDependency(MD: It->second->ID);
729 }
730
731 for (auto &&I : MDC.FileDeps)
732 MDC.Consumer.handleFileDependency(Filename: I);
733
734 for (auto &&I : MDC.DirectPrebuiltModularDeps)
735 MDC.Consumer.handlePrebuiltModuleDependency(PMD: I.second);
736}
737
738std::optional<ModuleID>
739ModuleDepCollectorPP::handleTopLevelModule(const Module *M) {
740 assert(M == M->getTopLevelModule() && "Expected top level module!");
741
742 // A top-level module might not be actually imported as a module when
743 // -fmodule-name is used to compile a translation unit that imports this
744 // module. In that case it can be skipped. The appropriate header
745 // dependencies will still be reported as expected.
746 if (!M->getASTFile())
747 return {};
748
749 // If this module has been handled already, just return its ID.
750 if (auto ModI = MDC.ModularDeps.find(Key: M); ModI != MDC.ModularDeps.end())
751 return ModI->second->ID;
752
753 auto OwnedMD = std::make_unique<ModuleDeps>();
754 ModuleDeps &MD = *OwnedMD;
755
756 MD.ID.ModuleName = M->getFullModuleName();
757 MD.IsSystem = M->IsSystem;
758
759 // Start off with the assumption that this module is shareable when there
760 // are stable directories. As more dependencies are discovered, check if those
761 // come from the provided directories.
762 MD.IsInStableDirectories = !MDC.StableDirs.empty();
763
764 // For modules which use export_as link name, the linked product that of the
765 // corresponding export_as-named module.
766 if (!M->UseExportAsModuleLinkName)
767 MD.LinkLibraries = M->LinkLibraries;
768
769 ModuleMap &ModMapInfo =
770 MDC.ScanInstance.getPreprocessor().getHeaderSearchInfo().getModuleMap();
771
772 if (auto ModuleMap = ModMapInfo.getModuleMapFileForUniquing(M)) {
773 SmallString<128> Path = ModuleMap->getNameAsRequested();
774 ModMapInfo.canonicalizeModuleMapPath(Path);
775 MD.ClangModuleMapFile = std::string(Path);
776 }
777
778 serialization::ModuleFile *MF =
779 MDC.ScanInstance.getASTReader()->getModuleManager().lookup(
780 File: *M->getASTFile());
781 MD.FileDepsBaseDir = MF->BaseDirectory;
782 MDC.ScanInstance.getASTReader()->visitInputFileInfos(
783 MF&: *MF, /*IncludeSystem=*/true,
784 Visitor: [&](const serialization::InputFileInfo &IFI, bool IsSystem) {
785 // The __inferred_module.map file is an insignificant implementation
786 // detail of implicitly-built modules. The PCM will also report the
787 // actual on-disk module map file that allowed inferring the module,
788 // which is what we need for building the module explicitly
789 // Let's ignore this file.
790 if (IFI.UnresolvedImportedFilename.ends_with(Suffix: "__inferred_module.map"))
791 return;
792 MDC.addFileDep(MD, Path: IFI.UnresolvedImportedFilename);
793 });
794
795 llvm::DenseSet<const Module *> SeenDeps;
796 addAllSubmodulePrebuiltDeps(M, MD, SeenSubmodules&: SeenDeps);
797 addAllSubmoduleDeps(M, MD, AddedModules&: SeenDeps);
798 addAllAffectingClangModules(M, MD, AddedModules&: SeenDeps);
799
800 SmallString<0> PathBuf;
801 PathBuf.reserve(N: 256);
802 MDC.ScanInstance.getASTReader()->visitInputFileInfos(
803 MF&: *MF, /*IncludeSystem=*/true,
804 Visitor: [&](const serialization::InputFileInfo &IFI, bool IsSystem) {
805 if (MD.IsInStableDirectories) {
806 auto FullFilePath = ASTReader::ResolveImportedPath(
807 Buf&: PathBuf, Path: IFI.UnresolvedImportedFilename, Prefix: MF->BaseDirectory);
808 MD.IsInStableDirectories =
809 isPathInStableDir(Directories: MDC.StableDirs, Input: *FullFilePath);
810 }
811 if (!(IFI.TopLevel && IFI.ModuleMap))
812 return;
813 if (IFI.UnresolvedImportedFilenameAsRequested.ends_with(
814 Suffix: "__inferred_module.map"))
815 return;
816 auto ResolvedFilenameAsRequested = ASTReader::ResolveImportedPath(
817 Buf&: PathBuf, Path: IFI.UnresolvedImportedFilenameAsRequested,
818 Prefix: MF->BaseDirectory);
819 MD.ModuleMapFileDeps.emplace_back(args: *ResolvedFilenameAsRequested);
820 });
821
822 bool IgnoreCWD = false;
823 CowCompilerInvocation CI =
824 MDC.getInvocationAdjustedForModuleBuildWithoutOutputs(
825 Deps: MD, Optimize: [&](CowCompilerInvocation &BuildInvocation) {
826 if (any(Val: MDC.Service.getOptimizeArgs() &
827 (ScanningOptimizations::HeaderSearch |
828 ScanningOptimizations::VFS)))
829 optimizeHeaderSearchOpts(Opts&: BuildInvocation.getMutHeaderSearchOpts(),
830 Reader&: *MDC.ScanInstance.getASTReader(), MF: *MF,
831 PrebuiltModulesASTMap: MDC.PrebuiltModulesASTMap,
832 OptimizeArgs: MDC.Service.getOptimizeArgs());
833
834 if (any(Val: MDC.Service.getOptimizeArgs() &
835 ScanningOptimizations::SystemWarnings))
836 optimizeDiagnosticOpts(
837 Opts&: BuildInvocation.getMutDiagnosticOpts(),
838 IsSystemModule: BuildInvocation.getFrontendOpts().IsSystemModule);
839
840 IgnoreCWD = any(Val: MDC.Service.getOptimizeArgs() &
841 ScanningOptimizations::IgnoreCWD) &&
842 isSafeToIgnoreCWD(CI: BuildInvocation);
843 if (IgnoreCWD) {
844 llvm::ErrorOr<std::string> CWD =
845 MDC.ScanInstance.getVirtualFileSystem()
846 .getCurrentWorkingDirectory();
847 if (CWD)
848 optimizeCWD(BuildInvocation, CWD: *CWD);
849 }
850 });
851
852 // Check provided input paths from the invocation for determining
853 // IsInStableDirectories.
854 if (MD.IsInStableDirectories)
855 MD.IsInStableDirectories =
856 areOptionsInStableDir(Directories: MDC.StableDirs, HSOpts: CI.getHeaderSearchOpts());
857
858 MDC.associateWithContextHash(CI, IgnoreCWD, Deps&: MD);
859
860 // Finish the compiler invocation. Requires dependencies and the context hash.
861 MDC.addOutputPaths(CI, Deps&: MD);
862
863 MD.BuildInfo = std::move(CI);
864
865 MDC.ModularDeps.insert(KV: {M, std::move(OwnedMD)});
866
867 return MD.ID;
868}
869
870static void forEachSubmoduleSorted(const Module *M,
871 llvm::function_ref<void(const Module *)> F) {
872 // Submodule order depends on order of header includes for inferred submodules
873 // we don't care about the exact order, so sort so that it's consistent across
874 // TUs to improve sharing.
875 SmallVector<const Module *> Submodules(M->submodules());
876 llvm::stable_sort(Range&: Submodules, C: [](const Module *A, const Module *B) {
877 return A->Name < B->Name;
878 });
879 for (const Module *SubM : Submodules)
880 F(SubM);
881}
882
883void ModuleDepCollectorPP::addAllSubmodulePrebuiltDeps(
884 const Module *M, ModuleDeps &MD,
885 llvm::DenseSet<const Module *> &SeenSubmodules) {
886 addModulePrebuiltDeps(M, MD, SeenSubmodules);
887
888 forEachSubmoduleSorted(M, F: [&](const Module *SubM) {
889 addAllSubmodulePrebuiltDeps(M: SubM, MD, SeenSubmodules);
890 });
891}
892
893void ModuleDepCollectorPP::addModulePrebuiltDeps(
894 const Module *M, ModuleDeps &MD,
895 llvm::DenseSet<const Module *> &SeenSubmodules) {
896 for (const Module *Import : M->Imports)
897 if (Import->getTopLevelModule() != M->getTopLevelModule())
898 if (MDC.isPrebuiltModule(M: Import->getTopLevelModule()))
899 if (SeenSubmodules.insert(V: Import->getTopLevelModule()).second) {
900 MD.PrebuiltModuleDeps.emplace_back(args: Import->getTopLevelModule());
901 if (MD.IsInStableDirectories) {
902 auto PrebuiltModulePropIt = MDC.PrebuiltModulesASTMap.find(
903 Key: MD.PrebuiltModuleDeps.back().PCMFile);
904 MD.IsInStableDirectories =
905 (PrebuiltModulePropIt != MDC.PrebuiltModulesASTMap.end()) &&
906 PrebuiltModulePropIt->second.isInStableDir();
907 }
908 }
909}
910
911void ModuleDepCollectorPP::addAllSubmoduleDeps(
912 const Module *M, ModuleDeps &MD,
913 llvm::DenseSet<const Module *> &AddedModules) {
914 addModuleDep(M, MD, AddedModules);
915
916 forEachSubmoduleSorted(M, F: [&](const Module *SubM) {
917 addAllSubmoduleDeps(M: SubM, MD, AddedModules);
918 });
919}
920
921void ModuleDepCollectorPP::addOneModuleDep(const Module *M, const ModuleID ID,
922 ModuleDeps &MD) {
923 MD.ClangModuleDeps.push_back(x: std::move(ID));
924 if (MD.IsInStableDirectories)
925 MD.IsInStableDirectories = MDC.ModularDeps[M]->IsInStableDirectories;
926}
927
928void ModuleDepCollectorPP::addModuleDep(
929 const Module *M, ModuleDeps &MD,
930 llvm::DenseSet<const Module *> &AddedModules) {
931 for (const Module *Import : M->Imports) {
932 if (Import->getTopLevelModule() != M->getTopLevelModule() &&
933 !MDC.isPrebuiltModule(M: Import)) {
934 if (auto ImportID = handleTopLevelModule(M: Import->getTopLevelModule()))
935 if (AddedModules.insert(V: Import->getTopLevelModule()).second)
936 addOneModuleDep(M: Import->getTopLevelModule(), ID: *ImportID, MD);
937 }
938 }
939}
940
941void ModuleDepCollectorPP::addAllAffectingClangModules(
942 const Module *M, ModuleDeps &MD,
943 llvm::DenseSet<const Module *> &AddedModules) {
944 addAffectingClangModule(M, MD, AddedModules);
945
946 for (const Module *SubM : M->submodules())
947 addAllAffectingClangModules(M: SubM, MD, AddedModules);
948}
949
950void ModuleDepCollectorPP::addAffectingClangModule(
951 const Module *M, ModuleDeps &MD,
952 llvm::DenseSet<const Module *> &AddedModules) {
953 for (const Module *Affecting : M->AffectingClangModules) {
954 assert(Affecting == Affecting->getTopLevelModule() &&
955 "Not quite import not top-level module");
956 if (Affecting != M->getTopLevelModule() &&
957 !MDC.isPrebuiltModule(M: Affecting)) {
958 if (auto ImportID = handleTopLevelModule(M: Affecting))
959 if (AddedModules.insert(V: Affecting).second)
960 addOneModuleDep(M: Affecting, ID: *ImportID, MD);
961 }
962 }
963}
964
965ModuleDepCollector::ModuleDepCollector(
966 DependencyScanningService &Service,
967 std::unique_ptr<DependencyOutputOptions> Opts,
968 CompilerInstance &ScanInstance, DependencyConsumer &C,
969 DependencyActionController &Controller, CompilerInvocation OriginalCI,
970 const PrebuiltModulesAttrsMap PrebuiltModulesASTMap,
971 const ArrayRef<StringRef> StableDirs)
972 : Service(Service), ScanInstance(ScanInstance), Consumer(C),
973 Controller(Controller),
974 PrebuiltModulesASTMap(std::move(PrebuiltModulesASTMap)),
975 StableDirs(StableDirs), Opts(std::move(Opts)),
976 CommonInvocation(
977 makeCommonInvocationForModuleBuild(CI: std::move(OriginalCI))) {}
978
979void ModuleDepCollector::attachToPreprocessor(Preprocessor &PP) {
980 PP.addPPCallbacks(C: std::make_unique<ModuleDepCollectorPP>(args&: *this));
981}
982
983void ModuleDepCollector::attachToASTReader(ASTReader &R) {}
984
985bool ModuleDepCollector::isPrebuiltModule(const Module *M) {
986 std::string Name(M->getTopLevelModuleName());
987 const auto &PrebuiltModuleFiles =
988 ScanInstance.getHeaderSearchOpts().PrebuiltModuleFiles;
989 auto PrebuiltModuleFileIt = PrebuiltModuleFiles.find(x: Name);
990 if (PrebuiltModuleFileIt == PrebuiltModuleFiles.end())
991 return false;
992 assert("Prebuilt module came from the expected AST file" &&
993 PrebuiltModuleFileIt->second == M->getASTFile()->getName());
994 return true;
995}
996
997static StringRef makeAbsoluteAndPreferred(CompilerInstance &CI, StringRef Path,
998 SmallVectorImpl<char> &Storage) {
999 if (llvm::sys::path::is_absolute(path: Path) &&
1000 !llvm::sys::path::is_style_windows(S: llvm::sys::path::Style::native))
1001 return Path;
1002 Storage.assign(in_start: Path.begin(), in_end: Path.end());
1003 CI.getFileManager().makeAbsolutePath(Path&: Storage);
1004 llvm::sys::path::make_preferred(path&: Storage);
1005 return StringRef(Storage.data(), Storage.size());
1006}
1007
1008void ModuleDepCollector::addFileDep(StringRef Path) {
1009 if (Service.getFormat() == ScanningOutputFormat::P1689) {
1010 // Within P1689 format, we don't want all the paths to be absolute path
1011 // since it may violate the traditional make style dependencies info.
1012 FileDeps.emplace_back(args&: Path);
1013 return;
1014 }
1015
1016 llvm::SmallString<256> Storage;
1017 Path = makeAbsoluteAndPreferred(CI&: ScanInstance, Path, Storage);
1018 FileDeps.emplace_back(args&: Path);
1019}
1020
1021void ModuleDepCollector::addFileDep(ModuleDeps &MD, StringRef Path) {
1022 MD.FileDeps.emplace_back(args&: Path);
1023}
1024

Provided by KDAB

Privacy Policy
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more

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