Path: blob/main/contrib/llvm-project/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp
35266 views
//===- ModuleDepCollector.cpp - Callbacks to collect deps -------*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "clang/Tooling/DependencyScanning/ModuleDepCollector.h"910#include "clang/Basic/MakeSupport.h"11#include "clang/Frontend/CompilerInstance.h"12#include "clang/Lex/Preprocessor.h"13#include "clang/Tooling/DependencyScanning/DependencyScanningWorker.h"14#include "llvm/ADT/STLExtras.h"15#include "llvm/Support/BLAKE3.h"16#include "llvm/Support/StringSaver.h"17#include <optional>1819using namespace clang;20using namespace tooling;21using namespace dependencies;2223const std::vector<std::string> &ModuleDeps::getBuildArguments() {24assert(!std::holds_alternative<std::monostate>(BuildInfo) &&25"Using uninitialized ModuleDeps");26if (const auto *CI = std::get_if<CowCompilerInvocation>(&BuildInfo))27BuildInfo = CI->getCC1CommandLine();28return std::get<std::vector<std::string>>(BuildInfo);29}3031static void32optimizeHeaderSearchOpts(HeaderSearchOptions &Opts, ASTReader &Reader,33const serialization::ModuleFile &MF,34const PrebuiltModuleVFSMapT &PrebuiltModuleVFSMap,35ScanningOptimizations OptimizeArgs) {36if (any(OptimizeArgs & ScanningOptimizations::HeaderSearch)) {37// Only preserve search paths that were used during the dependency scan.38std::vector<HeaderSearchOptions::Entry> Entries;39std::swap(Opts.UserEntries, Entries);4041llvm::BitVector SearchPathUsage(Entries.size());42llvm::DenseSet<const serialization::ModuleFile *> Visited;43std::function<void(const serialization::ModuleFile *)> VisitMF =44[&](const serialization::ModuleFile *MF) {45SearchPathUsage |= MF->SearchPathUsage;46Visited.insert(MF);47for (const serialization::ModuleFile *Import : MF->Imports)48if (!Visited.contains(Import))49VisitMF(Import);50};51VisitMF(&MF);5253if (SearchPathUsage.size() != Entries.size())54llvm::report_fatal_error(55"Inconsistent search path options between modules detected");5657for (auto Idx : SearchPathUsage.set_bits())58Opts.UserEntries.push_back(std::move(Entries[Idx]));59}60if (any(OptimizeArgs & ScanningOptimizations::VFS)) {61std::vector<std::string> VFSOverlayFiles;62std::swap(Opts.VFSOverlayFiles, VFSOverlayFiles);6364llvm::BitVector VFSUsage(VFSOverlayFiles.size());65llvm::DenseSet<const serialization::ModuleFile *> Visited;66std::function<void(const serialization::ModuleFile *)> VisitMF =67[&](const serialization::ModuleFile *MF) {68Visited.insert(MF);69if (MF->Kind == serialization::MK_ImplicitModule) {70VFSUsage |= MF->VFSUsage;71// We only need to recurse into implicit modules. Other module types72// will have the correct set of VFSs for anything they depend on.73for (const serialization::ModuleFile *Import : MF->Imports)74if (!Visited.contains(Import))75VisitMF(Import);76} else {77// This is not an implicitly built module, so it may have different78// VFS options. Fall back to a string comparison instead.79auto VFSMap = PrebuiltModuleVFSMap.find(MF->FileName);80if (VFSMap == PrebuiltModuleVFSMap.end())81return;82for (std::size_t I = 0, E = VFSOverlayFiles.size(); I != E; ++I) {83if (VFSMap->second.contains(VFSOverlayFiles[I]))84VFSUsage[I] = true;85}86}87};88VisitMF(&MF);8990if (VFSUsage.size() != VFSOverlayFiles.size())91llvm::report_fatal_error(92"Inconsistent -ivfsoverlay options between modules detected");9394for (auto Idx : VFSUsage.set_bits())95Opts.VFSOverlayFiles.push_back(std::move(VFSOverlayFiles[Idx]));96}97}9899static void optimizeDiagnosticOpts(DiagnosticOptions &Opts,100bool IsSystemModule) {101// If this is not a system module or -Wsystem-headers was passed, don't102// optimize.103if (!IsSystemModule)104return;105bool Wsystem_headers = false;106for (StringRef Opt : Opts.Warnings) {107bool isPositive = !Opt.consume_front("no-");108if (Opt == "system-headers")109Wsystem_headers = isPositive;110}111if (Wsystem_headers)112return;113114// Remove all warning flags. System modules suppress most, but not all,115// warnings.116Opts.Warnings.clear();117Opts.UndefPrefixes.clear();118Opts.Remarks.clear();119}120121static std::vector<std::string> splitString(std::string S, char Separator) {122SmallVector<StringRef> Segments;123StringRef(S).split(Segments, Separator, /*MaxSplit=*/-1, /*KeepEmpty=*/false);124std::vector<std::string> Result;125Result.reserve(Segments.size());126for (StringRef Segment : Segments)127Result.push_back(Segment.str());128return Result;129}130131void ModuleDepCollector::addOutputPaths(CowCompilerInvocation &CI,132ModuleDeps &Deps) {133CI.getMutFrontendOpts().OutputFile =134Controller.lookupModuleOutput(Deps.ID, ModuleOutputKind::ModuleFile);135if (!CI.getDiagnosticOpts().DiagnosticSerializationFile.empty())136CI.getMutDiagnosticOpts().DiagnosticSerializationFile =137Controller.lookupModuleOutput(138Deps.ID, ModuleOutputKind::DiagnosticSerializationFile);139if (!CI.getDependencyOutputOpts().OutputFile.empty()) {140CI.getMutDependencyOutputOpts().OutputFile = Controller.lookupModuleOutput(141Deps.ID, ModuleOutputKind::DependencyFile);142CI.getMutDependencyOutputOpts().Targets =143splitString(Controller.lookupModuleOutput(144Deps.ID, ModuleOutputKind::DependencyTargets),145'\0');146if (!CI.getDependencyOutputOpts().OutputFile.empty() &&147CI.getDependencyOutputOpts().Targets.empty()) {148// Fallback to -o as dependency target, as in the driver.149SmallString<128> Target;150quoteMakeTarget(CI.getFrontendOpts().OutputFile, Target);151CI.getMutDependencyOutputOpts().Targets.push_back(std::string(Target));152}153}154}155156void dependencies::resetBenignCodeGenOptions(frontend::ActionKind ProgramAction,157const LangOptions &LangOpts,158CodeGenOptions &CGOpts) {159// TODO: Figure out better way to set options to their default value.160if (ProgramAction == frontend::GenerateModule) {161CGOpts.MainFileName.clear();162CGOpts.DwarfDebugFlags.clear();163}164if (ProgramAction == frontend::GeneratePCH ||165(ProgramAction == frontend::GenerateModule && !LangOpts.ModulesCodegen)) {166CGOpts.DebugCompilationDir.clear();167CGOpts.CoverageCompilationDir.clear();168CGOpts.CoverageDataFile.clear();169CGOpts.CoverageNotesFile.clear();170CGOpts.ProfileInstrumentUsePath.clear();171CGOpts.SampleProfileFile.clear();172CGOpts.ProfileRemappingFile.clear();173}174}175176static CowCompilerInvocation177makeCommonInvocationForModuleBuild(CompilerInvocation CI) {178CI.resetNonModularOptions();179CI.clearImplicitModuleBuildOptions();180181// The scanner takes care to avoid passing non-affecting module maps to the182// explicit compiles. No need to do extra work just to find out there are no183// module map files to prune.184CI.getHeaderSearchOpts().ModulesPruneNonAffectingModuleMaps = false;185186// Remove options incompatible with explicit module build or are likely to187// differ between identical modules discovered from different translation188// units.189CI.getFrontendOpts().Inputs.clear();190CI.getFrontendOpts().OutputFile.clear();191// LLVM options are not going to affect the AST192CI.getFrontendOpts().LLVMArgs.clear();193194resetBenignCodeGenOptions(frontend::GenerateModule, CI.getLangOpts(),195CI.getCodeGenOpts());196197// Map output paths that affect behaviour to "-" so their existence is in the198// context hash. The final path will be computed in addOutputPaths.199if (!CI.getDiagnosticOpts().DiagnosticSerializationFile.empty())200CI.getDiagnosticOpts().DiagnosticSerializationFile = "-";201if (!CI.getDependencyOutputOpts().OutputFile.empty())202CI.getDependencyOutputOpts().OutputFile = "-";203CI.getDependencyOutputOpts().Targets.clear();204205CI.getFrontendOpts().ProgramAction = frontend::GenerateModule;206CI.getFrontendOpts().ARCMTAction = FrontendOptions::ARCMT_None;207CI.getFrontendOpts().ObjCMTAction = FrontendOptions::ObjCMT_None;208CI.getFrontendOpts().MTMigrateDir.clear();209CI.getLangOpts().ModuleName.clear();210211// Remove any macro definitions that are explicitly ignored.212if (!CI.getHeaderSearchOpts().ModulesIgnoreMacros.empty()) {213llvm::erase_if(214CI.getPreprocessorOpts().Macros,215[&CI](const std::pair<std::string, bool> &Def) {216StringRef MacroDef = Def.first;217return CI.getHeaderSearchOpts().ModulesIgnoreMacros.contains(218llvm::CachedHashString(MacroDef.split('=').first));219});220// Remove the now unused option.221CI.getHeaderSearchOpts().ModulesIgnoreMacros.clear();222}223224return CI;225}226227CowCompilerInvocation228ModuleDepCollector::getInvocationAdjustedForModuleBuildWithoutOutputs(229const ModuleDeps &Deps,230llvm::function_ref<void(CowCompilerInvocation &)> Optimize) const {231CowCompilerInvocation CI = CommonInvocation;232233CI.getMutLangOpts().ModuleName = Deps.ID.ModuleName;234CI.getMutFrontendOpts().IsSystemModule = Deps.IsSystem;235236// Inputs237InputKind ModuleMapInputKind(CI.getFrontendOpts().DashX.getLanguage(),238InputKind::Format::ModuleMap);239CI.getMutFrontendOpts().Inputs.emplace_back(Deps.ClangModuleMapFile,240ModuleMapInputKind);241242auto CurrentModuleMapEntry =243ScanInstance.getFileManager().getFile(Deps.ClangModuleMapFile);244assert(CurrentModuleMapEntry && "module map file entry not found");245246// Remove directly passed modulemap files. They will get added back if they247// were actually used.248CI.getMutFrontendOpts().ModuleMapFiles.clear();249250auto DepModuleMapFiles = collectModuleMapFiles(Deps.ClangModuleDeps);251for (StringRef ModuleMapFile : Deps.ModuleMapFileDeps) {252// TODO: Track these as `FileEntryRef` to simplify the equality check below.253auto ModuleMapEntry = ScanInstance.getFileManager().getFile(ModuleMapFile);254assert(ModuleMapEntry && "module map file entry not found");255256// Don't report module maps describing eagerly-loaded dependency. This257// information will be deserialized from the PCM.258// TODO: Verify this works fine when modulemap for module A is eagerly259// loaded from A.pcm, and module map passed on the command line contains260// definition of a submodule: "explicit module A.Private { ... }".261if (EagerLoadModules && DepModuleMapFiles.contains(*ModuleMapEntry))262continue;263264// Don't report module map file of the current module unless it also265// describes a dependency (for symmetry).266if (*ModuleMapEntry == *CurrentModuleMapEntry &&267!DepModuleMapFiles.contains(*ModuleMapEntry))268continue;269270CI.getMutFrontendOpts().ModuleMapFiles.emplace_back(ModuleMapFile);271}272273// Report the prebuilt modules this module uses.274for (const auto &PrebuiltModule : Deps.PrebuiltModuleDeps)275CI.getMutFrontendOpts().ModuleFiles.push_back(PrebuiltModule.PCMFile);276277// Add module file inputs from dependencies.278addModuleFiles(CI, Deps.ClangModuleDeps);279280if (!CI.getDiagnosticOpts().SystemHeaderWarningsModules.empty()) {281// Apply -Wsystem-headers-in-module for the current module.282if (llvm::is_contained(CI.getDiagnosticOpts().SystemHeaderWarningsModules,283Deps.ID.ModuleName))284CI.getMutDiagnosticOpts().Warnings.push_back("system-headers");285// Remove the now unused option(s).286CI.getMutDiagnosticOpts().SystemHeaderWarningsModules.clear();287}288289Optimize(CI);290291return CI;292}293294llvm::DenseSet<const FileEntry *> ModuleDepCollector::collectModuleMapFiles(295ArrayRef<ModuleID> ClangModuleDeps) const {296llvm::DenseSet<const FileEntry *> ModuleMapFiles;297for (const ModuleID &MID : ClangModuleDeps) {298ModuleDeps *MD = ModuleDepsByID.lookup(MID);299assert(MD && "Inconsistent dependency info");300// TODO: Track ClangModuleMapFile as `FileEntryRef`.301auto FE = ScanInstance.getFileManager().getFile(MD->ClangModuleMapFile);302assert(FE && "Missing module map file that was previously found");303ModuleMapFiles.insert(*FE);304}305return ModuleMapFiles;306}307308void ModuleDepCollector::addModuleMapFiles(309CompilerInvocation &CI, ArrayRef<ModuleID> ClangModuleDeps) const {310if (EagerLoadModules)311return; // Only pcm is needed for eager load.312313for (const ModuleID &MID : ClangModuleDeps) {314ModuleDeps *MD = ModuleDepsByID.lookup(MID);315assert(MD && "Inconsistent dependency info");316CI.getFrontendOpts().ModuleMapFiles.push_back(MD->ClangModuleMapFile);317}318}319320void ModuleDepCollector::addModuleFiles(321CompilerInvocation &CI, ArrayRef<ModuleID> ClangModuleDeps) const {322for (const ModuleID &MID : ClangModuleDeps) {323std::string PCMPath =324Controller.lookupModuleOutput(MID, ModuleOutputKind::ModuleFile);325if (EagerLoadModules)326CI.getFrontendOpts().ModuleFiles.push_back(std::move(PCMPath));327else328CI.getHeaderSearchOpts().PrebuiltModuleFiles.insert(329{MID.ModuleName, std::move(PCMPath)});330}331}332333void ModuleDepCollector::addModuleFiles(334CowCompilerInvocation &CI, ArrayRef<ModuleID> ClangModuleDeps) const {335for (const ModuleID &MID : ClangModuleDeps) {336std::string PCMPath =337Controller.lookupModuleOutput(MID, ModuleOutputKind::ModuleFile);338if (EagerLoadModules)339CI.getMutFrontendOpts().ModuleFiles.push_back(std::move(PCMPath));340else341CI.getMutHeaderSearchOpts().PrebuiltModuleFiles.insert(342{MID.ModuleName, std::move(PCMPath)});343}344}345346static bool needsModules(FrontendInputFile FIF) {347switch (FIF.getKind().getLanguage()) {348case Language::Unknown:349case Language::Asm:350case Language::LLVM_IR:351return false;352default:353return true;354}355}356357void ModuleDepCollector::applyDiscoveredDependencies(CompilerInvocation &CI) {358CI.clearImplicitModuleBuildOptions();359resetBenignCodeGenOptions(CI.getFrontendOpts().ProgramAction,360CI.getLangOpts(), CI.getCodeGenOpts());361362if (llvm::any_of(CI.getFrontendOpts().Inputs, needsModules)) {363Preprocessor &PP = ScanInstance.getPreprocessor();364if (Module *CurrentModule = PP.getCurrentModuleImplementation())365if (OptionalFileEntryRef CurrentModuleMap =366PP.getHeaderSearchInfo()367.getModuleMap()368.getModuleMapFileForUniquing(CurrentModule))369CI.getFrontendOpts().ModuleMapFiles.emplace_back(370CurrentModuleMap->getNameAsRequested());371372SmallVector<ModuleID> DirectDeps;373for (const auto &KV : ModularDeps)374if (DirectModularDeps.contains(KV.first))375DirectDeps.push_back(KV.second->ID);376377// TODO: Report module maps the same way it's done for modular dependencies.378addModuleMapFiles(CI, DirectDeps);379380addModuleFiles(CI, DirectDeps);381382for (const auto &KV : DirectPrebuiltModularDeps)383CI.getFrontendOpts().ModuleFiles.push_back(KV.second.PCMFile);384}385}386387static std::string getModuleContextHash(const ModuleDeps &MD,388const CowCompilerInvocation &CI,389bool EagerLoadModules,390llvm::vfs::FileSystem &VFS) {391llvm::HashBuilder<llvm::TruncatedBLAKE3<16>, llvm::endianness::native>392HashBuilder;393SmallString<32> Scratch;394395// Hash the compiler version and serialization version to ensure the module396// will be readable.397HashBuilder.add(getClangFullRepositoryVersion());398HashBuilder.add(serialization::VERSION_MAJOR, serialization::VERSION_MINOR);399llvm::ErrorOr<std::string> CWD = VFS.getCurrentWorkingDirectory();400if (CWD)401HashBuilder.add(*CWD);402403// Hash the BuildInvocation without any input files.404SmallString<0> ArgVec;405ArgVec.reserve(4096);406CI.generateCC1CommandLine([&](const Twine &Arg) {407Arg.toVector(ArgVec);408ArgVec.push_back('\0');409});410HashBuilder.add(ArgVec);411412// Hash the module dependencies. These paths may differ even if the invocation413// is identical if they depend on the contents of the files in the TU -- for414// example, case-insensitive paths to modulemap files. Usually such a case415// would indicate a missed optimization to canonicalize, but it may be416// difficult to canonicalize all cases when there is a VFS.417for (const auto &ID : MD.ClangModuleDeps) {418HashBuilder.add(ID.ModuleName);419HashBuilder.add(ID.ContextHash);420}421422HashBuilder.add(EagerLoadModules);423424llvm::BLAKE3Result<16> Hash = HashBuilder.final();425std::array<uint64_t, 2> Words;426static_assert(sizeof(Hash) == sizeof(Words), "Hash must match Words");427std::memcpy(Words.data(), Hash.data(), sizeof(Hash));428return toString(llvm::APInt(sizeof(Words) * 8, Words), 36, /*Signed=*/false);429}430431void ModuleDepCollector::associateWithContextHash(432const CowCompilerInvocation &CI, ModuleDeps &Deps) {433Deps.ID.ContextHash = getModuleContextHash(434Deps, CI, EagerLoadModules, ScanInstance.getVirtualFileSystem());435bool Inserted = ModuleDepsByID.insert({Deps.ID, &Deps}).second;436(void)Inserted;437assert(Inserted && "duplicate module mapping");438}439440void ModuleDepCollectorPP::LexedFileChanged(FileID FID,441LexedFileChangeReason Reason,442SrcMgr::CharacteristicKind FileType,443FileID PrevFID,444SourceLocation Loc) {445if (Reason != LexedFileChangeReason::EnterFile)446return;447448// This has to be delayed as the context hash can change at the start of449// `CompilerInstance::ExecuteAction`.450if (MDC.ContextHash.empty()) {451MDC.ContextHash = MDC.ScanInstance.getInvocation().getModuleHash();452MDC.Consumer.handleContextHash(MDC.ContextHash);453}454455SourceManager &SM = MDC.ScanInstance.getSourceManager();456457// Dependency generation really does want to go all the way to the458// file entry for a source location to find out what is depended on.459// We do not want #line markers to affect dependency generation!460if (std::optional<StringRef> Filename = SM.getNonBuiltinFilenameForID(FID))461MDC.addFileDep(llvm::sys::path::remove_leading_dotslash(*Filename));462}463464void ModuleDepCollectorPP::InclusionDirective(465SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName,466bool IsAngled, CharSourceRange FilenameRange, OptionalFileEntryRef File,467StringRef SearchPath, StringRef RelativePath, const Module *SuggestedModule,468bool ModuleImported, SrcMgr::CharacteristicKind FileType) {469if (!File && !ModuleImported) {470// This is a non-modular include that HeaderSearch failed to find. Add it471// here as `FileChanged` will never see it.472MDC.addFileDep(FileName);473}474handleImport(SuggestedModule);475}476477void ModuleDepCollectorPP::moduleImport(SourceLocation ImportLoc,478ModuleIdPath Path,479const Module *Imported) {480if (MDC.ScanInstance.getPreprocessor().isInImportingCXXNamedModules()) {481P1689ModuleInfo RequiredModule;482RequiredModule.ModuleName = Path[0].first->getName().str();483RequiredModule.Type = P1689ModuleInfo::ModuleType::NamedCXXModule;484MDC.RequiredStdCXXModules.push_back(RequiredModule);485return;486}487488handleImport(Imported);489}490491void ModuleDepCollectorPP::handleImport(const Module *Imported) {492if (!Imported)493return;494495const Module *TopLevelModule = Imported->getTopLevelModule();496497if (MDC.isPrebuiltModule(TopLevelModule))498MDC.DirectPrebuiltModularDeps.insert(499{TopLevelModule, PrebuiltModuleDep{TopLevelModule}});500else501MDC.DirectModularDeps.insert(TopLevelModule);502}503504void ModuleDepCollectorPP::EndOfMainFile() {505FileID MainFileID = MDC.ScanInstance.getSourceManager().getMainFileID();506MDC.MainFile = std::string(MDC.ScanInstance.getSourceManager()507.getFileEntryRefForID(MainFileID)508->getName());509510auto &PP = MDC.ScanInstance.getPreprocessor();511if (PP.isInNamedModule()) {512P1689ModuleInfo ProvidedModule;513ProvidedModule.ModuleName = PP.getNamedModuleName();514ProvidedModule.Type = P1689ModuleInfo::ModuleType::NamedCXXModule;515ProvidedModule.IsStdCXXModuleInterface = PP.isInNamedInterfaceUnit();516// Don't put implementation (non partition) unit as Provide.517// Put the module as required instead. Since the implementation518// unit will import the primary module implicitly.519if (PP.isInImplementationUnit())520MDC.RequiredStdCXXModules.push_back(ProvidedModule);521else522MDC.ProvidedStdCXXModule = ProvidedModule;523}524525if (!MDC.ScanInstance.getPreprocessorOpts().ImplicitPCHInclude.empty())526MDC.addFileDep(MDC.ScanInstance.getPreprocessorOpts().ImplicitPCHInclude);527528for (const Module *M :529MDC.ScanInstance.getPreprocessor().getAffectingClangModules())530if (!MDC.isPrebuiltModule(M))531MDC.DirectModularDeps.insert(M);532533for (const Module *M : MDC.DirectModularDeps)534handleTopLevelModule(M);535536MDC.Consumer.handleDependencyOutputOpts(*MDC.Opts);537538if (MDC.IsStdModuleP1689Format)539MDC.Consumer.handleProvidedAndRequiredStdCXXModules(540MDC.ProvidedStdCXXModule, MDC.RequiredStdCXXModules);541542for (auto &&I : MDC.ModularDeps)543MDC.Consumer.handleModuleDependency(*I.second);544545for (const Module *M : MDC.DirectModularDeps) {546auto It = MDC.ModularDeps.find(M);547// Only report direct dependencies that were successfully handled.548if (It != MDC.ModularDeps.end())549MDC.Consumer.handleDirectModuleDependency(MDC.ModularDeps[M]->ID);550}551552for (auto &&I : MDC.FileDeps)553MDC.Consumer.handleFileDependency(I);554555for (auto &&I : MDC.DirectPrebuiltModularDeps)556MDC.Consumer.handlePrebuiltModuleDependency(I.second);557}558559std::optional<ModuleID>560ModuleDepCollectorPP::handleTopLevelModule(const Module *M) {561assert(M == M->getTopLevelModule() && "Expected top level module!");562563// A top-level module might not be actually imported as a module when564// -fmodule-name is used to compile a translation unit that imports this565// module. In that case it can be skipped. The appropriate header566// dependencies will still be reported as expected.567if (!M->getASTFile())568return {};569570// If this module has been handled already, just return its ID.571auto ModI = MDC.ModularDeps.insert({M, nullptr});572if (!ModI.second)573return ModI.first->second->ID;574575ModI.first->second = std::make_unique<ModuleDeps>();576ModuleDeps &MD = *ModI.first->second;577578MD.ID.ModuleName = M->getFullModuleName();579MD.IsSystem = M->IsSystem;580// For modules which use export_as link name, the linked product that of the581// corresponding export_as-named module.582if (!M->UseExportAsModuleLinkName)583MD.LinkLibraries = M->LinkLibraries;584585ModuleMap &ModMapInfo =586MDC.ScanInstance.getPreprocessor().getHeaderSearchInfo().getModuleMap();587588OptionalFileEntryRef ModuleMap = ModMapInfo.getModuleMapFileForUniquing(M);589590if (ModuleMap) {591SmallString<128> Path = ModuleMap->getNameAsRequested();592ModMapInfo.canonicalizeModuleMapPath(Path);593MD.ClangModuleMapFile = std::string(Path);594}595596serialization::ModuleFile *MF =597MDC.ScanInstance.getASTReader()->getModuleManager().lookup(598*M->getASTFile());599MDC.ScanInstance.getASTReader()->visitInputFileInfos(600*MF, /*IncludeSystem=*/true,601[&](const serialization::InputFileInfo &IFI, bool IsSystem) {602// __inferred_module.map is the result of the way in which an implicit603// module build handles inferred modules. It adds an overlay VFS with604// this file in the proper directory and relies on the rest of Clang to605// handle it like normal. With explicitly built modules we don't need606// to play VFS tricks, so replace it with the correct module map.607if (StringRef(IFI.Filename).ends_with("__inferred_module.map")) {608MDC.addFileDep(MD, ModuleMap->getName());609return;610}611MDC.addFileDep(MD, IFI.Filename);612});613614llvm::DenseSet<const Module *> SeenDeps;615addAllSubmodulePrebuiltDeps(M, MD, SeenDeps);616addAllSubmoduleDeps(M, MD, SeenDeps);617addAllAffectingClangModules(M, MD, SeenDeps);618619MDC.ScanInstance.getASTReader()->visitInputFileInfos(620*MF, /*IncludeSystem=*/true,621[&](const serialization::InputFileInfo &IFI, bool IsSystem) {622if (!(IFI.TopLevel && IFI.ModuleMap))623return;624if (StringRef(IFI.FilenameAsRequested)625.ends_with("__inferred_module.map"))626return;627MD.ModuleMapFileDeps.emplace_back(IFI.FilenameAsRequested);628});629630CowCompilerInvocation CI =631MDC.getInvocationAdjustedForModuleBuildWithoutOutputs(632MD, [&](CowCompilerInvocation &BuildInvocation) {633if (any(MDC.OptimizeArgs & (ScanningOptimizations::HeaderSearch |634ScanningOptimizations::VFS)))635optimizeHeaderSearchOpts(BuildInvocation.getMutHeaderSearchOpts(),636*MDC.ScanInstance.getASTReader(), *MF,637MDC.PrebuiltModuleVFSMap,638MDC.OptimizeArgs);639if (any(MDC.OptimizeArgs & ScanningOptimizations::SystemWarnings))640optimizeDiagnosticOpts(641BuildInvocation.getMutDiagnosticOpts(),642BuildInvocation.getFrontendOpts().IsSystemModule);643});644645MDC.associateWithContextHash(CI, MD);646647// Finish the compiler invocation. Requires dependencies and the context hash.648MDC.addOutputPaths(CI, MD);649650MD.BuildInfo = std::move(CI);651652return MD.ID;653}654655static void forEachSubmoduleSorted(const Module *M,656llvm::function_ref<void(const Module *)> F) {657// Submodule order depends on order of header includes for inferred submodules658// we don't care about the exact order, so sort so that it's consistent across659// TUs to improve sharing.660SmallVector<const Module *> Submodules(M->submodules());661llvm::stable_sort(Submodules, [](const Module *A, const Module *B) {662return A->Name < B->Name;663});664for (const Module *SubM : Submodules)665F(SubM);666}667668void ModuleDepCollectorPP::addAllSubmodulePrebuiltDeps(669const Module *M, ModuleDeps &MD,670llvm::DenseSet<const Module *> &SeenSubmodules) {671addModulePrebuiltDeps(M, MD, SeenSubmodules);672673forEachSubmoduleSorted(M, [&](const Module *SubM) {674addAllSubmodulePrebuiltDeps(SubM, MD, SeenSubmodules);675});676}677678void ModuleDepCollectorPP::addModulePrebuiltDeps(679const Module *M, ModuleDeps &MD,680llvm::DenseSet<const Module *> &SeenSubmodules) {681for (const Module *Import : M->Imports)682if (Import->getTopLevelModule() != M->getTopLevelModule())683if (MDC.isPrebuiltModule(Import->getTopLevelModule()))684if (SeenSubmodules.insert(Import->getTopLevelModule()).second)685MD.PrebuiltModuleDeps.emplace_back(Import->getTopLevelModule());686}687688void ModuleDepCollectorPP::addAllSubmoduleDeps(689const Module *M, ModuleDeps &MD,690llvm::DenseSet<const Module *> &AddedModules) {691addModuleDep(M, MD, AddedModules);692693forEachSubmoduleSorted(M, [&](const Module *SubM) {694addAllSubmoduleDeps(SubM, MD, AddedModules);695});696}697698void ModuleDepCollectorPP::addModuleDep(699const Module *M, ModuleDeps &MD,700llvm::DenseSet<const Module *> &AddedModules) {701for (const Module *Import : M->Imports) {702if (Import->getTopLevelModule() != M->getTopLevelModule() &&703!MDC.isPrebuiltModule(Import)) {704if (auto ImportID = handleTopLevelModule(Import->getTopLevelModule()))705if (AddedModules.insert(Import->getTopLevelModule()).second)706MD.ClangModuleDeps.push_back(*ImportID);707}708}709}710711void ModuleDepCollectorPP::addAllAffectingClangModules(712const Module *M, ModuleDeps &MD,713llvm::DenseSet<const Module *> &AddedModules) {714addAffectingClangModule(M, MD, AddedModules);715716for (const Module *SubM : M->submodules())717addAllAffectingClangModules(SubM, MD, AddedModules);718}719720void ModuleDepCollectorPP::addAffectingClangModule(721const Module *M, ModuleDeps &MD,722llvm::DenseSet<const Module *> &AddedModules) {723for (const Module *Affecting : M->AffectingClangModules) {724assert(Affecting == Affecting->getTopLevelModule() &&725"Not quite import not top-level module");726if (Affecting != M->getTopLevelModule() &&727!MDC.isPrebuiltModule(Affecting)) {728if (auto ImportID = handleTopLevelModule(Affecting))729if (AddedModules.insert(Affecting).second)730MD.ClangModuleDeps.push_back(*ImportID);731}732}733}734735ModuleDepCollector::ModuleDepCollector(736std::unique_ptr<DependencyOutputOptions> Opts,737CompilerInstance &ScanInstance, DependencyConsumer &C,738DependencyActionController &Controller, CompilerInvocation OriginalCI,739PrebuiltModuleVFSMapT PrebuiltModuleVFSMap,740ScanningOptimizations OptimizeArgs, bool EagerLoadModules,741bool IsStdModuleP1689Format)742: ScanInstance(ScanInstance), Consumer(C), Controller(Controller),743PrebuiltModuleVFSMap(std::move(PrebuiltModuleVFSMap)),744Opts(std::move(Opts)),745CommonInvocation(746makeCommonInvocationForModuleBuild(std::move(OriginalCI))),747OptimizeArgs(OptimizeArgs), EagerLoadModules(EagerLoadModules),748IsStdModuleP1689Format(IsStdModuleP1689Format) {}749750void ModuleDepCollector::attachToPreprocessor(Preprocessor &PP) {751PP.addPPCallbacks(std::make_unique<ModuleDepCollectorPP>(*this));752}753754void ModuleDepCollector::attachToASTReader(ASTReader &R) {}755756bool ModuleDepCollector::isPrebuiltModule(const Module *M) {757std::string Name(M->getTopLevelModuleName());758const auto &PrebuiltModuleFiles =759ScanInstance.getHeaderSearchOpts().PrebuiltModuleFiles;760auto PrebuiltModuleFileIt = PrebuiltModuleFiles.find(Name);761if (PrebuiltModuleFileIt == PrebuiltModuleFiles.end())762return false;763assert("Prebuilt module came from the expected AST file" &&764PrebuiltModuleFileIt->second == M->getASTFile()->getName());765return true;766}767768static StringRef makeAbsoluteAndPreferred(CompilerInstance &CI, StringRef Path,769SmallVectorImpl<char> &Storage) {770if (llvm::sys::path::is_absolute(Path) &&771!llvm::sys::path::is_style_windows(llvm::sys::path::Style::native))772return Path;773Storage.assign(Path.begin(), Path.end());774CI.getFileManager().makeAbsolutePath(Storage);775llvm::sys::path::make_preferred(Storage);776return StringRef(Storage.data(), Storage.size());777}778779void ModuleDepCollector::addFileDep(StringRef Path) {780if (IsStdModuleP1689Format) {781// Within P1689 format, we don't want all the paths to be absolute path782// since it may violate the tranditional make style dependencies info.783FileDeps.push_back(std::string(Path));784return;785}786787llvm::SmallString<256> Storage;788Path = makeAbsoluteAndPreferred(ScanInstance, Path, Storage);789FileDeps.push_back(std::string(Path));790}791792void ModuleDepCollector::addFileDep(ModuleDeps &MD, StringRef Path) {793if (IsStdModuleP1689Format) {794MD.FileDeps.insert(Path);795return;796}797798llvm::SmallString<256> Storage;799Path = makeAbsoluteAndPreferred(ScanInstance, Path, Storage);800MD.FileDeps.insert(Path);801}802803804