133a745e6SMichael Spencer //===- ModuleDepCollector.cpp - Callbacks to collect deps -------*- C++ -*-===//
233a745e6SMichael Spencer //
3c874dd53SChristopher Di Bella // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4c874dd53SChristopher Di Bella // See https://llvm.org/LICENSE.txt for license information.
5c874dd53SChristopher Di Bella // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
633a745e6SMichael Spencer //
733a745e6SMichael Spencer //===----------------------------------------------------------------------===//
833a745e6SMichael Spencer 
933a745e6SMichael Spencer #include "clang/Tooling/DependencyScanning/ModuleDepCollector.h"
1033a745e6SMichael Spencer 
11*3ce78cbdSBen Langmuir #include "clang/Basic/MakeSupport.h"
1233a745e6SMichael Spencer #include "clang/Frontend/CompilerInstance.h"
1333a745e6SMichael Spencer #include "clang/Lex/Preprocessor.h"
1433a745e6SMichael Spencer #include "clang/Tooling/DependencyScanning/DependencyScanningWorker.h"
150a92e09cSJan Svoboda #include "llvm/Support/StringSaver.h"
1633a745e6SMichael Spencer 
1733a745e6SMichael Spencer using namespace clang;
1833a745e6SMichael Spencer using namespace tooling;
1933a745e6SMichael Spencer using namespace dependencies;
2033a745e6SMichael Spencer 
optimizeHeaderSearchOpts(HeaderSearchOptions & Opts,ASTReader & Reader,const serialization::ModuleFile & MF)216a1f50b8SJan Svoboda static void optimizeHeaderSearchOpts(HeaderSearchOptions &Opts,
226a1f50b8SJan Svoboda                                      ASTReader &Reader,
236a1f50b8SJan Svoboda                                      const serialization::ModuleFile &MF) {
246a1f50b8SJan Svoboda   // Only preserve search paths that were used during the dependency scan.
256a1f50b8SJan Svoboda   std::vector<HeaderSearchOptions::Entry> Entries = Opts.UserEntries;
266a1f50b8SJan Svoboda   Opts.UserEntries.clear();
27d73daa91SJan Svoboda 
28d73daa91SJan Svoboda   llvm::BitVector SearchPathUsage(Entries.size());
29d73daa91SJan Svoboda   llvm::DenseSet<const serialization::ModuleFile *> Visited;
30d73daa91SJan Svoboda   std::function<void(const serialization::ModuleFile *)> VisitMF =
31d73daa91SJan Svoboda       [&](const serialization::ModuleFile *MF) {
32d73daa91SJan Svoboda         SearchPathUsage |= MF->SearchPathUsage;
33d73daa91SJan Svoboda         Visited.insert(MF);
34d73daa91SJan Svoboda         for (const serialization::ModuleFile *Import : MF->Imports)
35d73daa91SJan Svoboda           if (!Visited.contains(Import))
36d73daa91SJan Svoboda             VisitMF(Import);
37d73daa91SJan Svoboda       };
38d73daa91SJan Svoboda   VisitMF(&MF);
39d73daa91SJan Svoboda 
40d73daa91SJan Svoboda   for (auto Idx : SearchPathUsage.set_bits())
41d73daa91SJan Svoboda     Opts.UserEntries.push_back(Entries[Idx]);
426a1f50b8SJan Svoboda }
436a1f50b8SJan Svoboda 
makeInvocationForModuleBuildWithoutPaths(const ModuleDeps & Deps,llvm::function_ref<void (CompilerInvocation &)> Optimize) const444629554fSJan Svoboda CompilerInvocation ModuleDepCollector::makeInvocationForModuleBuildWithoutPaths(
456a1f50b8SJan Svoboda     const ModuleDeps &Deps,
466a1f50b8SJan Svoboda     llvm::function_ref<void(CompilerInvocation &)> Optimize) const {
4780c0c639SJan Svoboda   // Make a deep copy of the original Clang invocation.
4880c0c639SJan Svoboda   CompilerInvocation CI(OriginalInvocation);
490a92e09cSJan Svoboda 
506da811fdSJan Svoboda   CI.getLangOpts()->resetNonModularOptions();
516da811fdSJan Svoboda   CI.getPreprocessorOpts().resetNonModularOptions();
526da811fdSJan Svoboda 
535b6c0837SJan Svoboda   // Remove options incompatible with explicit module build or are likely to
545b6c0837SJan Svoboda   // differ between identical modules discovered from different translation
555b6c0837SJan Svoboda   // units.
560a92e09cSJan Svoboda   CI.getFrontendOpts().Inputs.clear();
570a92e09cSJan Svoboda   CI.getFrontendOpts().OutputFile.clear();
585b6c0837SJan Svoboda   CI.getCodeGenOpts().MainFileName.clear();
595b6c0837SJan Svoboda   CI.getCodeGenOpts().DwarfDebugFlags.clear();
606626f6feSBen Langmuir   CI.getDiagnosticOpts().DiagnosticSerializationFile.clear();
616626f6feSBen Langmuir   CI.getDependencyOutputOpts().OutputFile.clear();
626626f6feSBen Langmuir   CI.getDependencyOutputOpts().Targets.clear();
630a92e09cSJan Svoboda 
640a92e09cSJan Svoboda   CI.getFrontendOpts().ProgramAction = frontend::GenerateModule;
650a92e09cSJan Svoboda   CI.getLangOpts()->ModuleName = Deps.ID.ModuleName;
660a92e09cSJan Svoboda   CI.getFrontendOpts().IsSystemModule = Deps.IsSystem;
670a92e09cSJan Svoboda 
68509223daSBen Langmuir   // Disable implicit modules and canonicalize options that are only used by
69509223daSBen Langmuir   // implicit modules.
700a92e09cSJan Svoboda   CI.getLangOpts()->ImplicitModules = false;
71a6ef3635SJan Svoboda   CI.getHeaderSearchOpts().ImplicitModuleMaps = false;
72cf4a31fcSJan Svoboda   CI.getHeaderSearchOpts().ModuleCachePath.clear();
73509223daSBen Langmuir   CI.getHeaderSearchOpts().ModulesValidateOncePerBuildSession = false;
74509223daSBen Langmuir   CI.getHeaderSearchOpts().BuildSessionTimestamp = 0;
75509223daSBen Langmuir   // The specific values we canonicalize to for pruning don't affect behaviour,
76509223daSBen Langmuir   /// so use the default values so they will be dropped from the command-line.
77509223daSBen Langmuir   CI.getHeaderSearchOpts().ModuleCachePruneInterval = 7 * 24 * 60 * 60;
78509223daSBen Langmuir   CI.getHeaderSearchOpts().ModuleCachePruneAfter = 31 * 24 * 60 * 60;
790a92e09cSJan Svoboda 
804629554fSJan Svoboda   // Report the prebuilt modules this module uses.
8117ec9d1fSJan Svoboda   for (const auto &PrebuiltModule : Deps.PrebuiltModuleDeps)
824629554fSJan Svoboda     CI.getFrontendOpts().ModuleFiles.push_back(PrebuiltModule.PCMFile);
834629554fSJan Svoboda 
84a6ef3635SJan Svoboda   CI.getFrontendOpts().ModuleMapFiles = Deps.ModuleMapFileDeps;
85a6ef3635SJan Svoboda 
866a1f50b8SJan Svoboda   Optimize(CI);
876a1f50b8SJan Svoboda 
88954d77b9SJan Svoboda   // The original invocation probably didn't have strict context hash enabled.
89954d77b9SJan Svoboda   // We will use the context hash of this invocation to distinguish between
90954d77b9SJan Svoboda   // multiple incompatible versions of the same module and will use it when
91954d77b9SJan Svoboda   // reporting dependencies to the clients. Let's make sure we're using
92954d77b9SJan Svoboda   // **strict** context hash in order to prevent accidental sharing of
93954d77b9SJan Svoboda   // incompatible modules (e.g. with differences in search paths).
94954d77b9SJan Svoboda   CI.getHeaderSearchOpts().ModulesStrictContextHash = true;
95954d77b9SJan Svoboda 
960a92e09cSJan Svoboda   return CI;
970a92e09cSJan Svoboda }
980a92e09cSJan Svoboda 
990a92e09cSJan Svoboda static std::vector<std::string>
serializeCompilerInvocation(const CompilerInvocation & CI)100d3fb4b90SJan Svoboda serializeCompilerInvocation(const CompilerInvocation &CI) {
1010a92e09cSJan Svoboda   // Set up string allocator.
1020a92e09cSJan Svoboda   llvm::BumpPtrAllocator Alloc;
1030a92e09cSJan Svoboda   llvm::StringSaver Strings(Alloc);
1040a92e09cSJan Svoboda   auto SA = [&Strings](const Twine &Arg) { return Strings.save(Arg).data(); };
1050a92e09cSJan Svoboda 
106398f2534SJan Svoboda   // Synthesize full command line from the CompilerInvocation, including "-cc1".
107398f2534SJan Svoboda   SmallVector<const char *, 32> Args{"-cc1"};
1080a92e09cSJan Svoboda   CI.generateCC1CommandLine(Args, SA);
1090a92e09cSJan Svoboda 
1100a92e09cSJan Svoboda   // Convert arguments to the return type.
111398f2534SJan Svoboda   return std::vector<std::string>{Args.begin(), Args.end()};
1120a92e09cSJan Svoboda }
1130a92e09cSJan Svoboda 
splitString(std::string S,char Separator)1146626f6feSBen Langmuir static std::vector<std::string> splitString(std::string S, char Separator) {
1156626f6feSBen Langmuir   SmallVector<StringRef> Segments;
116*3ce78cbdSBen Langmuir   StringRef(S).split(Segments, Separator, /*MaxSplit=*/-1, /*KeepEmpty=*/false);
1176626f6feSBen Langmuir   std::vector<std::string> Result;
1186626f6feSBen Langmuir   Result.reserve(Segments.size());
1196626f6feSBen Langmuir   for (StringRef Segment : Segments)
1206626f6feSBen Langmuir     Result.push_back(Segment.str());
1216626f6feSBen Langmuir   return Result;
1226626f6feSBen Langmuir }
1236626f6feSBen Langmuir 
getCanonicalCommandLine(llvm::function_ref<std::string (const ModuleID &,ModuleOutputKind)> LookupModuleOutput) const1240f7d4105SJan Svoboda std::vector<std::string> ModuleDeps::getCanonicalCommandLine(
1256626f6feSBen Langmuir     llvm::function_ref<std::string(const ModuleID &, ModuleOutputKind)>
1266626f6feSBen Langmuir         LookupModuleOutput) const {
127c62220f9SJan Svoboda   CompilerInvocation CI(BuildInvocation);
1287defab08SMichael Spencer   FrontendOptions &FrontendOpts = CI.getFrontendOpts();
1297defab08SMichael Spencer 
1307defab08SMichael Spencer   InputKind ModuleMapInputKind(FrontendOpts.DashX.getLanguage(),
1317defab08SMichael Spencer                                InputKind::Format::ModuleMap);
1327defab08SMichael Spencer   FrontendOpts.Inputs.emplace_back(ClangModuleMapFile, ModuleMapInputKind);
1336626f6feSBen Langmuir   FrontendOpts.OutputFile =
1346626f6feSBen Langmuir       LookupModuleOutput(ID, ModuleOutputKind::ModuleFile);
1356626f6feSBen Langmuir   if (HadSerializedDiagnostics)
1366626f6feSBen Langmuir     CI.getDiagnosticOpts().DiagnosticSerializationFile =
1376626f6feSBen Langmuir         LookupModuleOutput(ID, ModuleOutputKind::DiagnosticSerializationFile);
1386626f6feSBen Langmuir   if (HadDependencyFile) {
139*3ce78cbdSBen Langmuir     DependencyOutputOptions &DepOpts = CI.getDependencyOutputOpts();
140*3ce78cbdSBen Langmuir     DepOpts.OutputFile =
1416626f6feSBen Langmuir         LookupModuleOutput(ID, ModuleOutputKind::DependencyFile);
142*3ce78cbdSBen Langmuir     DepOpts.Targets = splitString(
1436626f6feSBen Langmuir         LookupModuleOutput(ID, ModuleOutputKind::DependencyTargets), '\0');
144*3ce78cbdSBen Langmuir     if (!DepOpts.OutputFile.empty() && DepOpts.Targets.empty()) {
145*3ce78cbdSBen Langmuir       // Fallback to -o as dependency target, as in the driver.
146*3ce78cbdSBen Langmuir       SmallString<128> Target;
147*3ce78cbdSBen Langmuir       quoteMakeTarget(FrontendOpts.OutputFile, Target);
148*3ce78cbdSBen Langmuir       DepOpts.Targets.push_back(std::string(Target));
149*3ce78cbdSBen Langmuir     }
1506626f6feSBen Langmuir   }
151356a4b43SMichael Spencer 
1527f6af607SJan Svoboda   for (ModuleID MID : ClangModuleDeps)
1536626f6feSBen Langmuir     FrontendOpts.ModuleFiles.push_back(
1546626f6feSBen Langmuir         LookupModuleOutput(MID, ModuleOutputKind::ModuleFile));
155356a4b43SMichael Spencer 
1560a92e09cSJan Svoboda   return serializeCompilerInvocation(CI);
157356a4b43SMichael Spencer }
158356a4b43SMichael Spencer 
1590f7d4105SJan Svoboda std::vector<std::string>
getCanonicalCommandLineWithoutModulePaths() const1600f7d4105SJan Svoboda ModuleDeps::getCanonicalCommandLineWithoutModulePaths() const {
161c62220f9SJan Svoboda   return serializeCompilerInvocation(BuildInvocation);
1620f7d4105SJan Svoboda }
1630f7d4105SJan Svoboda 
FileChanged(SourceLocation Loc,FileChangeReason Reason,SrcMgr::CharacteristicKind FileType,FileID PrevFID)16433a745e6SMichael Spencer void ModuleDepCollectorPP::FileChanged(SourceLocation Loc,
16533a745e6SMichael Spencer                                        FileChangeReason Reason,
16633a745e6SMichael Spencer                                        SrcMgr::CharacteristicKind FileType,
16733a745e6SMichael Spencer                                        FileID PrevFID) {
16833a745e6SMichael Spencer   if (Reason != PPCallbacks::EnterFile)
16933a745e6SMichael Spencer     return;
17033a745e6SMichael Spencer 
171356a4b43SMichael Spencer   // This has to be delayed as the context hash can change at the start of
172356a4b43SMichael Spencer   // `CompilerInstance::ExecuteAction`.
173356a4b43SMichael Spencer   if (MDC.ContextHash.empty()) {
174207e9fdeSJan Svoboda     MDC.ContextHash = MDC.ScanInstance.getInvocation().getModuleHash();
175356a4b43SMichael Spencer     MDC.Consumer.handleContextHash(MDC.ContextHash);
176356a4b43SMichael Spencer   }
177356a4b43SMichael Spencer 
178207e9fdeSJan Svoboda   SourceManager &SM = MDC.ScanInstance.getSourceManager();
17933a745e6SMichael Spencer 
18033a745e6SMichael Spencer   // Dependency generation really does want to go all the way to the
18133a745e6SMichael Spencer   // file entry for a source location to find out what is depended on.
18233a745e6SMichael Spencer   // We do not want #line markers to affect dependency generation!
183cf593d22SDuncan P. N. Exon Smith   if (Optional<StringRef> Filename =
184cf593d22SDuncan P. N. Exon Smith           SM.getNonBuiltinFilenameForID(SM.getFileID(SM.getExpansionLoc(Loc))))
185772e9f88SJan Svoboda     MDC.FileDeps.push_back(
186cf593d22SDuncan P. N. Exon Smith         std::string(llvm::sys::path::remove_leading_dotslash(*Filename)));
18733a745e6SMichael Spencer }
18833a745e6SMichael Spencer 
InclusionDirective(SourceLocation HashLoc,const Token & IncludeTok,StringRef FileName,bool IsAngled,CharSourceRange FilenameRange,Optional<FileEntryRef> File,StringRef SearchPath,StringRef RelativePath,const Module * Imported,SrcMgr::CharacteristicKind FileType)18933a745e6SMichael Spencer void ModuleDepCollectorPP::InclusionDirective(
19033a745e6SMichael Spencer     SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName,
191d79ad2f1SJan Svoboda     bool IsAngled, CharSourceRange FilenameRange, Optional<FileEntryRef> File,
19233a745e6SMichael Spencer     StringRef SearchPath, StringRef RelativePath, const Module *Imported,
19333a745e6SMichael Spencer     SrcMgr::CharacteristicKind FileType) {
19433a745e6SMichael Spencer   if (!File && !Imported) {
19533a745e6SMichael Spencer     // This is a non-modular include that HeaderSearch failed to find. Add it
19633a745e6SMichael Spencer     // here as `FileChanged` will never see it.
197772e9f88SJan Svoboda     MDC.FileDeps.push_back(std::string(FileName));
19833a745e6SMichael Spencer   }
199356a4b43SMichael Spencer   handleImport(Imported);
200356a4b43SMichael Spencer }
20133a745e6SMichael Spencer 
moduleImport(SourceLocation ImportLoc,ModuleIdPath Path,const Module * Imported)202356a4b43SMichael Spencer void ModuleDepCollectorPP::moduleImport(SourceLocation ImportLoc,
203356a4b43SMichael Spencer                                         ModuleIdPath Path,
204356a4b43SMichael Spencer                                         const Module *Imported) {
205356a4b43SMichael Spencer   handleImport(Imported);
206356a4b43SMichael Spencer }
207356a4b43SMichael Spencer 
handleImport(const Module * Imported)208356a4b43SMichael Spencer void ModuleDepCollectorPP::handleImport(const Module *Imported) {
20933a745e6SMichael Spencer   if (!Imported)
21033a745e6SMichael Spencer     return;
21133a745e6SMichael Spencer 
212772e9f88SJan Svoboda   const Module *TopLevelModule = Imported->getTopLevelModule();
2134629554fSJan Svoboda 
2144629554fSJan Svoboda   if (MDC.isPrebuiltModule(TopLevelModule))
2154629554fSJan Svoboda     DirectPrebuiltModularDeps.insert(TopLevelModule);
2164629554fSJan Svoboda   else
217772e9f88SJan Svoboda     DirectModularDeps.insert(TopLevelModule);
21833a745e6SMichael Spencer }
21933a745e6SMichael Spencer 
EndOfMainFile()22033a745e6SMichael Spencer void ModuleDepCollectorPP::EndOfMainFile() {
221207e9fdeSJan Svoboda   FileID MainFileID = MDC.ScanInstance.getSourceManager().getMainFileID();
222207e9fdeSJan Svoboda   MDC.MainFile = std::string(MDC.ScanInstance.getSourceManager()
223207e9fdeSJan Svoboda                                  .getFileEntryForID(MainFileID)
224207e9fdeSJan Svoboda                                  ->getName());
22533a745e6SMichael Spencer 
226207e9fdeSJan Svoboda   if (!MDC.ScanInstance.getPreprocessorOpts().ImplicitPCHInclude.empty())
22724616664SJan Svoboda     MDC.FileDeps.push_back(
228207e9fdeSJan Svoboda         MDC.ScanInstance.getPreprocessorOpts().ImplicitPCHInclude);
2299223209bSJan Svoboda 
230c68f2472SAlex Lorenz   for (const Module *M : DirectModularDeps) {
231c68f2472SAlex Lorenz     // A top-level module might not be actually imported as a module when
232c68f2472SAlex Lorenz     // -fmodule-name is used to compile a translation unit that imports this
233c68f2472SAlex Lorenz     // module. In that case it can be skipped. The appropriate header
234c68f2472SAlex Lorenz     // dependencies will still be reported as expected.
235c68f2472SAlex Lorenz     if (!M->getASTFile())
236c68f2472SAlex Lorenz       continue;
23733a745e6SMichael Spencer     handleTopLevelModule(M);
238c68f2472SAlex Lorenz   }
23933a745e6SMichael Spencer 
240f95ff816SJan Svoboda   MDC.Consumer.handleDependencyOutputOpts(*MDC.Opts);
241f95ff816SJan Svoboda 
242772e9f88SJan Svoboda   for (auto &&I : MDC.ModularDeps)
243835fcf2aSBen Langmuir     MDC.Consumer.handleModuleDependency(*I.second);
24433a745e6SMichael Spencer 
245772e9f88SJan Svoboda   for (auto &&I : MDC.FileDeps)
246f95ff816SJan Svoboda     MDC.Consumer.handleFileDependency(I);
2474629554fSJan Svoboda 
2484629554fSJan Svoboda   for (auto &&I : DirectPrebuiltModularDeps)
2494629554fSJan Svoboda     MDC.Consumer.handlePrebuiltModuleDependency(PrebuiltModuleDep{I});
25033a745e6SMichael Spencer }
25133a745e6SMichael Spencer 
handleTopLevelModule(const Module * M)252b9d5b0c2SJan Svoboda ModuleID ModuleDepCollectorPP::handleTopLevelModule(const Module *M) {
25333a745e6SMichael Spencer   assert(M == M->getTopLevelModule() && "Expected top level module!");
25433a745e6SMichael Spencer 
255b9d5b0c2SJan Svoboda   // If this module has been handled already, just return its ID.
256835fcf2aSBen Langmuir   auto ModI = MDC.ModularDeps.insert({M, nullptr});
257b9d5b0c2SJan Svoboda   if (!ModI.second)
258835fcf2aSBen Langmuir     return ModI.first->second->ID;
25933a745e6SMichael Spencer 
260835fcf2aSBen Langmuir   ModI.first->second = std::make_unique<ModuleDeps>();
261835fcf2aSBen Langmuir   ModuleDeps &MD = *ModI.first->second;
26233a745e6SMichael Spencer 
263b9d5b0c2SJan Svoboda   MD.ID.ModuleName = M->getFullModuleName();
264b9d5b0c2SJan Svoboda   MD.ImportedByMainFile = DirectModularDeps.contains(M);
265b9d5b0c2SJan Svoboda   MD.ImplicitModulePCMPath = std::string(M->getASTFile()->getName());
266b9d5b0c2SJan Svoboda   MD.IsSystem = M->IsSystem;
267b9d5b0c2SJan Svoboda 
268207e9fdeSJan Svoboda   const FileEntry *ModuleMap = MDC.ScanInstance.getPreprocessor()
26933a745e6SMichael Spencer                                    .getHeaderSearchInfo()
27033a745e6SMichael Spencer                                    .getModuleMap()
2717defab08SMichael Spencer                                    .getModuleMapFileForUniquing(M);
27237e6e022SMichael Spencer 
27337e6e022SMichael Spencer   if (ModuleMap) {
27437e6e022SMichael Spencer     StringRef Path = ModuleMap->tryGetRealPathName();
27537e6e022SMichael Spencer     if (Path.empty())
27637e6e022SMichael Spencer       Path = ModuleMap->getName();
27737e6e022SMichael Spencer     MD.ClangModuleMapFile = std::string(Path);
27837e6e022SMichael Spencer   }
279b9d5b0c2SJan Svoboda 
28033a745e6SMichael Spencer   serialization::ModuleFile *MF =
281207e9fdeSJan Svoboda       MDC.ScanInstance.getASTReader()->getModuleManager().lookup(
282207e9fdeSJan Svoboda           M->getASTFile());
283207e9fdeSJan Svoboda   MDC.ScanInstance.getASTReader()->visitInputFiles(
28433a745e6SMichael Spencer       *MF, true, true, [&](const serialization::InputFile &IF, bool isSystem) {
2857defab08SMichael Spencer         // __inferred_module.map is the result of the way in which an implicit
2867defab08SMichael Spencer         // module build handles inferred modules. It adds an overlay VFS with
2877defab08SMichael Spencer         // this file in the proper directory and relies on the rest of Clang to
2887defab08SMichael Spencer         // handle it like normal. With explicitly built modules we don't need
2897defab08SMichael Spencer         // to play VFS tricks, so replace it with the correct module map.
2907defab08SMichael Spencer         if (IF.getFile()->getName().endswith("__inferred_module.map")) {
2917defab08SMichael Spencer           MD.FileDeps.insert(ModuleMap->getName());
2927defab08SMichael Spencer           return;
2937defab08SMichael Spencer         }
29433a745e6SMichael Spencer         MD.FileDeps.insert(IF.getFile()->getName());
29533a745e6SMichael Spencer       });
29633a745e6SMichael Spencer 
297a6ef3635SJan Svoboda   // We usually don't need to list the module map files of our dependencies when
298a6ef3635SJan Svoboda   // building a module explicitly: their semantics will be deserialized from PCM
299a6ef3635SJan Svoboda   // files.
300a6ef3635SJan Svoboda   //
301a6ef3635SJan Svoboda   // However, some module maps loaded implicitly during the dependency scan can
302a6ef3635SJan Svoboda   // describe anti-dependencies. That happens when this module, let's call it
303a6ef3635SJan Svoboda   // M1, is marked as '[no_undeclared_includes]' and tries to access a header
304a6ef3635SJan Svoboda   // "M2/M2.h" from another module, M2, but doesn't have a 'use M2;'
305a6ef3635SJan Svoboda   // declaration. The explicit build needs the module map for M2 so that it
306a6ef3635SJan Svoboda   // knows that textually including "M2/M2.h" is not allowed.
307a6ef3635SJan Svoboda   // E.g., '__has_include("M2/M2.h")' should return false, but without M2's
308a6ef3635SJan Svoboda   // module map the explicit build would return true.
309a6ef3635SJan Svoboda   //
310a6ef3635SJan Svoboda   // An alternative approach would be to tell the explicit build what its
311a6ef3635SJan Svoboda   // textual dependencies are, instead of having it re-discover its
312a6ef3635SJan Svoboda   // anti-dependencies. For example, we could create and use an `-ivfs-overlay`
313a6ef3635SJan Svoboda   // with `fall-through: false` that explicitly listed the dependencies.
314a6ef3635SJan Svoboda   // However, that's more complicated to implement and harder to reason about.
315a6ef3635SJan Svoboda   if (M->NoUndeclaredIncludes) {
316a6ef3635SJan Svoboda     // We don't have a good way to determine which module map described the
317a6ef3635SJan Svoboda     // anti-dependency (let alone what's the corresponding top-level module
318a6ef3635SJan Svoboda     // map). We simply specify all the module maps in the order they were loaded
319a6ef3635SJan Svoboda     // during the implicit build during scan.
320a6ef3635SJan Svoboda     // TODO: Resolve this by serializing and only using Module::UndeclaredUses.
321a6ef3635SJan Svoboda     MDC.ScanInstance.getASTReader()->visitTopLevelModuleMaps(
322a6ef3635SJan Svoboda         *MF, [&](const FileEntry *FE) {
323a6ef3635SJan Svoboda           if (FE->getName().endswith("__inferred_module.map"))
324a6ef3635SJan Svoboda             return;
325a6ef3635SJan Svoboda           // The top-level modulemap of this module will be the input file. We
326a6ef3635SJan Svoboda           // don't need to specify it as a module map.
327a6ef3635SJan Svoboda           if (FE == ModuleMap)
328a6ef3635SJan Svoboda             return;
329a6ef3635SJan Svoboda           MD.ModuleMapFileDeps.push_back(FE->getName().str());
330a6ef3635SJan Svoboda         });
331a6ef3635SJan Svoboda   }
332a6ef3635SJan Svoboda 
3334629554fSJan Svoboda   // Add direct prebuilt module dependencies now, so that we can use them when
3344629554fSJan Svoboda   // creating a CompilerInvocation and computing context hash for this
3354629554fSJan Svoboda   // ModuleDeps instance.
33683c633eaSJan Svoboda   llvm::DenseSet<const Module *> SeenModules;
33783c633eaSJan Svoboda   addAllSubmodulePrebuiltDeps(M, MD, SeenModules);
3384629554fSJan Svoboda 
339c62220f9SJan Svoboda   MD.BuildInvocation = MDC.makeInvocationForModuleBuildWithoutPaths(
340c62220f9SJan Svoboda       MD, [&](CompilerInvocation &BuildInvocation) {
3416a1f50b8SJan Svoboda         if (MDC.OptimizeArgs)
342c62220f9SJan Svoboda           optimizeHeaderSearchOpts(BuildInvocation.getHeaderSearchOpts(),
343207e9fdeSJan Svoboda                                    *MDC.ScanInstance.getASTReader(), *MF);
3446a1f50b8SJan Svoboda       });
3456626f6feSBen Langmuir   MD.HadSerializedDiagnostics = !MDC.OriginalInvocation.getDiagnosticOpts()
3466626f6feSBen Langmuir                                      .DiagnosticSerializationFile.empty();
3476626f6feSBen Langmuir   MD.HadDependencyFile =
3486626f6feSBen Langmuir       !MDC.OriginalInvocation.getDependencyOutputOpts().OutputFile.empty();
3496626f6feSBen Langmuir   // FIXME: HadSerializedDiagnostics and HadDependencyFile should be included in
3506626f6feSBen Langmuir   // the context hash since it can affect the command-line.
351c62220f9SJan Svoboda   MD.ID.ContextHash = MD.BuildInvocation.getModuleHash();
352b9d5b0c2SJan Svoboda 
353356a4b43SMichael Spencer   llvm::DenseSet<const Module *> AddedModules;
354356a4b43SMichael Spencer   addAllSubmoduleDeps(M, MD, AddedModules);
355b9d5b0c2SJan Svoboda 
356b9d5b0c2SJan Svoboda   return MD.ID;
35733a745e6SMichael Spencer }
35833a745e6SMichael Spencer 
forEachSubmoduleSorted(const Module * M,llvm::function_ref<void (const Module *)> F)3594a3a9a5fSBen Langmuir static void forEachSubmoduleSorted(const Module *M,
3604a3a9a5fSBen Langmuir                                    llvm::function_ref<void(const Module *)> F) {
3614a3a9a5fSBen Langmuir   // Submodule order depends on order of header includes for inferred submodules
3624a3a9a5fSBen Langmuir   // we don't care about the exact order, so sort so that it's consistent across
3634a3a9a5fSBen Langmuir   // TUs to improve sharing.
3644a3a9a5fSBen Langmuir   SmallVector<const Module *> Submodules(M->submodule_begin(),
3654a3a9a5fSBen Langmuir                                          M->submodule_end());
3664a3a9a5fSBen Langmuir   llvm::stable_sort(Submodules, [](const Module *A, const Module *B) {
3674a3a9a5fSBen Langmuir     return A->Name < B->Name;
3684a3a9a5fSBen Langmuir   });
3694a3a9a5fSBen Langmuir   for (const Module *SubM : Submodules)
3704a3a9a5fSBen Langmuir     F(SubM);
3714a3a9a5fSBen Langmuir }
3724a3a9a5fSBen Langmuir 
addAllSubmodulePrebuiltDeps(const Module * M,ModuleDeps & MD,llvm::DenseSet<const Module * > & SeenSubmodules)37383c633eaSJan Svoboda void ModuleDepCollectorPP::addAllSubmodulePrebuiltDeps(
37483c633eaSJan Svoboda     const Module *M, ModuleDeps &MD,
37583c633eaSJan Svoboda     llvm::DenseSet<const Module *> &SeenSubmodules) {
37683c633eaSJan Svoboda   addModulePrebuiltDeps(M, MD, SeenSubmodules);
37783c633eaSJan Svoboda 
3784a3a9a5fSBen Langmuir   forEachSubmoduleSorted(M, [&](const Module *SubM) {
37983c633eaSJan Svoboda     addAllSubmodulePrebuiltDeps(SubM, MD, SeenSubmodules);
3804a3a9a5fSBen Langmuir   });
38183c633eaSJan Svoboda }
38283c633eaSJan Svoboda 
addModulePrebuiltDeps(const Module * M,ModuleDeps & MD,llvm::DenseSet<const Module * > & SeenSubmodules)38383c633eaSJan Svoboda void ModuleDepCollectorPP::addModulePrebuiltDeps(
38483c633eaSJan Svoboda     const Module *M, ModuleDeps &MD,
38583c633eaSJan Svoboda     llvm::DenseSet<const Module *> &SeenSubmodules) {
3864629554fSJan Svoboda   for (const Module *Import : M->Imports)
3874629554fSJan Svoboda     if (Import->getTopLevelModule() != M->getTopLevelModule())
3883b8f536fSJan Svoboda       if (MDC.isPrebuiltModule(Import->getTopLevelModule()))
3893b8f536fSJan Svoboda         if (SeenSubmodules.insert(Import->getTopLevelModule()).second)
3903b8f536fSJan Svoboda           MD.PrebuiltModuleDeps.emplace_back(Import->getTopLevelModule());
3914629554fSJan Svoboda }
3924629554fSJan Svoboda 
addAllSubmoduleDeps(const Module * M,ModuleDeps & MD,llvm::DenseSet<const Module * > & AddedModules)393356a4b43SMichael Spencer void ModuleDepCollectorPP::addAllSubmoduleDeps(
394356a4b43SMichael Spencer     const Module *M, ModuleDeps &MD,
395356a4b43SMichael Spencer     llvm::DenseSet<const Module *> &AddedModules) {
396356a4b43SMichael Spencer   addModuleDep(M, MD, AddedModules);
39733a745e6SMichael Spencer 
3984a3a9a5fSBen Langmuir   forEachSubmoduleSorted(M, [&](const Module *SubM) {
399356a4b43SMichael Spencer     addAllSubmoduleDeps(SubM, MD, AddedModules);
4004a3a9a5fSBen Langmuir   });
40133a745e6SMichael Spencer }
40233a745e6SMichael Spencer 
addModuleDep(const Module * M,ModuleDeps & MD,llvm::DenseSet<const Module * > & AddedModules)403356a4b43SMichael Spencer void ModuleDepCollectorPP::addModuleDep(
404356a4b43SMichael Spencer     const Module *M, ModuleDeps &MD,
405356a4b43SMichael Spencer     llvm::DenseSet<const Module *> &AddedModules) {
40633a745e6SMichael Spencer   for (const Module *Import : M->Imports) {
4074629554fSJan Svoboda     if (Import->getTopLevelModule() != M->getTopLevelModule() &&
4084629554fSJan Svoboda         !MDC.isPrebuiltModule(Import)) {
409b9d5b0c2SJan Svoboda       ModuleID ImportID = handleTopLevelModule(Import->getTopLevelModule());
410356a4b43SMichael Spencer       if (AddedModules.insert(Import->getTopLevelModule()).second)
411b9d5b0c2SJan Svoboda         MD.ClangModuleDeps.push_back(ImportID);
41233a745e6SMichael Spencer     }
41333a745e6SMichael Spencer   }
41433a745e6SMichael Spencer }
41533a745e6SMichael Spencer 
ModuleDepCollector(std::unique_ptr<DependencyOutputOptions> Opts,CompilerInstance & ScanInstance,DependencyConsumer & C,CompilerInvocation && OriginalCI,bool OptimizeArgs)416356a4b43SMichael Spencer ModuleDepCollector::ModuleDepCollector(
417207e9fdeSJan Svoboda     std::unique_ptr<DependencyOutputOptions> Opts,
418207e9fdeSJan Svoboda     CompilerInstance &ScanInstance, DependencyConsumer &C,
419207e9fdeSJan Svoboda     CompilerInvocation &&OriginalCI, bool OptimizeArgs)
420207e9fdeSJan Svoboda     : ScanInstance(ScanInstance), Consumer(C), Opts(std::move(Opts)),
4216a1f50b8SJan Svoboda       OriginalInvocation(std::move(OriginalCI)), OptimizeArgs(OptimizeArgs) {}
42233a745e6SMichael Spencer 
attachToPreprocessor(Preprocessor & PP)42333a745e6SMichael Spencer void ModuleDepCollector::attachToPreprocessor(Preprocessor &PP) {
42424616664SJan Svoboda   PP.addPPCallbacks(std::make_unique<ModuleDepCollectorPP>(*this));
42533a745e6SMichael Spencer }
42633a745e6SMichael Spencer 
attachToASTReader(ASTReader & R)42733a745e6SMichael Spencer void ModuleDepCollector::attachToASTReader(ASTReader &R) {}
4284629554fSJan Svoboda 
isPrebuiltModule(const Module * M)4294629554fSJan Svoboda bool ModuleDepCollector::isPrebuiltModule(const Module *M) {
4304629554fSJan Svoboda   std::string Name(M->getTopLevelModuleName());
4314629554fSJan Svoboda   const auto &PrebuiltModuleFiles =
432207e9fdeSJan Svoboda       ScanInstance.getHeaderSearchOpts().PrebuiltModuleFiles;
4334629554fSJan Svoboda   auto PrebuiltModuleFileIt = PrebuiltModuleFiles.find(Name);
4344629554fSJan Svoboda   if (PrebuiltModuleFileIt == PrebuiltModuleFiles.end())
4354629554fSJan Svoboda     return false;
4364629554fSJan Svoboda   assert("Prebuilt module came from the expected AST file" &&
4374629554fSJan Svoboda          PrebuiltModuleFileIt->second == M->getASTFile()->getName());
4384629554fSJan Svoboda   return true;
4394629554fSJan Svoboda }
440