cmCxxModuleMapper.cxx 12 KB

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