summaryrefslogtreecommitdiff
path: root/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2021-07-29 20:15:26 +0000
committerDimitry Andric <dim@FreeBSD.org>2021-07-29 20:15:26 +0000
commit344a3780b2e33f6ca763666c380202b18aab72a3 (patch)
treef0b203ee6eb71d7fdd792373e3c81eb18d6934dd /clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp
parentb60736ec1405bb0a8dd40989f67ef4c93da068ab (diff)
Diffstat (limited to 'clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp')
-rw-r--r--clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp209
1 files changed, 157 insertions, 52 deletions
diff --git a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp
index f74ce7304df5..88cee63c98aa 100644
--- a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp
+++ b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp
@@ -12,49 +12,97 @@
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Tooling/DependencyScanning/DependencyScanningWorker.h"
+#include "llvm/Support/StringSaver.h"
using namespace clang;
using namespace tooling;
using namespace dependencies;
-std::vector<std::string> ModuleDeps::getFullCommandLine(
- std::function<StringRef(ClangModuleDep)> LookupPCMPath,
- std::function<const ModuleDeps &(ClangModuleDep)> LookupModuleDeps) const {
- std::vector<std::string> Ret = NonPathCommandLine;
+CompilerInvocation ModuleDepCollector::makeInvocationForModuleBuildWithoutPaths(
+ const ModuleDeps &Deps) const {
+ // Make a deep copy of the original Clang invocation.
+ CompilerInvocation CI(OriginalInvocation);
- // TODO: Build full command line. That also means capturing the original
- // command line into NonPathCommandLine.
+ // Remove options incompatible with explicit module build.
+ CI.getFrontendOpts().Inputs.clear();
+ CI.getFrontendOpts().OutputFile.clear();
- dependencies::detail::appendCommonModuleArguments(
- ClangModuleDeps, LookupPCMPath, LookupModuleDeps, Ret);
+ CI.getFrontendOpts().ProgramAction = frontend::GenerateModule;
+ CI.getLangOpts()->ModuleName = Deps.ID.ModuleName;
+ CI.getFrontendOpts().IsSystemModule = Deps.IsSystem;
- return Ret;
+ CI.getLangOpts()->ImplicitModules = false;
+
+ // Report the prebuilt modules this module uses.
+ for (const auto &PrebuiltModule : Deps.PrebuiltModuleDeps) {
+ CI.getFrontendOpts().ModuleFiles.push_back(PrebuiltModule.PCMFile);
+ CI.getFrontendOpts().ModuleMapFiles.push_back(PrebuiltModule.ModuleMapFile);
+ }
+
+ CI.getPreprocessorOpts().ImplicitPCHInclude.clear();
+
+ return CI;
}
-void dependencies::detail::appendCommonModuleArguments(
- llvm::ArrayRef<ClangModuleDep> Modules,
- std::function<StringRef(ClangModuleDep)> LookupPCMPath,
- std::function<const ModuleDeps &(ClangModuleDep)> LookupModuleDeps,
- std::vector<std::string> &Result) {
+static std::vector<std::string>
+serializeCompilerInvocation(const CompilerInvocation &CI) {
+ // Set up string allocator.
+ llvm::BumpPtrAllocator Alloc;
+ llvm::StringSaver Strings(Alloc);
+ auto SA = [&Strings](const Twine &Arg) { return Strings.save(Arg).data(); };
+
+ // Synthesize full command line from the CompilerInvocation, including "-cc1".
+ SmallVector<const char *, 32> Args{"-cc1"};
+ CI.generateCC1CommandLine(Args, SA);
+
+ // Convert arguments to the return type.
+ return std::vector<std::string>{Args.begin(), Args.end()};
+}
+
+std::vector<std::string> ModuleDeps::getCanonicalCommandLine(
+ std::function<StringRef(ModuleID)> LookupPCMPath,
+ std::function<const ModuleDeps &(ModuleID)> LookupModuleDeps) const {
+ CompilerInvocation CI(Invocation);
+ FrontendOptions &FrontendOpts = CI.getFrontendOpts();
+
+ InputKind ModuleMapInputKind(FrontendOpts.DashX.getLanguage(),
+ InputKind::Format::ModuleMap);
+ FrontendOpts.Inputs.emplace_back(ClangModuleMapFile, ModuleMapInputKind);
+ FrontendOpts.OutputFile = std::string(LookupPCMPath(ID));
+
+ dependencies::detail::collectPCMAndModuleMapPaths(
+ ClangModuleDeps, LookupPCMPath, LookupModuleDeps,
+ FrontendOpts.ModuleFiles, FrontendOpts.ModuleMapFiles);
+
+ return serializeCompilerInvocation(CI);
+}
+
+std::vector<std::string>
+ModuleDeps::getCanonicalCommandLineWithoutModulePaths() const {
+ return serializeCompilerInvocation(Invocation);
+}
+
+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<ClangModuleDep>)> AddArgs =
- [&](llvm::ArrayRef<ClangModuleDep> Modules) {
- for (const ClangModuleDep &CMD : Modules) {
- if (!AlreadyAdded.insert(CMD.ModuleName + CMD.ContextHash).second)
+ 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(CMD);
+ const ModuleDeps &M = LookupModuleDeps(MID);
// Depth first traversal.
AddArgs(M.ClangModuleDeps);
- Result.push_back(("-fmodule-file=" + LookupPCMPath(CMD)).str());
- if (!M.ClangModuleMapFile.empty()) {
- Result.push_back("-fmodule-map-file=" + M.ClangModuleMapFile);
- }
+ PCMPaths.push_back(LookupPCMPath(MID).str());
+ if (!M.ClangModuleMapFile.empty())
+ ModMapPaths.push_back(M.ClangModuleMapFile);
}
};
- Result.push_back("-fno-implicit-modules");
- Result.push_back("-fno-implicit-module-maps");
AddArgs(Modules);
}
@@ -79,7 +127,7 @@ void ModuleDepCollectorPP::FileChanged(SourceLocation Loc,
// We do not want #line markers to affect dependency generation!
if (Optional<StringRef> Filename =
SM.getNonBuiltinFilenameForID(SM.getFileID(SM.getExpansionLoc(Loc))))
- MDC.MainDeps.push_back(
+ MDC.FileDeps.push_back(
std::string(llvm::sys::path::remove_leading_dotslash(*Filename)));
}
@@ -91,7 +139,7 @@ void ModuleDepCollectorPP::InclusionDirective(
if (!File && !Imported) {
// This is a non-modular include that HeaderSearch failed to find. Add it
// here as `FileChanged` will never see it.
- MDC.MainDeps.push_back(std::string(FileName));
+ MDC.FileDeps.push_back(std::string(FileName));
}
handleImport(Imported);
}
@@ -106,9 +154,12 @@ void ModuleDepCollectorPP::handleImport(const Module *Imported) {
if (!Imported)
return;
- MDC.Deps[MDC.ContextHash + Imported->getTopLevelModule()->getFullModuleName()]
- .ImportedByMainFile = true;
- DirectDeps.insert(Imported->getTopLevelModule());
+ const Module *TopLevelModule = Imported->getTopLevelModule();
+
+ if (MDC.isPrebuiltModule(TopLevelModule))
+ DirectPrebuiltModularDeps.insert(TopLevelModule);
+ else
+ DirectModularDeps.insert(TopLevelModule);
}
void ModuleDepCollectorPP::EndOfMainFile() {
@@ -116,46 +167,88 @@ void ModuleDepCollectorPP::EndOfMainFile() {
MDC.MainFile = std::string(
Instance.getSourceManager().getFileEntryForID(MainFileID)->getName());
- for (const Module *M : DirectDeps) {
+ if (!Instance.getPreprocessorOpts().ImplicitPCHInclude.empty())
+ MDC.FileDeps.push_back(Instance.getPreprocessorOpts().ImplicitPCHInclude);
+
+ for (const Module *M : DirectModularDeps) {
+ // A top-level module might not be actually imported as a module when
+ // -fmodule-name is used to compile a translation unit that imports this
+ // module. In that case it can be skipped. The appropriate header
+ // dependencies will still be reported as expected.
+ if (!M->getASTFile())
+ continue;
handleTopLevelModule(M);
}
- for (auto &&I : MDC.Deps)
+ MDC.Consumer.handleDependencyOutputOpts(*MDC.Opts);
+
+ for (auto &&I : MDC.ModularDeps)
MDC.Consumer.handleModuleDependency(I.second);
- for (auto &&I : MDC.MainDeps)
- MDC.Consumer.handleFileDependency(*MDC.Opts, I);
+ for (auto &&I : MDC.FileDeps)
+ MDC.Consumer.handleFileDependency(I);
+
+ for (auto &&I : DirectPrebuiltModularDeps)
+ MDC.Consumer.handlePrebuiltModuleDependency(PrebuiltModuleDep{I});
}
-void ModuleDepCollectorPP::handleTopLevelModule(const Module *M) {
+ModuleID ModuleDepCollectorPP::handleTopLevelModule(const Module *M) {
assert(M == M->getTopLevelModule() && "Expected top level module!");
- auto ModI = MDC.Deps.insert(
- std::make_pair(MDC.ContextHash + M->getFullModuleName(), ModuleDeps{}));
-
- if (!ModI.first->second.ModuleName.empty())
- return;
+ // If this module has been handled already, just return its ID.
+ auto ModI = MDC.ModularDeps.insert({M, ModuleDeps{}});
+ if (!ModI.second)
+ return ModI.first->second.ID;
ModuleDeps &MD = ModI.first->second;
+ MD.ID.ModuleName = M->getFullModuleName();
+ MD.ImportedByMainFile = DirectModularDeps.contains(M);
+ MD.ImplicitModulePCMPath = std::string(M->getASTFile()->getName());
+ MD.IsSystem = M->IsSystem;
+
const FileEntry *ModuleMap = Instance.getPreprocessor()
.getHeaderSearchInfo()
.getModuleMap()
- .getContainingModuleMapFile(M);
-
+ .getModuleMapFileForUniquing(M);
MD.ClangModuleMapFile = std::string(ModuleMap ? ModuleMap->getName() : "");
- MD.ModuleName = M->getFullModuleName();
- MD.ImplicitModulePCMPath = std::string(M->getASTFile()->getName());
- MD.ContextHash = MDC.ContextHash;
+
serialization::ModuleFile *MF =
MDC.Instance.getASTReader()->getModuleManager().lookup(M->getASTFile());
MDC.Instance.getASTReader()->visitInputFiles(
*MF, true, true, [&](const serialization::InputFile &IF, bool isSystem) {
+ // __inferred_module.map is the result of the way in which an implicit
+ // module build handles inferred modules. It adds an overlay VFS with
+ // this file in the proper directory and relies on the rest of Clang to
+ // handle it like normal. With explicitly built modules we don't need
+ // to play VFS tricks, so replace it with the correct module map.
+ if (IF.getFile()->getName().endswith("__inferred_module.map")) {
+ MD.FileDeps.insert(ModuleMap->getName());
+ return;
+ }
MD.FileDeps.insert(IF.getFile()->getName());
});
+ // Add direct prebuilt module dependencies now, so that we can use them when
+ // creating a CompilerInvocation and computing context hash for this
+ // ModuleDeps instance.
+ addDirectPrebuiltModuleDeps(M, MD);
+
+ MD.Invocation = MDC.makeInvocationForModuleBuildWithoutPaths(MD);
+ MD.ID.ContextHash = MD.Invocation.getModuleHash();
+
llvm::DenseSet<const Module *> AddedModules;
addAllSubmoduleDeps(M, MD, AddedModules);
+
+ return MD.ID;
+}
+
+void ModuleDepCollectorPP::addDirectPrebuiltModuleDeps(const Module *M,
+ ModuleDeps &MD) {
+ for (const Module *Import : M->Imports)
+ if (Import->getTopLevelModule() != M->getTopLevelModule())
+ if (MDC.isPrebuiltModule(Import))
+ MD.PrebuiltModuleDeps.emplace_back(Import);
}
void ModuleDepCollectorPP::addAllSubmoduleDeps(
@@ -171,23 +264,35 @@ void ModuleDepCollectorPP::addModuleDep(
const Module *M, ModuleDeps &MD,
llvm::DenseSet<const Module *> &AddedModules) {
for (const Module *Import : M->Imports) {
- if (Import->getTopLevelModule() != M->getTopLevelModule()) {
+ if (Import->getTopLevelModule() != M->getTopLevelModule() &&
+ !MDC.isPrebuiltModule(Import)) {
+ ModuleID ImportID = handleTopLevelModule(Import->getTopLevelModule());
if (AddedModules.insert(Import->getTopLevelModule()).second)
- MD.ClangModuleDeps.push_back(
- {std::string(Import->getTopLevelModuleName()),
- Instance.getInvocation().getModuleHash()});
- handleTopLevelModule(Import->getTopLevelModule());
+ MD.ClangModuleDeps.push_back(ImportID);
}
}
}
ModuleDepCollector::ModuleDepCollector(
std::unique_ptr<DependencyOutputOptions> Opts, CompilerInstance &I,
- DependencyConsumer &C)
- : Instance(I), Consumer(C), Opts(std::move(Opts)) {}
+ DependencyConsumer &C, CompilerInvocation &&OriginalCI)
+ : Instance(I), Consumer(C), Opts(std::move(Opts)),
+ OriginalInvocation(std::move(OriginalCI)) {}
void ModuleDepCollector::attachToPreprocessor(Preprocessor &PP) {
PP.addPPCallbacks(std::make_unique<ModuleDepCollectorPP>(Instance, *this));
}
void ModuleDepCollector::attachToASTReader(ASTReader &R) {}
+
+bool ModuleDepCollector::isPrebuiltModule(const Module *M) {
+ std::string Name(M->getTopLevelModuleName());
+ const auto &PrebuiltModuleFiles =
+ Instance.getHeaderSearchOpts().PrebuiltModuleFiles;
+ auto PrebuiltModuleFileIt = PrebuiltModuleFiles.find(Name);
+ if (PrebuiltModuleFileIt == PrebuiltModuleFiles.end())
+ return false;
+ assert("Prebuilt module came from the expected AST file" &&
+ PrebuiltModuleFileIt->second == M->getASTFile()->getName());
+ return true;
+}