resolve cyclic dependency with zstd
[platform/upstream/cmake.git] / Source / cmCxxModuleMapper.cxx
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"
4
5 #include <cassert>
6 #include <cstddef>
7 #include <set>
8 #include <sstream>
9 #include <string>
10 #include <utility>
11 #include <vector>
12
13 #include <cm/string_view>
14 #include <cmext/string_view>
15
16 #include "cmScanDepFormat.h"
17 #include "cmStringAlgorithms.h"
18 #include "cmSystemTools.h"
19
20 cm::optional<std::string> CxxModuleLocations::BmiGeneratorPathForModule(
21   std::string const& logical_name) const
22 {
23   if (auto l = this->BmiLocationForModule(logical_name)) {
24     return this->PathForGenerator(*l);
25   }
26   return {};
27 }
28
29 namespace {
30
31 std::string CxxModuleMapContentGcc(CxxModuleLocations const& loc,
32                                    cmScanDepInfo const& obj)
33 {
34   std::stringstream mm;
35
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
41   // generate any).
42
43   // Write the root directory to use for module paths.
44   mm << "$root " << loc.RootDirectory << "\n";
45
46   for (auto const& p : obj.Provides) {
47     if (auto bmi_loc = loc.BmiGeneratorPathForModule(p.LogicalName)) {
48       mm << p.LogicalName << ' ' << *bmi_loc << '\n';
49     }
50   }
51   for (auto const& r : obj.Requires) {
52     if (auto bmi_loc = loc.BmiGeneratorPathForModule(r.LogicalName)) {
53       mm << r.LogicalName << ' ' << *bmi_loc << '\n';
54     }
55   }
56
57   return mm.str();
58 }
59
60 std::string CxxModuleMapContentMsvc(CxxModuleLocations const& loc,
61                                     cmScanDepInfo const& obj,
62                                     CxxModuleUsage const& usages)
63 {
64   std::stringstream mm;
65
66   // A response file of `-reference NAME=PATH` arguments.
67
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) {
71     return {};
72   }
73
74   auto flag_for_method = [](LookupMethod method) -> cm::static_string_view {
75     switch (method) {
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;
82     }
83     assert(false && "unsupported lookup method");
84     return ""_s;
85   };
86
87   for (auto const& p : obj.Provides) {
88     if (p.IsInterface) {
89       mm << "-interface\n";
90     } else {
91       mm << "-internalPartition\n";
92     }
93
94     if (auto bmi_loc = loc.BmiGeneratorPathForModule(p.LogicalName)) {
95       mm << "-ifcOutput " << *bmi_loc << '\n';
96     }
97   }
98
99   std::set<std::string> transitive_usage_directs;
100   std::set<std::string> transitive_usage_names;
101
102   for (auto const& r : obj.Requires) {
103     if (auto bmi_loc = loc.BmiGeneratorPathForModule(r.LogicalName)) {
104       auto flag = flag_for_method(r.Method);
105
106       mm << flag << ' ' << r.LogicalName << '=' << *bmi_loc << "\n";
107       transitive_usage_directs.insert(r.LogicalName);
108
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());
114       }
115     }
116   }
117
118   for (auto const& transitive_name : transitive_usage_names) {
119     if (transitive_usage_directs.count(transitive_name)) {
120       continue;
121     }
122
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
127          << "\n";
128     }
129   }
130
131   return mm.str();
132 }
133 }
134
135 bool CxxModuleUsage::AddReference(std::string const& logical,
136                                   std::string const& loc, LookupMethod method)
137 {
138   auto r = this->Reference.find(logical);
139   if (r != this->Reference.end()) {
140     auto& ref = r->second;
141
142     if (ref.Path == loc && ref.Method == method) {
143       return true;
144     }
145
146     auto method_name = [](LookupMethod m) -> cm::static_string_view {
147       switch (m) {
148         case LookupMethod::ByName:
149           return "by-name"_s;
150         case LookupMethod::IncludeAngle:
151           return "include-angle"_s;
152         case LookupMethod::IncludeQuote:
153           return "include-quote"_s;
154       }
155       assert(false && "unsupported lookup method");
156       return ""_s;
157     };
158
159     cmSystemTools::Error(cmStrCat("Disagreement of the location of the '",
160                                   logical,
161                                   "' module. "
162                                   "Location A: '",
163                                   ref.Path, "' via ", method_name(ref.Method),
164                                   "; "
165                                   "Location B: '",
166                                   loc, "' via ", method_name(method), "."));
167     return false;
168   }
169
170   auto& ref = this->Reference[logical];
171   ref.Path = loc;
172   ref.Method = method;
173
174   return true;
175 }
176
177 cm::static_string_view CxxModuleMapExtension(
178   cm::optional<CxxModuleMapFormat> format)
179 {
180   if (format) {
181     switch (*format) {
182       case CxxModuleMapFormat::Gcc:
183         return ".gcm"_s;
184       case CxxModuleMapFormat::Msvc:
185         return ".ifc"_s;
186     }
187   }
188
189   return ".bmi"_s;
190 }
191
192 std::set<std::string> CxxModuleUsageSeed(
193   CxxModuleLocations const& loc, std::vector<cmScanDepInfo> const& objects,
194   CxxModuleUsage& usages)
195 {
196   // Track inner usages to populate usages from internal bits.
197   //
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;
202
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);
210       }
211     }
212
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);
219
220       for (auto const& p : object.Provides) {
221         auto& this_usages = usages.Usage[p.LogicalName];
222
223         // Add the direct usage.
224         this_usages.insert(r.LogicalName);
225
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);
233         }
234       }
235
236       if (bmi_loc) {
237         usages.AddReference(r.LogicalName, loc.PathForGenerator(*bmi_loc),
238                             r.Method);
239       }
240     }
241   }
242
243   // While we have internal usages to manage.
244   while (!internal_usages.empty()) {
245     size_t starting_size = internal_usages.size();
246
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];
251
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
255         // if so.
256         if (internal_usages.count(*use)) {
257           // Advance the iterator.
258           ++use;
259           continue;
260         }
261
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());
266         }
267
268         // Remove the entry and advance the iterator.
269         use = usage->second.erase(use);
270       }
271
272       // Erase the entry if it doesn't have any remaining usages.
273       if (usage->second.empty()) {
274         usage = internal_usages.erase(usage);
275       } else {
276         ++usage;
277       }
278     }
279
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
283       // cycle and exit.
284       for (auto const& usage : internal_usages) {
285         unresolved.insert(usage.first);
286       }
287       break;
288     }
289   }
290
291   return unresolved;
292 }
293
294 std::string CxxModuleMapContent(CxxModuleMapFormat format,
295                                 CxxModuleLocations const& loc,
296                                 cmScanDepInfo const& obj,
297                                 CxxModuleUsage const& usages)
298 {
299   switch (format) {
300     case CxxModuleMapFormat::Gcc:
301       return CxxModuleMapContentGcc(loc, obj);
302     case CxxModuleMapFormat::Msvc:
303       return CxxModuleMapContentMsvc(loc, obj, usages);
304   }
305
306   assert(false);
307   return {};
308 }