cmCxxModuleMapper.cxx 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  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, bool& private_usage_found)
  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. private_usage_found = true;
  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. if (transitive_usages == usages.Usage.end() ||
  293. internal_usages.find(r.LogicalName) != internal_usages.end()) {
  294. // Mark that we need to update transitive usages later.
  295. if (bmi_loc.IsKnown()) {
  296. internal_usages[p.LogicalName].insert(r.LogicalName);
  297. }
  298. } else {
  299. // Add the transitive usage.
  300. this_usages.insert(transitive_usages->second.begin(),
  301. transitive_usages->second.end());
  302. }
  303. }
  304. if (bmi_loc.IsKnown()) {
  305. usages.AddReference(r.LogicalName, bmi_loc.Location(), r.Method);
  306. }
  307. }
  308. }
  309. // While we have internal usages to manage.
  310. while (!internal_usages.empty()) {
  311. size_t starting_size = internal_usages.size();
  312. // For each internal usage.
  313. for (auto usage = internal_usages.begin(); usage != internal_usages.end();
  314. /* see end of loop */) {
  315. auto& this_usages = usages.Usage[usage->first];
  316. for (auto use = usage->second.begin(); use != usage->second.end();
  317. /* see end of loop */) {
  318. // Check if this required module uses other internal modules; defer
  319. // if so.
  320. if (internal_usages.count(*use)) {
  321. // Advance the iterator.
  322. ++use;
  323. continue;
  324. }
  325. auto transitive_usages = usages.Usage.find(*use);
  326. if (transitive_usages != usages.Usage.end()) {
  327. this_usages.insert(transitive_usages->second.begin(),
  328. transitive_usages->second.end());
  329. }
  330. // Remove the entry and advance the iterator.
  331. use = usage->second.erase(use);
  332. }
  333. // Erase the entry if it doesn't have any remaining usages.
  334. if (usage->second.empty()) {
  335. usage = internal_usages.erase(usage);
  336. } else {
  337. ++usage;
  338. }
  339. }
  340. // Check that at least one usage was resolved.
  341. if (starting_size == internal_usages.size()) {
  342. // Nothing could be resolved this loop; we have a cycle, so record the
  343. // cycle and exit.
  344. for (auto const& usage : internal_usages) {
  345. unresolved.insert(usage.first);
  346. }
  347. break;
  348. }
  349. }
  350. return unresolved;
  351. }
  352. std::string CxxModuleMapContent(CxxModuleMapFormat format,
  353. CxxModuleLocations const& loc,
  354. cmScanDepInfo const& obj,
  355. CxxModuleUsage const& usages)
  356. {
  357. switch (format) {
  358. case CxxModuleMapFormat::Clang:
  359. return CxxModuleMapContentClang(loc, obj, usages);
  360. case CxxModuleMapFormat::Gcc:
  361. return CxxModuleMapContentGcc(loc, obj);
  362. case CxxModuleMapFormat::Msvc:
  363. return CxxModuleMapContentMsvc(loc, obj, usages);
  364. }
  365. assert(false);
  366. return {};
  367. }
  368. CxxModuleMapMode CxxModuleMapOpenMode(CxxModuleMapFormat format)
  369. {
  370. switch (format) {
  371. case CxxModuleMapFormat::Gcc:
  372. return CxxModuleMapMode::Binary;
  373. case CxxModuleMapFormat::Clang:
  374. case CxxModuleMapFormat::Msvc:
  375. return CxxModuleMapMode::Default;
  376. }
  377. assert(false);
  378. return CxxModuleMapMode::Default;
  379. }