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