cmCxxModuleMapper.cxx 11 KB

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