cmCxxModuleMapper: track transitive modules for MSVC

MSVC needs the transitive closure of module usage to compile.
This commit is contained in:
Ben Boeckel
2022-07-19 16:00:58 -04:00
parent a43713d615
commit b3c2880cb2
3 changed files with 278 additions and 6 deletions

View File

@@ -3,10 +3,17 @@
#include "cmCxxModuleMapper.h"
#include <cassert>
#include <cstddef>
#include <sstream>
#include <utility>
#include <vector>
#include <cm/string_view>
#include <cmext/string_view>
#include "cmScanDepFormat.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
cm::optional<std::string> CxxModuleLocations::BmiGeneratorPathForModule(
std::string const& logical_name) const
@@ -49,6 +56,48 @@ std::string CxxModuleMapContentGcc(CxxModuleLocations const& loc,
}
}
bool CxxModuleUsage::AddReference(std::string const& logical,
std::string const& loc, LookupMethod method)
{
auto r = this->Reference.find(logical);
if (r != this->Reference.end()) {
auto& ref = r->second;
if (ref.Path == loc && ref.Method == method) {
return true;
}
auto method_name = [](LookupMethod m) -> cm::static_string_view {
switch (m) {
case LookupMethod::ByName:
return "by-name"_s;
case LookupMethod::IncludeAngle:
return "include-angle"_s;
case LookupMethod::IncludeQuote:
return "include-quote"_s;
}
assert(false && "unsupported lookup method");
return ""_s;
};
cmSystemTools::Error(cmStrCat("Disagreement of the location of the '",
logical,
"' module. "
"Location A: '",
ref.Path, "' via ", method_name(ref.Method),
"; "
"Location B: '",
loc, "' via ", method_name(method), "."));
return false;
}
auto& ref = this->Reference[logical];
ref.Path = loc;
ref.Method = method;
return true;
}
cm::static_string_view CxxModuleMapExtension(
cm::optional<CxxModuleMapFormat> format)
{
@@ -62,6 +111,108 @@ cm::static_string_view CxxModuleMapExtension(
return ".bmi"_s;
}
std::set<std::string> CxxModuleUsageSeed(
CxxModuleLocations const& loc, std::vector<cmScanDepInfo> const& objects,
CxxModuleUsage& usages)
{
// Track inner usages to populate usages from internal bits.
//
// This is a map of modules that required some other module that was not
// found to those that were not found.
std::map<std::string, std::set<std::string>> internal_usages;
std::set<std::string> unresolved;
for (cmScanDepInfo const& object : objects) {
// Add references for each of the provided modules.
for (auto const& p : object.Provides) {
if (auto bmi_loc = loc.BmiGeneratorPathForModule(p.LogicalName)) {
// XXX(cxx-modules): How to support header units?
usages.AddReference(p.LogicalName, loc.PathForGenerator(*bmi_loc),
LookupMethod::ByName);
}
}
// For each requires, pull in what is required.
for (auto const& r : object.Requires) {
// Find transitive usages.
auto transitive_usages = usages.Usage.find(r.LogicalName);
// Find the required name in the current target.
auto bmi_loc = loc.BmiGeneratorPathForModule(r.LogicalName);
for (auto const& p : object.Provides) {
auto& this_usages = usages.Usage[p.LogicalName];
// Add the direct usage.
this_usages.insert(r.LogicalName);
// Add the transitive usage.
if (transitive_usages != usages.Usage.end()) {
this_usages.insert(transitive_usages->second.begin(),
transitive_usages->second.end());
} else if (bmi_loc) {
// Mark that we need to update transitive usages later.
internal_usages[p.LogicalName].insert(r.LogicalName);
}
}
if (bmi_loc) {
usages.AddReference(r.LogicalName, loc.PathForGenerator(*bmi_loc),
r.Method);
}
}
}
// While we have internal usages to manage.
while (!internal_usages.empty()) {
size_t starting_size = internal_usages.size();
// For each internal usage.
for (auto usage = internal_usages.begin(); usage != internal_usages.end();
/* see end of loop */) {
auto& this_usages = usages.Usage[usage->first];
for (auto use = usage->second.begin(); use != usage->second.end();
/* see end of loop */) {
// Check if this required module uses other internal modules; defer
// if so.
if (internal_usages.count(*use)) {
// Advance the iterator.
++use;
continue;
}
auto transitive_usages = usages.Usage.find(*use);
if (transitive_usages != usages.Usage.end()) {
this_usages.insert(transitive_usages->second.begin(),
transitive_usages->second.end());
}
// Remove the entry and advance the iterator.
use = usage->second.erase(use);
}
// Erase the entry if it doesn't have any remaining usages.
if (usage->second.empty()) {
usage = internal_usages.erase(usage);
} else {
++usage;
}
}
// Check that at least one usage was resolved.
if (starting_size == internal_usages.size()) {
// Nothing could be resolved this loop; we have a cycle, so record the
// cycle and exit.
for (auto const& usage : internal_usages) {
unresolved.insert(usage.first);
}
break;
}
}
return unresolved;
}
std::string CxxModuleMapContent(CxxModuleMapFormat format,
CxxModuleLocations const& loc,
cmScanDepInfo const& obj)