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