cmCxxModuleMapper.cxx 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  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. #include <cassert>
  5. #include <cstddef>
  6. #include <set>
  7. #include <sstream>
  8. #include <string>
  9. #include <utility>
  10. #include <vector>
  11. #include <cm/string_view>
  12. #include <cmext/string_view>
  13. #include "cmScanDepFormat.h"
  14. #include "cmStringAlgorithms.h"
  15. #include "cmSystemTools.h"
  16. cm::optional<std::string> CxxModuleLocations::BmiGeneratorPathForModule(
  17. std::string const& logical_name) const
  18. {
  19. if (auto l = this->BmiLocationForModule(logical_name)) {
  20. return this->PathForGenerator(*l);
  21. }
  22. return {};
  23. }
  24. namespace {
  25. std::string CxxModuleMapContentClang(CxxModuleLocations const& loc,
  26. cmScanDepInfo const& obj)
  27. {
  28. std::stringstream mm;
  29. // Clang's command line only supports a single output. If more than one is
  30. // expected, we cannot make a useful module map file.
  31. if (obj.Provides.size() > 1) {
  32. return {};
  33. }
  34. // A series of flags which tell the compiler where to look for modules.
  35. for (auto const& p : obj.Provides) {
  36. if (auto bmi_loc = loc.BmiGeneratorPathForModule(p.LogicalName)) {
  37. // Force the TU to be considered a C++ module source file regardless of
  38. // extension.
  39. mm << "-x c++-module\n";
  40. mm << "-fmodule-output=" << *bmi_loc << '\n';
  41. break;
  42. }
  43. }
  44. for (auto const& r : obj.Requires) {
  45. if (auto bmi_loc = loc.BmiGeneratorPathForModule(r.LogicalName)) {
  46. mm << "-fmodule-file=" << r.LogicalName << "=" << *bmi_loc << '\n';
  47. }
  48. }
  49. return mm.str();
  50. }
  51. std::string CxxModuleMapContentGcc(CxxModuleLocations const& loc,
  52. cmScanDepInfo const& obj)
  53. {
  54. std::stringstream mm;
  55. // Documented in GCC's documentation. The format is a series of
  56. // lines with a module name and the associated filename separated
  57. // by spaces. The first line may use `$root` as the module name
  58. // to specify a "repository root". That is used to anchor any
  59. // relative paths present in the file (CMake should never
  60. // generate any).
  61. // Write the root directory to use for module paths.
  62. mm << "$root " << loc.RootDirectory << "\n";
  63. for (auto const& p : obj.Provides) {
  64. if (auto bmi_loc = loc.BmiGeneratorPathForModule(p.LogicalName)) {
  65. mm << p.LogicalName << ' ' << *bmi_loc << '\n';
  66. }
  67. }
  68. for (auto const& r : obj.Requires) {
  69. if (auto bmi_loc = loc.BmiGeneratorPathForModule(r.LogicalName)) {
  70. mm << r.LogicalName << ' ' << *bmi_loc << '\n';
  71. }
  72. }
  73. return mm.str();
  74. }
  75. std::string CxxModuleMapContentMsvc(CxxModuleLocations const& loc,
  76. cmScanDepInfo const& obj,
  77. CxxModuleUsage const& usages)
  78. {
  79. std::stringstream mm;
  80. // A response file of `-reference NAME=PATH` arguments.
  81. // MSVC's command line only supports a single output. If more than one is
  82. // expected, we cannot make a useful module map file.
  83. if (obj.Provides.size() > 1) {
  84. return {};
  85. }
  86. auto flag_for_method = [](LookupMethod method) -> cm::static_string_view {
  87. switch (method) {
  88. case LookupMethod::ByName:
  89. return "-reference"_s;
  90. case LookupMethod::IncludeAngle:
  91. return "-headerUnit:angle"_s;
  92. case LookupMethod::IncludeQuote:
  93. return "-headerUnit:quote"_s;
  94. }
  95. assert(false && "unsupported lookup method");
  96. return ""_s;
  97. };
  98. for (auto const& p : obj.Provides) {
  99. if (p.IsInterface) {
  100. mm << "-interface\n";
  101. } else {
  102. mm << "-internalPartition\n";
  103. }
  104. if (auto bmi_loc = loc.BmiGeneratorPathForModule(p.LogicalName)) {
  105. mm << "-ifcOutput " << *bmi_loc << '\n';
  106. }
  107. }
  108. std::set<std::string> transitive_usage_directs;
  109. std::set<std::string> transitive_usage_names;
  110. for (auto const& r : obj.Requires) {
  111. if (auto bmi_loc = loc.BmiGeneratorPathForModule(r.LogicalName)) {
  112. auto flag = flag_for_method(r.Method);
  113. mm << flag << ' ' << r.LogicalName << '=' << *bmi_loc << "\n";
  114. transitive_usage_directs.insert(r.LogicalName);
  115. // Insert transitive usages.
  116. auto transitive_usages = usages.Usage.find(r.LogicalName);
  117. if (transitive_usages != usages.Usage.end()) {
  118. transitive_usage_names.insert(transitive_usages->second.begin(),
  119. transitive_usages->second.end());
  120. }
  121. }
  122. }
  123. for (auto const& transitive_name : transitive_usage_names) {
  124. if (transitive_usage_directs.count(transitive_name)) {
  125. continue;
  126. }
  127. auto module_ref = usages.Reference.find(transitive_name);
  128. if (module_ref != usages.Reference.end()) {
  129. auto flag = flag_for_method(module_ref->second.Method);
  130. mm << flag << ' ' << transitive_name << '=' << module_ref->second.Path
  131. << "\n";
  132. }
  133. }
  134. return mm.str();
  135. }
  136. }
  137. bool CxxModuleUsage::AddReference(std::string const& logical,
  138. std::string const& loc, LookupMethod method)
  139. {
  140. auto r = this->Reference.find(logical);
  141. if (r != this->Reference.end()) {
  142. auto& ref = r->second;
  143. if (ref.Path == loc && ref.Method == method) {
  144. return true;
  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. cmSystemTools::Error(cmStrCat("Disagreement of the location of the '",
  159. logical,
  160. "' module. "
  161. "Location A: '",
  162. ref.Path, "' via ", method_name(ref.Method),
  163. "; "
  164. "Location B: '",
  165. loc, "' via ", method_name(method), "."));
  166. return false;
  167. }
  168. auto& ref = this->Reference[logical];
  169. ref.Path = loc;
  170. ref.Method = method;
  171. return true;
  172. }
  173. cm::static_string_view CxxModuleMapExtension(
  174. cm::optional<CxxModuleMapFormat> format)
  175. {
  176. if (format) {
  177. switch (*format) {
  178. case CxxModuleMapFormat::Clang:
  179. return ".pcm"_s;
  180. case CxxModuleMapFormat::Gcc:
  181. return ".gcm"_s;
  182. case CxxModuleMapFormat::Msvc:
  183. return ".ifc"_s;
  184. }
  185. }
  186. return ".bmi"_s;
  187. }
  188. std::set<std::string> CxxModuleUsageSeed(
  189. CxxModuleLocations const& loc, std::vector<cmScanDepInfo> const& objects,
  190. CxxModuleUsage& usages)
  191. {
  192. // Track inner usages to populate usages from internal bits.
  193. //
  194. // This is a map of modules that required some other module that was not
  195. // found to those that were not found.
  196. std::map<std::string, std::set<std::string>> internal_usages;
  197. std::set<std::string> unresolved;
  198. for (cmScanDepInfo const& object : objects) {
  199. // Add references for each of the provided modules.
  200. for (auto const& p : object.Provides) {
  201. if (auto bmi_loc = loc.BmiGeneratorPathForModule(p.LogicalName)) {
  202. // XXX(cxx-modules): How to support header units?
  203. usages.AddReference(p.LogicalName, *bmi_loc, LookupMethod::ByName);
  204. }
  205. }
  206. // For each requires, pull in what is required.
  207. for (auto const& r : object.Requires) {
  208. // Find transitive usages.
  209. auto transitive_usages = usages.Usage.find(r.LogicalName);
  210. // Find the required name in the current target.
  211. auto bmi_loc = loc.BmiGeneratorPathForModule(r.LogicalName);
  212. for (auto const& p : object.Provides) {
  213. auto& this_usages = usages.Usage[p.LogicalName];
  214. // Add the direct usage.
  215. this_usages.insert(r.LogicalName);
  216. // Add the transitive usage.
  217. if (transitive_usages != usages.Usage.end()) {
  218. this_usages.insert(transitive_usages->second.begin(),
  219. transitive_usages->second.end());
  220. } else if (bmi_loc) {
  221. // Mark that we need to update transitive usages later.
  222. internal_usages[p.LogicalName].insert(r.LogicalName);
  223. }
  224. }
  225. if (bmi_loc) {
  226. usages.AddReference(r.LogicalName, *bmi_loc, r.Method);
  227. }
  228. }
  229. }
  230. // While we have internal usages to manage.
  231. while (!internal_usages.empty()) {
  232. size_t starting_size = internal_usages.size();
  233. // For each internal usage.
  234. for (auto usage = internal_usages.begin(); usage != internal_usages.end();
  235. /* see end of loop */) {
  236. auto& this_usages = usages.Usage[usage->first];
  237. for (auto use = usage->second.begin(); use != usage->second.end();
  238. /* see end of loop */) {
  239. // Check if this required module uses other internal modules; defer
  240. // if so.
  241. if (internal_usages.count(*use)) {
  242. // Advance the iterator.
  243. ++use;
  244. continue;
  245. }
  246. auto transitive_usages = usages.Usage.find(*use);
  247. if (transitive_usages != usages.Usage.end()) {
  248. this_usages.insert(transitive_usages->second.begin(),
  249. transitive_usages->second.end());
  250. }
  251. // Remove the entry and advance the iterator.
  252. use = usage->second.erase(use);
  253. }
  254. // Erase the entry if it doesn't have any remaining usages.
  255. if (usage->second.empty()) {
  256. usage = internal_usages.erase(usage);
  257. } else {
  258. ++usage;
  259. }
  260. }
  261. // Check that at least one usage was resolved.
  262. if (starting_size == internal_usages.size()) {
  263. // Nothing could be resolved this loop; we have a cycle, so record the
  264. // cycle and exit.
  265. for (auto const& usage : internal_usages) {
  266. unresolved.insert(usage.first);
  267. }
  268. break;
  269. }
  270. }
  271. return unresolved;
  272. }
  273. std::string CxxModuleMapContent(CxxModuleMapFormat format,
  274. CxxModuleLocations const& loc,
  275. cmScanDepInfo const& obj,
  276. CxxModuleUsage const& usages)
  277. {
  278. switch (format) {
  279. case CxxModuleMapFormat::Clang:
  280. return CxxModuleMapContentClang(loc, obj);
  281. case CxxModuleMapFormat::Gcc:
  282. return CxxModuleMapContentGcc(loc, obj);
  283. case CxxModuleMapFormat::Msvc:
  284. return CxxModuleMapContentMsvc(loc, obj, usages);
  285. }
  286. assert(false);
  287. return {};
  288. }