diff options
Diffstat (limited to 'clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp')
| -rw-r--r-- | clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp | 129 |
1 files changed, 90 insertions, 39 deletions
diff --git a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp index 086215e7a573..f7d96130b971 100644 --- a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp +++ b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp @@ -23,9 +23,21 @@ static void optimizeHeaderSearchOpts(HeaderSearchOptions &Opts, // Only preserve search paths that were used during the dependency scan. std::vector<HeaderSearchOptions::Entry> Entries = Opts.UserEntries; Opts.UserEntries.clear(); - for (unsigned I = 0; I < Entries.size(); ++I) - if (MF.SearchPathUsage[I]) - Opts.UserEntries.push_back(Entries[I]); + + llvm::BitVector SearchPathUsage(Entries.size()); + llvm::DenseSet<const serialization::ModuleFile *> Visited; + std::function<void(const serialization::ModuleFile *)> VisitMF = + [&](const serialization::ModuleFile *MF) { + SearchPathUsage |= MF->SearchPathUsage; + Visited.insert(MF); + for (const serialization::ModuleFile *Import : MF->Imports) + if (!Visited.contains(Import)) + VisitMF(Import); + }; + VisitMF(&MF); + + for (auto Idx : SearchPathUsage.set_bits()) + Opts.UserEntries.push_back(Entries[Idx]); } CompilerInvocation ModuleDepCollector::makeInvocationForModuleBuildWithoutPaths( @@ -49,12 +61,24 @@ CompilerInvocation ModuleDepCollector::makeInvocationForModuleBuildWithoutPaths( CI.getLangOpts()->ModuleName = Deps.ID.ModuleName; CI.getFrontendOpts().IsSystemModule = Deps.IsSystem; + // Disable implicit modules and canonicalize options that are only used by + // implicit modules. CI.getLangOpts()->ImplicitModules = false; + CI.getHeaderSearchOpts().ImplicitModuleMaps = false; + CI.getHeaderSearchOpts().ModuleCachePath.clear(); + CI.getHeaderSearchOpts().ModulesValidateOncePerBuildSession = false; + CI.getHeaderSearchOpts().BuildSessionTimestamp = 0; + // The specific values we canonicalize to for pruning don't affect behaviour, + /// so use the default values so they will be dropped from the command-line. + CI.getHeaderSearchOpts().ModuleCachePruneInterval = 7 * 24 * 60 * 60; + CI.getHeaderSearchOpts().ModuleCachePruneAfter = 31 * 24 * 60 * 60; // Report the prebuilt modules this module uses. for (const auto &PrebuiltModule : Deps.PrebuiltModuleDeps) CI.getFrontendOpts().ModuleFiles.push_back(PrebuiltModule.PCMFile); + CI.getFrontendOpts().ModuleMapFiles = Deps.ModuleMapFileDeps; + Optimize(CI); // The original invocation probably didn't have strict context hash enabled. @@ -84,8 +108,7 @@ serializeCompilerInvocation(const CompilerInvocation &CI) { } std::vector<std::string> ModuleDeps::getCanonicalCommandLine( - std::function<StringRef(ModuleID)> LookupPCMPath, - std::function<const ModuleDeps &(ModuleID)> LookupModuleDeps) const { + std::function<StringRef(ModuleID)> LookupPCMPath) const { CompilerInvocation CI(BuildInvocation); FrontendOptions &FrontendOpts = CI.getFrontendOpts(); @@ -94,9 +117,8 @@ std::vector<std::string> ModuleDeps::getCanonicalCommandLine( FrontendOpts.Inputs.emplace_back(ClangModuleMapFile, ModuleMapInputKind); FrontendOpts.OutputFile = std::string(LookupPCMPath(ID)); - dependencies::detail::collectPCMAndModuleMapPaths( - ClangModuleDeps, LookupPCMPath, LookupModuleDeps, - FrontendOpts.ModuleFiles, FrontendOpts.ModuleMapFiles); + for (ModuleID MID : ClangModuleDeps) + FrontendOpts.ModuleFiles.emplace_back(LookupPCMPath(MID)); return serializeCompilerInvocation(CI); } @@ -106,30 +128,6 @@ ModuleDeps::getCanonicalCommandLineWithoutModulePaths() const { return serializeCompilerInvocation(BuildInvocation); } -void dependencies::detail::collectPCMAndModuleMapPaths( - llvm::ArrayRef<ModuleID> Modules, - std::function<StringRef(ModuleID)> LookupPCMPath, - std::function<const ModuleDeps &(ModuleID)> LookupModuleDeps, - std::vector<std::string> &PCMPaths, std::vector<std::string> &ModMapPaths) { - llvm::StringSet<> AlreadyAdded; - - std::function<void(llvm::ArrayRef<ModuleID>)> AddArgs = - [&](llvm::ArrayRef<ModuleID> Modules) { - for (const ModuleID &MID : Modules) { - if (!AlreadyAdded.insert(MID.ModuleName + MID.ContextHash).second) - continue; - const ModuleDeps &M = LookupModuleDeps(MID); - // Depth first traversal. - AddArgs(M.ClangModuleDeps); - PCMPaths.push_back(LookupPCMPath(MID).str()); - if (!M.ClangModuleMapFile.empty()) - ModMapPaths.push_back(M.ClangModuleMapFile); - } - }; - - AddArgs(Modules); -} - void ModuleDepCollectorPP::FileChanged(SourceLocation Loc, FileChangeReason Reason, SrcMgr::CharacteristicKind FileType, @@ -157,7 +155,7 @@ void ModuleDepCollectorPP::FileChanged(SourceLocation Loc, void ModuleDepCollectorPP::InclusionDirective( SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName, - bool IsAngled, CharSourceRange FilenameRange, const FileEntry *File, + bool IsAngled, CharSourceRange FilenameRange, Optional<FileEntryRef> File, StringRef SearchPath, StringRef RelativePath, const Module *Imported, SrcMgr::CharacteristicKind FileType) { if (!File && !Imported) { @@ -209,7 +207,7 @@ void ModuleDepCollectorPP::EndOfMainFile() { MDC.Consumer.handleDependencyOutputOpts(*MDC.Opts); for (auto &&I : MDC.ModularDeps) - MDC.Consumer.handleModuleDependency(I.second); + MDC.Consumer.handleModuleDependency(*I.second); for (auto &&I : MDC.FileDeps) MDC.Consumer.handleFileDependency(I); @@ -222,11 +220,12 @@ ModuleID ModuleDepCollectorPP::handleTopLevelModule(const Module *M) { assert(M == M->getTopLevelModule() && "Expected top level module!"); // If this module has been handled already, just return its ID. - auto ModI = MDC.ModularDeps.insert({M, ModuleDeps{}}); + auto ModI = MDC.ModularDeps.insert({M, nullptr}); if (!ModI.second) - return ModI.first->second.ID; + return ModI.first->second->ID; - ModuleDeps &MD = ModI.first->second; + ModI.first->second = std::make_unique<ModuleDeps>(); + ModuleDeps &MD = *ModI.first->second; MD.ID.ModuleName = M->getFullModuleName(); MD.ImportedByMainFile = DirectModularDeps.contains(M); @@ -262,6 +261,42 @@ ModuleID ModuleDepCollectorPP::handleTopLevelModule(const Module *M) { MD.FileDeps.insert(IF.getFile()->getName()); }); + // We usually don't need to list the module map files of our dependencies when + // building a module explicitly: their semantics will be deserialized from PCM + // files. + // + // However, some module maps loaded implicitly during the dependency scan can + // describe anti-dependencies. That happens when this module, let's call it + // M1, is marked as '[no_undeclared_includes]' and tries to access a header + // "M2/M2.h" from another module, M2, but doesn't have a 'use M2;' + // declaration. The explicit build needs the module map for M2 so that it + // knows that textually including "M2/M2.h" is not allowed. + // E.g., '__has_include("M2/M2.h")' should return false, but without M2's + // module map the explicit build would return true. + // + // An alternative approach would be to tell the explicit build what its + // textual dependencies are, instead of having it re-discover its + // anti-dependencies. For example, we could create and use an `-ivfs-overlay` + // with `fall-through: false` that explicitly listed the dependencies. + // However, that's more complicated to implement and harder to reason about. + if (M->NoUndeclaredIncludes) { + // We don't have a good way to determine which module map described the + // anti-dependency (let alone what's the corresponding top-level module + // map). We simply specify all the module maps in the order they were loaded + // during the implicit build during scan. + // TODO: Resolve this by serializing and only using Module::UndeclaredUses. + MDC.ScanInstance.getASTReader()->visitTopLevelModuleMaps( + *MF, [&](const FileEntry *FE) { + if (FE->getName().endswith("__inferred_module.map")) + return; + // The top-level modulemap of this module will be the input file. We + // don't need to specify it as a module map. + if (FE == ModuleMap) + return; + MD.ModuleMapFileDeps.push_back(FE->getName().str()); + }); + } + // Add direct prebuilt module dependencies now, so that we can use them when // creating a CompilerInvocation and computing context hash for this // ModuleDeps instance. @@ -282,13 +317,28 @@ ModuleID ModuleDepCollectorPP::handleTopLevelModule(const Module *M) { return MD.ID; } +static void forEachSubmoduleSorted(const Module *M, + llvm::function_ref<void(const Module *)> F) { + // Submodule order depends on order of header includes for inferred submodules + // we don't care about the exact order, so sort so that it's consistent across + // TUs to improve sharing. + SmallVector<const Module *> Submodules(M->submodule_begin(), + M->submodule_end()); + llvm::stable_sort(Submodules, [](const Module *A, const Module *B) { + return A->Name < B->Name; + }); + for (const Module *SubM : Submodules) + F(SubM); +} + void ModuleDepCollectorPP::addAllSubmodulePrebuiltDeps( const Module *M, ModuleDeps &MD, llvm::DenseSet<const Module *> &SeenSubmodules) { addModulePrebuiltDeps(M, MD, SeenSubmodules); - for (const Module *SubM : M->submodules()) + forEachSubmoduleSorted(M, [&](const Module *SubM) { addAllSubmodulePrebuiltDeps(SubM, MD, SeenSubmodules); + }); } void ModuleDepCollectorPP::addModulePrebuiltDeps( @@ -306,8 +356,9 @@ void ModuleDepCollectorPP::addAllSubmoduleDeps( llvm::DenseSet<const Module *> &AddedModules) { addModuleDep(M, MD, AddedModules); - for (const Module *SubM : M->submodules()) + forEachSubmoduleSorted(M, [&](const Module *SubM) { addAllSubmoduleDeps(SubM, MD, AddedModules); + }); } void ModuleDepCollectorPP::addModuleDep( |
