1 /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2 file Copyright.txt or https://cmake.org/licensing for details. */
3 #include "cmCxxModuleMapper.h"
13 #include <cm/string_view>
14 #include <cmext/string_view>
16 #include "cmScanDepFormat.h"
17 #include "cmStringAlgorithms.h"
18 #include "cmSystemTools.h"
20 cm::optional<std::string> CxxModuleLocations::BmiGeneratorPathForModule(
21 std::string const& logical_name) const
23 if (auto l = this->BmiLocationForModule(logical_name)) {
24 return this->PathForGenerator(*l);
31 std::string CxxModuleMapContentGcc(CxxModuleLocations const& loc,
32 cmScanDepInfo const& obj)
36 // Documented in GCC's documentation. The format is a series of
37 // lines with a module name and the associated filename separated
38 // by spaces. The first line may use `$root` as the module name
39 // to specify a "repository root". That is used to anchor any
40 // relative paths present in the file (CMake should never
43 // Write the root directory to use for module paths.
44 mm << "$root " << loc.RootDirectory << "\n";
46 for (auto const& p : obj.Provides) {
47 if (auto bmi_loc = loc.BmiGeneratorPathForModule(p.LogicalName)) {
48 mm << p.LogicalName << ' ' << *bmi_loc << '\n';
51 for (auto const& r : obj.Requires) {
52 if (auto bmi_loc = loc.BmiGeneratorPathForModule(r.LogicalName)) {
53 mm << r.LogicalName << ' ' << *bmi_loc << '\n';
60 std::string CxxModuleMapContentMsvc(CxxModuleLocations const& loc,
61 cmScanDepInfo const& obj,
62 CxxModuleUsage const& usages)
66 // A response file of `-reference NAME=PATH` arguments.
68 // MSVC's command line only supports a single output. If more than one is
69 // expected, we cannot make a useful module map file.
70 if (obj.Provides.size() > 1) {
74 auto flag_for_method = [](LookupMethod method) -> cm::static_string_view {
76 case LookupMethod::ByName:
77 return "-reference"_s;
78 case LookupMethod::IncludeAngle:
79 return "-headerUnit:angle"_s;
80 case LookupMethod::IncludeQuote:
81 return "-headerUnit:quote"_s;
83 assert(false && "unsupported lookup method");
87 for (auto const& p : obj.Provides) {
91 mm << "-internalPartition\n";
94 if (auto bmi_loc = loc.BmiGeneratorPathForModule(p.LogicalName)) {
95 mm << "-ifcOutput " << *bmi_loc << '\n';
99 std::set<std::string> transitive_usage_directs;
100 std::set<std::string> transitive_usage_names;
102 for (auto const& r : obj.Requires) {
103 if (auto bmi_loc = loc.BmiGeneratorPathForModule(r.LogicalName)) {
104 auto flag = flag_for_method(r.Method);
106 mm << flag << ' ' << r.LogicalName << '=' << *bmi_loc << "\n";
107 transitive_usage_directs.insert(r.LogicalName);
109 // Insert transitive usages.
110 auto transitive_usages = usages.Usage.find(r.LogicalName);
111 if (transitive_usages != usages.Usage.end()) {
112 transitive_usage_names.insert(transitive_usages->second.begin(),
113 transitive_usages->second.end());
118 for (auto const& transitive_name : transitive_usage_names) {
119 if (transitive_usage_directs.count(transitive_name)) {
123 auto module_ref = usages.Reference.find(transitive_name);
124 if (module_ref != usages.Reference.end()) {
125 auto flag = flag_for_method(module_ref->second.Method);
126 mm << flag << ' ' << transitive_name << '=' << module_ref->second.Path
135 bool CxxModuleUsage::AddReference(std::string const& logical,
136 std::string const& loc, LookupMethod method)
138 auto r = this->Reference.find(logical);
139 if (r != this->Reference.end()) {
140 auto& ref = r->second;
142 if (ref.Path == loc && ref.Method == method) {
146 auto method_name = [](LookupMethod m) -> cm::static_string_view {
148 case LookupMethod::ByName:
150 case LookupMethod::IncludeAngle:
151 return "include-angle"_s;
152 case LookupMethod::IncludeQuote:
153 return "include-quote"_s;
155 assert(false && "unsupported lookup method");
159 cmSystemTools::Error(cmStrCat("Disagreement of the location of the '",
163 ref.Path, "' via ", method_name(ref.Method),
166 loc, "' via ", method_name(method), "."));
170 auto& ref = this->Reference[logical];
177 cm::static_string_view CxxModuleMapExtension(
178 cm::optional<CxxModuleMapFormat> format)
182 case CxxModuleMapFormat::Gcc:
184 case CxxModuleMapFormat::Msvc:
192 std::set<std::string> CxxModuleUsageSeed(
193 CxxModuleLocations const& loc, std::vector<cmScanDepInfo> const& objects,
194 CxxModuleUsage& usages)
196 // Track inner usages to populate usages from internal bits.
198 // This is a map of modules that required some other module that was not
199 // found to those that were not found.
200 std::map<std::string, std::set<std::string>> internal_usages;
201 std::set<std::string> unresolved;
203 for (cmScanDepInfo const& object : objects) {
204 // Add references for each of the provided modules.
205 for (auto const& p : object.Provides) {
206 if (auto bmi_loc = loc.BmiGeneratorPathForModule(p.LogicalName)) {
207 // XXX(cxx-modules): How to support header units?
208 usages.AddReference(p.LogicalName, loc.PathForGenerator(*bmi_loc),
209 LookupMethod::ByName);
213 // For each requires, pull in what is required.
214 for (auto const& r : object.Requires) {
215 // Find transitive usages.
216 auto transitive_usages = usages.Usage.find(r.LogicalName);
217 // Find the required name in the current target.
218 auto bmi_loc = loc.BmiGeneratorPathForModule(r.LogicalName);
220 for (auto const& p : object.Provides) {
221 auto& this_usages = usages.Usage[p.LogicalName];
223 // Add the direct usage.
224 this_usages.insert(r.LogicalName);
226 // Add the transitive usage.
227 if (transitive_usages != usages.Usage.end()) {
228 this_usages.insert(transitive_usages->second.begin(),
229 transitive_usages->second.end());
230 } else if (bmi_loc) {
231 // Mark that we need to update transitive usages later.
232 internal_usages[p.LogicalName].insert(r.LogicalName);
237 usages.AddReference(r.LogicalName, loc.PathForGenerator(*bmi_loc),
243 // While we have internal usages to manage.
244 while (!internal_usages.empty()) {
245 size_t starting_size = internal_usages.size();
247 // For each internal usage.
248 for (auto usage = internal_usages.begin(); usage != internal_usages.end();
249 /* see end of loop */) {
250 auto& this_usages = usages.Usage[usage->first];
252 for (auto use = usage->second.begin(); use != usage->second.end();
253 /* see end of loop */) {
254 // Check if this required module uses other internal modules; defer
256 if (internal_usages.count(*use)) {
257 // Advance the iterator.
262 auto transitive_usages = usages.Usage.find(*use);
263 if (transitive_usages != usages.Usage.end()) {
264 this_usages.insert(transitive_usages->second.begin(),
265 transitive_usages->second.end());
268 // Remove the entry and advance the iterator.
269 use = usage->second.erase(use);
272 // Erase the entry if it doesn't have any remaining usages.
273 if (usage->second.empty()) {
274 usage = internal_usages.erase(usage);
280 // Check that at least one usage was resolved.
281 if (starting_size == internal_usages.size()) {
282 // Nothing could be resolved this loop; we have a cycle, so record the
284 for (auto const& usage : internal_usages) {
285 unresolved.insert(usage.first);
294 std::string CxxModuleMapContent(CxxModuleMapFormat format,
295 CxxModuleLocations const& loc,
296 cmScanDepInfo const& obj,
297 CxxModuleUsage const& usages)
300 case CxxModuleMapFormat::Gcc:
301 return CxxModuleMapContentGcc(loc, obj);
302 case CxxModuleMapFormat::Msvc:
303 return CxxModuleMapContentMsvc(loc, obj, usages);