xref: /freebsd-src/contrib/llvm-project/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp (revision fe6060f10f634930ff71b7c50291ddc610da2475)
1480093f4SDimitry Andric //===- ModuleDepCollector.cpp - Callbacks to collect deps -------*- C++ -*-===//
2480093f4SDimitry Andric //
3480093f4SDimitry Andric //                     The LLVM Compiler Infrastructure
4480093f4SDimitry Andric //
5480093f4SDimitry Andric // This file is distributed under the University of Illinois Open Source
6480093f4SDimitry Andric // License. See LICENSE.TXT for details.
7480093f4SDimitry Andric //
8480093f4SDimitry Andric //===----------------------------------------------------------------------===//
9480093f4SDimitry Andric 
10480093f4SDimitry Andric #include "clang/Tooling/DependencyScanning/ModuleDepCollector.h"
11480093f4SDimitry Andric 
12480093f4SDimitry Andric #include "clang/Frontend/CompilerInstance.h"
13480093f4SDimitry Andric #include "clang/Lex/Preprocessor.h"
14480093f4SDimitry Andric #include "clang/Tooling/DependencyScanning/DependencyScanningWorker.h"
15*fe6060f1SDimitry Andric #include "llvm/Support/StringSaver.h"
16480093f4SDimitry Andric 
17480093f4SDimitry Andric using namespace clang;
18480093f4SDimitry Andric using namespace tooling;
19480093f4SDimitry Andric using namespace dependencies;
20480093f4SDimitry Andric 
21*fe6060f1SDimitry Andric CompilerInvocation ModuleDepCollector::makeInvocationForModuleBuildWithoutPaths(
22*fe6060f1SDimitry Andric     const ModuleDeps &Deps) const {
23*fe6060f1SDimitry Andric   // Make a deep copy of the original Clang invocation.
24*fe6060f1SDimitry Andric   CompilerInvocation CI(OriginalInvocation);
255ffd83dbSDimitry Andric 
26*fe6060f1SDimitry Andric   // Remove options incompatible with explicit module build.
27*fe6060f1SDimitry Andric   CI.getFrontendOpts().Inputs.clear();
28*fe6060f1SDimitry Andric   CI.getFrontendOpts().OutputFile.clear();
295ffd83dbSDimitry Andric 
30*fe6060f1SDimitry Andric   CI.getFrontendOpts().ProgramAction = frontend::GenerateModule;
31*fe6060f1SDimitry Andric   CI.getLangOpts()->ModuleName = Deps.ID.ModuleName;
32*fe6060f1SDimitry Andric   CI.getFrontendOpts().IsSystemModule = Deps.IsSystem;
335ffd83dbSDimitry Andric 
34*fe6060f1SDimitry Andric   CI.getLangOpts()->ImplicitModules = false;
35*fe6060f1SDimitry Andric 
36*fe6060f1SDimitry Andric   // Report the prebuilt modules this module uses.
37*fe6060f1SDimitry Andric   for (const auto &PrebuiltModule : Deps.PrebuiltModuleDeps) {
38*fe6060f1SDimitry Andric     CI.getFrontendOpts().ModuleFiles.push_back(PrebuiltModule.PCMFile);
39*fe6060f1SDimitry Andric     CI.getFrontendOpts().ModuleMapFiles.push_back(PrebuiltModule.ModuleMapFile);
405ffd83dbSDimitry Andric   }
415ffd83dbSDimitry Andric 
42*fe6060f1SDimitry Andric   CI.getPreprocessorOpts().ImplicitPCHInclude.clear();
43*fe6060f1SDimitry Andric 
44*fe6060f1SDimitry Andric   return CI;
45*fe6060f1SDimitry Andric }
46*fe6060f1SDimitry Andric 
47*fe6060f1SDimitry Andric static std::vector<std::string>
48*fe6060f1SDimitry Andric serializeCompilerInvocation(const CompilerInvocation &CI) {
49*fe6060f1SDimitry Andric   // Set up string allocator.
50*fe6060f1SDimitry Andric   llvm::BumpPtrAllocator Alloc;
51*fe6060f1SDimitry Andric   llvm::StringSaver Strings(Alloc);
52*fe6060f1SDimitry Andric   auto SA = [&Strings](const Twine &Arg) { return Strings.save(Arg).data(); };
53*fe6060f1SDimitry Andric 
54*fe6060f1SDimitry Andric   // Synthesize full command line from the CompilerInvocation, including "-cc1".
55*fe6060f1SDimitry Andric   SmallVector<const char *, 32> Args{"-cc1"};
56*fe6060f1SDimitry Andric   CI.generateCC1CommandLine(Args, SA);
57*fe6060f1SDimitry Andric 
58*fe6060f1SDimitry Andric   // Convert arguments to the return type.
59*fe6060f1SDimitry Andric   return std::vector<std::string>{Args.begin(), Args.end()};
60*fe6060f1SDimitry Andric }
61*fe6060f1SDimitry Andric 
62*fe6060f1SDimitry Andric std::vector<std::string> ModuleDeps::getCanonicalCommandLine(
63*fe6060f1SDimitry Andric     std::function<StringRef(ModuleID)> LookupPCMPath,
64*fe6060f1SDimitry Andric     std::function<const ModuleDeps &(ModuleID)> LookupModuleDeps) const {
65*fe6060f1SDimitry Andric   CompilerInvocation CI(Invocation);
66*fe6060f1SDimitry Andric   FrontendOptions &FrontendOpts = CI.getFrontendOpts();
67*fe6060f1SDimitry Andric 
68*fe6060f1SDimitry Andric   InputKind ModuleMapInputKind(FrontendOpts.DashX.getLanguage(),
69*fe6060f1SDimitry Andric                                InputKind::Format::ModuleMap);
70*fe6060f1SDimitry Andric   FrontendOpts.Inputs.emplace_back(ClangModuleMapFile, ModuleMapInputKind);
71*fe6060f1SDimitry Andric   FrontendOpts.OutputFile = std::string(LookupPCMPath(ID));
72*fe6060f1SDimitry Andric 
73*fe6060f1SDimitry Andric   dependencies::detail::collectPCMAndModuleMapPaths(
74*fe6060f1SDimitry Andric       ClangModuleDeps, LookupPCMPath, LookupModuleDeps,
75*fe6060f1SDimitry Andric       FrontendOpts.ModuleFiles, FrontendOpts.ModuleMapFiles);
76*fe6060f1SDimitry Andric 
77*fe6060f1SDimitry Andric   return serializeCompilerInvocation(CI);
78*fe6060f1SDimitry Andric }
79*fe6060f1SDimitry Andric 
80*fe6060f1SDimitry Andric std::vector<std::string>
81*fe6060f1SDimitry Andric ModuleDeps::getCanonicalCommandLineWithoutModulePaths() const {
82*fe6060f1SDimitry Andric   return serializeCompilerInvocation(Invocation);
83*fe6060f1SDimitry Andric }
84*fe6060f1SDimitry Andric 
85*fe6060f1SDimitry Andric void dependencies::detail::collectPCMAndModuleMapPaths(
86*fe6060f1SDimitry Andric     llvm::ArrayRef<ModuleID> Modules,
87*fe6060f1SDimitry Andric     std::function<StringRef(ModuleID)> LookupPCMPath,
88*fe6060f1SDimitry Andric     std::function<const ModuleDeps &(ModuleID)> LookupModuleDeps,
89*fe6060f1SDimitry Andric     std::vector<std::string> &PCMPaths, std::vector<std::string> &ModMapPaths) {
905ffd83dbSDimitry Andric   llvm::StringSet<> AlreadyAdded;
915ffd83dbSDimitry Andric 
92*fe6060f1SDimitry Andric   std::function<void(llvm::ArrayRef<ModuleID>)> AddArgs =
93*fe6060f1SDimitry Andric       [&](llvm::ArrayRef<ModuleID> Modules) {
94*fe6060f1SDimitry Andric         for (const ModuleID &MID : Modules) {
95*fe6060f1SDimitry Andric           if (!AlreadyAdded.insert(MID.ModuleName + MID.ContextHash).second)
965ffd83dbSDimitry Andric             continue;
97*fe6060f1SDimitry Andric           const ModuleDeps &M = LookupModuleDeps(MID);
985ffd83dbSDimitry Andric           // Depth first traversal.
995ffd83dbSDimitry Andric           AddArgs(M.ClangModuleDeps);
100*fe6060f1SDimitry Andric           PCMPaths.push_back(LookupPCMPath(MID).str());
101*fe6060f1SDimitry Andric           if (!M.ClangModuleMapFile.empty())
102*fe6060f1SDimitry Andric             ModMapPaths.push_back(M.ClangModuleMapFile);
1035ffd83dbSDimitry Andric         }
1045ffd83dbSDimitry Andric       };
1055ffd83dbSDimitry Andric 
1065ffd83dbSDimitry Andric   AddArgs(Modules);
1075ffd83dbSDimitry Andric }
1085ffd83dbSDimitry Andric 
109480093f4SDimitry Andric void ModuleDepCollectorPP::FileChanged(SourceLocation Loc,
110480093f4SDimitry Andric                                        FileChangeReason Reason,
111480093f4SDimitry Andric                                        SrcMgr::CharacteristicKind FileType,
112480093f4SDimitry Andric                                        FileID PrevFID) {
113480093f4SDimitry Andric   if (Reason != PPCallbacks::EnterFile)
114480093f4SDimitry Andric     return;
115480093f4SDimitry Andric 
1165ffd83dbSDimitry Andric   // This has to be delayed as the context hash can change at the start of
1175ffd83dbSDimitry Andric   // `CompilerInstance::ExecuteAction`.
1185ffd83dbSDimitry Andric   if (MDC.ContextHash.empty()) {
1195ffd83dbSDimitry Andric     MDC.ContextHash = Instance.getInvocation().getModuleHash();
1205ffd83dbSDimitry Andric     MDC.Consumer.handleContextHash(MDC.ContextHash);
1215ffd83dbSDimitry Andric   }
1225ffd83dbSDimitry Andric 
123480093f4SDimitry Andric   SourceManager &SM = Instance.getSourceManager();
124480093f4SDimitry Andric 
125480093f4SDimitry Andric   // Dependency generation really does want to go all the way to the
126480093f4SDimitry Andric   // file entry for a source location to find out what is depended on.
127480093f4SDimitry Andric   // We do not want #line markers to affect dependency generation!
128e8d8bef9SDimitry Andric   if (Optional<StringRef> Filename =
129e8d8bef9SDimitry Andric           SM.getNonBuiltinFilenameForID(SM.getFileID(SM.getExpansionLoc(Loc))))
130*fe6060f1SDimitry Andric     MDC.FileDeps.push_back(
131e8d8bef9SDimitry Andric         std::string(llvm::sys::path::remove_leading_dotslash(*Filename)));
132480093f4SDimitry Andric }
133480093f4SDimitry Andric 
134480093f4SDimitry Andric void ModuleDepCollectorPP::InclusionDirective(
135480093f4SDimitry Andric     SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName,
136480093f4SDimitry Andric     bool IsAngled, CharSourceRange FilenameRange, const FileEntry *File,
137480093f4SDimitry Andric     StringRef SearchPath, StringRef RelativePath, const Module *Imported,
138480093f4SDimitry Andric     SrcMgr::CharacteristicKind FileType) {
139480093f4SDimitry Andric   if (!File && !Imported) {
140480093f4SDimitry Andric     // This is a non-modular include that HeaderSearch failed to find. Add it
141480093f4SDimitry Andric     // here as `FileChanged` will never see it.
142*fe6060f1SDimitry Andric     MDC.FileDeps.push_back(std::string(FileName));
1435ffd83dbSDimitry Andric   }
1445ffd83dbSDimitry Andric   handleImport(Imported);
145480093f4SDimitry Andric }
146480093f4SDimitry Andric 
1475ffd83dbSDimitry Andric void ModuleDepCollectorPP::moduleImport(SourceLocation ImportLoc,
1485ffd83dbSDimitry Andric                                         ModuleIdPath Path,
1495ffd83dbSDimitry Andric                                         const Module *Imported) {
1505ffd83dbSDimitry Andric   handleImport(Imported);
1515ffd83dbSDimitry Andric }
1525ffd83dbSDimitry Andric 
1535ffd83dbSDimitry Andric void ModuleDepCollectorPP::handleImport(const Module *Imported) {
154480093f4SDimitry Andric   if (!Imported)
155480093f4SDimitry Andric     return;
156480093f4SDimitry Andric 
157*fe6060f1SDimitry Andric   const Module *TopLevelModule = Imported->getTopLevelModule();
158*fe6060f1SDimitry Andric 
159*fe6060f1SDimitry Andric   if (MDC.isPrebuiltModule(TopLevelModule))
160*fe6060f1SDimitry Andric     DirectPrebuiltModularDeps.insert(TopLevelModule);
161*fe6060f1SDimitry Andric   else
162*fe6060f1SDimitry Andric     DirectModularDeps.insert(TopLevelModule);
163480093f4SDimitry Andric }
164480093f4SDimitry Andric 
165480093f4SDimitry Andric void ModuleDepCollectorPP::EndOfMainFile() {
166480093f4SDimitry Andric   FileID MainFileID = Instance.getSourceManager().getMainFileID();
1675ffd83dbSDimitry Andric   MDC.MainFile = std::string(
1685ffd83dbSDimitry Andric       Instance.getSourceManager().getFileEntryForID(MainFileID)->getName());
169480093f4SDimitry Andric 
170*fe6060f1SDimitry Andric   if (!Instance.getPreprocessorOpts().ImplicitPCHInclude.empty())
171*fe6060f1SDimitry Andric     MDC.FileDeps.push_back(Instance.getPreprocessorOpts().ImplicitPCHInclude);
172*fe6060f1SDimitry Andric 
173*fe6060f1SDimitry Andric   for (const Module *M : DirectModularDeps) {
174*fe6060f1SDimitry Andric     // A top-level module might not be actually imported as a module when
175*fe6060f1SDimitry Andric     // -fmodule-name is used to compile a translation unit that imports this
176*fe6060f1SDimitry Andric     // module. In that case it can be skipped. The appropriate header
177*fe6060f1SDimitry Andric     // dependencies will still be reported as expected.
178*fe6060f1SDimitry Andric     if (!M->getASTFile())
179*fe6060f1SDimitry Andric       continue;
180480093f4SDimitry Andric     handleTopLevelModule(M);
181480093f4SDimitry Andric   }
182480093f4SDimitry Andric 
183*fe6060f1SDimitry Andric   MDC.Consumer.handleDependencyOutputOpts(*MDC.Opts);
184*fe6060f1SDimitry Andric 
185*fe6060f1SDimitry Andric   for (auto &&I : MDC.ModularDeps)
186480093f4SDimitry Andric     MDC.Consumer.handleModuleDependency(I.second);
187480093f4SDimitry Andric 
188*fe6060f1SDimitry Andric   for (auto &&I : MDC.FileDeps)
189*fe6060f1SDimitry Andric     MDC.Consumer.handleFileDependency(I);
190*fe6060f1SDimitry Andric 
191*fe6060f1SDimitry Andric   for (auto &&I : DirectPrebuiltModularDeps)
192*fe6060f1SDimitry Andric     MDC.Consumer.handlePrebuiltModuleDependency(PrebuiltModuleDep{I});
193480093f4SDimitry Andric }
194480093f4SDimitry Andric 
195*fe6060f1SDimitry Andric ModuleID ModuleDepCollectorPP::handleTopLevelModule(const Module *M) {
196480093f4SDimitry Andric   assert(M == M->getTopLevelModule() && "Expected top level module!");
197480093f4SDimitry Andric 
198*fe6060f1SDimitry Andric   // If this module has been handled already, just return its ID.
199*fe6060f1SDimitry Andric   auto ModI = MDC.ModularDeps.insert({M, ModuleDeps{}});
200*fe6060f1SDimitry Andric   if (!ModI.second)
201*fe6060f1SDimitry Andric     return ModI.first->second.ID;
202480093f4SDimitry Andric 
203480093f4SDimitry Andric   ModuleDeps &MD = ModI.first->second;
204480093f4SDimitry Andric 
205*fe6060f1SDimitry Andric   MD.ID.ModuleName = M->getFullModuleName();
206*fe6060f1SDimitry Andric   MD.ImportedByMainFile = DirectModularDeps.contains(M);
207*fe6060f1SDimitry Andric   MD.ImplicitModulePCMPath = std::string(M->getASTFile()->getName());
208*fe6060f1SDimitry Andric   MD.IsSystem = M->IsSystem;
209*fe6060f1SDimitry Andric 
210480093f4SDimitry Andric   const FileEntry *ModuleMap = Instance.getPreprocessor()
211480093f4SDimitry Andric                                    .getHeaderSearchInfo()
212480093f4SDimitry Andric                                    .getModuleMap()
213*fe6060f1SDimitry Andric                                    .getModuleMapFileForUniquing(M);
2145ffd83dbSDimitry Andric   MD.ClangModuleMapFile = std::string(ModuleMap ? ModuleMap->getName() : "");
215*fe6060f1SDimitry Andric 
216480093f4SDimitry Andric   serialization::ModuleFile *MF =
217480093f4SDimitry Andric       MDC.Instance.getASTReader()->getModuleManager().lookup(M->getASTFile());
218480093f4SDimitry Andric   MDC.Instance.getASTReader()->visitInputFiles(
219480093f4SDimitry Andric       *MF, true, true, [&](const serialization::InputFile &IF, bool isSystem) {
220*fe6060f1SDimitry Andric         // __inferred_module.map is the result of the way in which an implicit
221*fe6060f1SDimitry Andric         // module build handles inferred modules. It adds an overlay VFS with
222*fe6060f1SDimitry Andric         // this file in the proper directory and relies on the rest of Clang to
223*fe6060f1SDimitry Andric         // handle it like normal. With explicitly built modules we don't need
224*fe6060f1SDimitry Andric         // to play VFS tricks, so replace it with the correct module map.
225*fe6060f1SDimitry Andric         if (IF.getFile()->getName().endswith("__inferred_module.map")) {
226*fe6060f1SDimitry Andric           MD.FileDeps.insert(ModuleMap->getName());
227*fe6060f1SDimitry Andric           return;
228*fe6060f1SDimitry Andric         }
229480093f4SDimitry Andric         MD.FileDeps.insert(IF.getFile()->getName());
230480093f4SDimitry Andric       });
231480093f4SDimitry Andric 
232*fe6060f1SDimitry Andric   // Add direct prebuilt module dependencies now, so that we can use them when
233*fe6060f1SDimitry Andric   // creating a CompilerInvocation and computing context hash for this
234*fe6060f1SDimitry Andric   // ModuleDeps instance.
235*fe6060f1SDimitry Andric   addDirectPrebuiltModuleDeps(M, MD);
236*fe6060f1SDimitry Andric 
237*fe6060f1SDimitry Andric   MD.Invocation = MDC.makeInvocationForModuleBuildWithoutPaths(MD);
238*fe6060f1SDimitry Andric   MD.ID.ContextHash = MD.Invocation.getModuleHash();
239*fe6060f1SDimitry Andric 
2405ffd83dbSDimitry Andric   llvm::DenseSet<const Module *> AddedModules;
2415ffd83dbSDimitry Andric   addAllSubmoduleDeps(M, MD, AddedModules);
242*fe6060f1SDimitry Andric 
243*fe6060f1SDimitry Andric   return MD.ID;
244*fe6060f1SDimitry Andric }
245*fe6060f1SDimitry Andric 
246*fe6060f1SDimitry Andric void ModuleDepCollectorPP::addDirectPrebuiltModuleDeps(const Module *M,
247*fe6060f1SDimitry Andric                                                        ModuleDeps &MD) {
248*fe6060f1SDimitry Andric   for (const Module *Import : M->Imports)
249*fe6060f1SDimitry Andric     if (Import->getTopLevelModule() != M->getTopLevelModule())
250*fe6060f1SDimitry Andric       if (MDC.isPrebuiltModule(Import))
251*fe6060f1SDimitry Andric         MD.PrebuiltModuleDeps.emplace_back(Import);
252480093f4SDimitry Andric }
253480093f4SDimitry Andric 
2545ffd83dbSDimitry Andric void ModuleDepCollectorPP::addAllSubmoduleDeps(
2555ffd83dbSDimitry Andric     const Module *M, ModuleDeps &MD,
2565ffd83dbSDimitry Andric     llvm::DenseSet<const Module *> &AddedModules) {
2575ffd83dbSDimitry Andric   addModuleDep(M, MD, AddedModules);
258480093f4SDimitry Andric 
259480093f4SDimitry Andric   for (const Module *SubM : M->submodules())
2605ffd83dbSDimitry Andric     addAllSubmoduleDeps(SubM, MD, AddedModules);
261480093f4SDimitry Andric }
262480093f4SDimitry Andric 
2635ffd83dbSDimitry Andric void ModuleDepCollectorPP::addModuleDep(
2645ffd83dbSDimitry Andric     const Module *M, ModuleDeps &MD,
2655ffd83dbSDimitry Andric     llvm::DenseSet<const Module *> &AddedModules) {
266480093f4SDimitry Andric   for (const Module *Import : M->Imports) {
267*fe6060f1SDimitry Andric     if (Import->getTopLevelModule() != M->getTopLevelModule() &&
268*fe6060f1SDimitry Andric         !MDC.isPrebuiltModule(Import)) {
269*fe6060f1SDimitry Andric       ModuleID ImportID = handleTopLevelModule(Import->getTopLevelModule());
2705ffd83dbSDimitry Andric       if (AddedModules.insert(Import->getTopLevelModule()).second)
271*fe6060f1SDimitry Andric         MD.ClangModuleDeps.push_back(ImportID);
272480093f4SDimitry Andric     }
273480093f4SDimitry Andric   }
274480093f4SDimitry Andric }
275480093f4SDimitry Andric 
2765ffd83dbSDimitry Andric ModuleDepCollector::ModuleDepCollector(
2775ffd83dbSDimitry Andric     std::unique_ptr<DependencyOutputOptions> Opts, CompilerInstance &I,
278*fe6060f1SDimitry Andric     DependencyConsumer &C, CompilerInvocation &&OriginalCI)
279*fe6060f1SDimitry Andric     : Instance(I), Consumer(C), Opts(std::move(Opts)),
280*fe6060f1SDimitry Andric       OriginalInvocation(std::move(OriginalCI)) {}
281480093f4SDimitry Andric 
282480093f4SDimitry Andric void ModuleDepCollector::attachToPreprocessor(Preprocessor &PP) {
283480093f4SDimitry Andric   PP.addPPCallbacks(std::make_unique<ModuleDepCollectorPP>(Instance, *this));
284480093f4SDimitry Andric }
285480093f4SDimitry Andric 
286480093f4SDimitry Andric void ModuleDepCollector::attachToASTReader(ASTReader &R) {}
287*fe6060f1SDimitry Andric 
288*fe6060f1SDimitry Andric bool ModuleDepCollector::isPrebuiltModule(const Module *M) {
289*fe6060f1SDimitry Andric   std::string Name(M->getTopLevelModuleName());
290*fe6060f1SDimitry Andric   const auto &PrebuiltModuleFiles =
291*fe6060f1SDimitry Andric       Instance.getHeaderSearchOpts().PrebuiltModuleFiles;
292*fe6060f1SDimitry Andric   auto PrebuiltModuleFileIt = PrebuiltModuleFiles.find(Name);
293*fe6060f1SDimitry Andric   if (PrebuiltModuleFileIt == PrebuiltModuleFiles.end())
294*fe6060f1SDimitry Andric     return false;
295*fe6060f1SDimitry Andric   assert("Prebuilt module came from the expected AST file" &&
296*fe6060f1SDimitry Andric          PrebuiltModuleFileIt->second == M->getASTFile()->getName());
297*fe6060f1SDimitry Andric   return true;
298*fe6060f1SDimitry Andric }
299