cmDyndepCollation.cxx 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692
  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 "cmDyndepCollation.h"
  4. #include <algorithm>
  5. #include <map>
  6. #include <ostream>
  7. #include <set>
  8. #include <utility>
  9. #include <vector>
  10. #include <cm/memory>
  11. #include <cm/string_view>
  12. #include <cmext/string_view>
  13. #include <cm3p/json/value.h>
  14. #include "cmExportBuildFileGenerator.h"
  15. #include "cmExportSet.h"
  16. #include "cmFileSet.h"
  17. #include "cmGeneratedFileStream.h"
  18. #include "cmGeneratorExpression.h" // IWYU pragma: keep
  19. #include "cmGeneratorTarget.h"
  20. #include "cmGlobalGenerator.h"
  21. #include "cmInstallCxxModuleBmiGenerator.h"
  22. #include "cmInstallExportGenerator.h"
  23. #include "cmInstallFileSetGenerator.h"
  24. #include "cmInstallGenerator.h"
  25. #include "cmMakefile.h"
  26. #include "cmMessageType.h"
  27. #include "cmOutputConverter.h"
  28. #include "cmScanDepFormat.h"
  29. #include "cmSourceFile.h"
  30. #include "cmStringAlgorithms.h"
  31. #include "cmSystemTools.h"
  32. #include "cmTarget.h"
  33. #include "cmTargetExport.h"
  34. namespace {
  35. Json::Value CollationInformationCxxModules(
  36. cmGeneratorTarget const* gt, std::string const& config,
  37. cmDyndepGeneratorCallbacks const& cb)
  38. {
  39. cmTarget const* tgt = gt->Target;
  40. auto all_file_sets = tgt->GetAllFileSetNames();
  41. Json::Value tdi_cxx_module_info = Json::objectValue;
  42. for (auto const& file_set_name : all_file_sets) {
  43. auto const* file_set = tgt->GetFileSet(file_set_name);
  44. if (!file_set) {
  45. gt->Makefile->IssueMessage(MessageType::INTERNAL_ERROR,
  46. cmStrCat("Target \"", tgt->GetName(),
  47. "\" is tracked to have file set \"",
  48. file_set_name,
  49. "\", but it was not found."));
  50. continue;
  51. }
  52. auto fs_type = file_set->GetType();
  53. // We only care about C++ module sources here.
  54. if (fs_type != "CXX_MODULES"_s) {
  55. continue;
  56. }
  57. auto fileEntries = file_set->CompileFileEntries();
  58. auto directoryEntries = file_set->CompileDirectoryEntries();
  59. auto directories = file_set->EvaluateDirectoryEntries(
  60. directoryEntries, gt->LocalGenerator, config, gt);
  61. std::map<std::string, std::vector<std::string>> files_per_dirs;
  62. for (auto const& entry : fileEntries) {
  63. file_set->EvaluateFileEntry(directories, files_per_dirs, entry,
  64. gt->LocalGenerator, config, gt);
  65. }
  66. enum class CompileType
  67. {
  68. ObjectAndBmi,
  69. BmiOnly,
  70. };
  71. std::map<std::string, std::pair<cmSourceFile const*, CompileType>> sf_map;
  72. {
  73. auto fill_sf_map = [gt, tgt, &sf_map](cmSourceFile const* sf,
  74. CompileType type) {
  75. auto full_path = sf->GetFullPath();
  76. if (full_path.empty()) {
  77. gt->Makefile->IssueMessage(
  78. MessageType::INTERNAL_ERROR,
  79. cmStrCat("Target \"", tgt->GetName(),
  80. "\" has a full path-less source file."));
  81. return;
  82. }
  83. sf_map[full_path] = std::make_pair(sf, type);
  84. };
  85. std::vector<cmSourceFile const*> objectSources;
  86. gt->GetObjectSources(objectSources, config);
  87. for (auto const* sf : objectSources) {
  88. fill_sf_map(sf, CompileType::ObjectAndBmi);
  89. }
  90. std::vector<cmSourceFile const*> cxxModuleSources;
  91. gt->GetCxxModuleSources(cxxModuleSources, config);
  92. for (auto const* sf : cxxModuleSources) {
  93. fill_sf_map(sf, CompileType::BmiOnly);
  94. }
  95. }
  96. Json::Value fs_dest = Json::nullValue;
  97. for (auto const& ig : gt->Makefile->GetInstallGenerators()) {
  98. if (auto const* fsg =
  99. dynamic_cast<cmInstallFileSetGenerator const*>(ig.get())) {
  100. if (fsg->GetTarget() == gt && fsg->GetFileSet() == file_set) {
  101. fs_dest = fsg->GetDestination(config);
  102. continue;
  103. }
  104. }
  105. }
  106. for (auto const& files_per_dir : files_per_dirs) {
  107. for (auto const& file : files_per_dir.second) {
  108. auto const full_file = cmSystemTools::CollapseFullPath(file);
  109. auto lookup = sf_map.find(full_file);
  110. if (lookup == sf_map.end()) {
  111. gt->Makefile->IssueMessage(
  112. MessageType::FATAL_ERROR,
  113. cmStrCat("Target \"", tgt->GetName(), "\" has source file\n ",
  114. file,
  115. "\nin a \"FILE_SET TYPE CXX_MODULES\" but it is not "
  116. "scheduled for compilation."));
  117. continue;
  118. }
  119. auto const* sf = lookup->second.first;
  120. CompileType const ct = lookup->second.second;
  121. if (!sf) {
  122. gt->Makefile->IssueMessage(
  123. MessageType::INTERNAL_ERROR,
  124. cmStrCat("Target \"", tgt->GetName(), "\" has source file \"",
  125. file, "\" which has not been tracked properly."));
  126. continue;
  127. }
  128. auto obj_path = ct == CompileType::ObjectAndBmi
  129. ? cb.ObjectFilePath(sf, config)
  130. : cb.BmiFilePath(sf, config);
  131. Json::Value& tdi_module_info = tdi_cxx_module_info[obj_path] =
  132. Json::objectValue;
  133. tdi_module_info["source"] = full_file;
  134. tdi_module_info["bmi-only"] = ct == CompileType::BmiOnly;
  135. tdi_module_info["relative-directory"] = files_per_dir.first;
  136. tdi_module_info["name"] = file_set->GetName();
  137. tdi_module_info["type"] = file_set->GetType();
  138. tdi_module_info["visibility"] =
  139. std::string(cmFileSetVisibilityToName(file_set->GetVisibility()));
  140. tdi_module_info["destination"] = fs_dest;
  141. }
  142. }
  143. }
  144. return tdi_cxx_module_info;
  145. }
  146. Json::Value CollationInformationBmiInstallation(cmGeneratorTarget const* gt,
  147. std::string const& config)
  148. {
  149. cmInstallCxxModuleBmiGenerator const* bmi_gen = nullptr;
  150. for (auto const& ig : gt->Makefile->GetInstallGenerators()) {
  151. if (auto const* bmig =
  152. dynamic_cast<cmInstallCxxModuleBmiGenerator const*>(ig.get())) {
  153. if (bmig->GetTarget() == gt) {
  154. bmi_gen = bmig;
  155. continue;
  156. }
  157. }
  158. }
  159. if (bmi_gen) {
  160. Json::Value tdi_bmi_info = Json::objectValue;
  161. tdi_bmi_info["permissions"] = bmi_gen->GetFilePermissions();
  162. tdi_bmi_info["destination"] = bmi_gen->GetDestination(config);
  163. const char* msg_level = "";
  164. switch (bmi_gen->GetMessageLevel()) {
  165. case cmInstallGenerator::MessageDefault:
  166. break;
  167. case cmInstallGenerator::MessageAlways:
  168. msg_level = "MESSAGE_ALWAYS";
  169. break;
  170. case cmInstallGenerator::MessageLazy:
  171. msg_level = "MESSAGE_LAZY";
  172. break;
  173. case cmInstallGenerator::MessageNever:
  174. msg_level = "MESSAGE_NEVER";
  175. break;
  176. }
  177. tdi_bmi_info["message-level"] = msg_level;
  178. tdi_bmi_info["script-location"] = bmi_gen->GetScriptLocation(config);
  179. return tdi_bmi_info;
  180. }
  181. return Json::nullValue;
  182. }
  183. Json::Value CollationInformationExports(cmGeneratorTarget const* gt)
  184. {
  185. Json::Value tdi_exports = Json::arrayValue;
  186. std::string export_name = gt->GetExportName();
  187. std::string fs_export_name = gt->GetFilesystemExportName();
  188. auto const& all_install_exports = gt->GetGlobalGenerator()->GetExportSets();
  189. for (auto const& exp : all_install_exports) {
  190. // Ignore exports sets which are not for this target.
  191. auto const& targets = exp.second.GetTargetExports();
  192. auto tgt_export =
  193. std::find_if(targets.begin(), targets.end(),
  194. [gt](std::unique_ptr<cmTargetExport> const& te) {
  195. return te->Target == gt;
  196. });
  197. if (tgt_export == targets.end()) {
  198. continue;
  199. }
  200. auto const* installs = exp.second.GetInstallations();
  201. for (auto const* install : *installs) {
  202. Json::Value tdi_export_info = Json::objectValue;
  203. auto const& ns = install->GetNamespace();
  204. auto const& dest = install->GetDestination();
  205. auto const& cxxm_dir = install->GetCxxModuleDirectory();
  206. auto const& export_prefix = install->GetTempDir();
  207. tdi_export_info["namespace"] = ns;
  208. tdi_export_info["export-name"] = export_name;
  209. tdi_export_info["filesystem-export-name"] = fs_export_name;
  210. tdi_export_info["destination"] = dest;
  211. tdi_export_info["cxx-module-info-dir"] = cxxm_dir;
  212. tdi_export_info["export-prefix"] = export_prefix;
  213. tdi_export_info["install"] = true;
  214. tdi_exports.append(tdi_export_info);
  215. }
  216. }
  217. auto const& all_build_exports = gt->Makefile->GetExportBuildFileGenerators();
  218. for (auto const& exp : all_build_exports) {
  219. std::vector<cmExportBuildFileGenerator::TargetExport> targets;
  220. exp->GetTargets(targets);
  221. // Ignore exports sets which are not for this target.
  222. auto const& name = gt->GetName();
  223. bool has_current_target =
  224. std::any_of(targets.begin(), targets.end(),
  225. [name](cmExportBuildFileGenerator::TargetExport const& te) {
  226. return te.Name == name;
  227. });
  228. if (!has_current_target) {
  229. continue;
  230. }
  231. Json::Value tdi_export_info = Json::objectValue;
  232. auto const& ns = exp->GetNamespace();
  233. auto const& main_fn = exp->GetMainExportFileName();
  234. auto const& cxxm_dir = exp->GetCxxModuleDirectory();
  235. auto dest = cmsys::SystemTools::GetParentDirectory(main_fn);
  236. auto const& export_prefix =
  237. cmSystemTools::GetFilenamePath(exp->GetMainExportFileName());
  238. tdi_export_info["namespace"] = ns;
  239. tdi_export_info["export-name"] = export_name;
  240. tdi_export_info["filesystem-export-name"] = fs_export_name;
  241. tdi_export_info["destination"] = dest;
  242. tdi_export_info["cxx-module-info-dir"] = cxxm_dir;
  243. tdi_export_info["export-prefix"] = export_prefix;
  244. tdi_export_info["install"] = false;
  245. tdi_exports.append(tdi_export_info);
  246. }
  247. return tdi_exports;
  248. }
  249. }
  250. void cmDyndepCollation::AddCollationInformation(
  251. Json::Value& tdi, cmGeneratorTarget const* gt, std::string const& config,
  252. cmDyndepGeneratorCallbacks const& cb)
  253. {
  254. tdi["cxx-modules"] = CollationInformationCxxModules(gt, config, cb);
  255. tdi["bmi-installation"] = CollationInformationBmiInstallation(gt, config);
  256. tdi["exports"] = CollationInformationExports(gt);
  257. tdi["config"] = config;
  258. }
  259. struct CxxModuleFileSet
  260. {
  261. std::string Name;
  262. bool BmiOnly = false;
  263. std::string RelativeDirectory;
  264. std::string SourcePath;
  265. std::string Type;
  266. cmFileSetVisibility Visibility = cmFileSetVisibility::Private;
  267. cm::optional<std::string> Destination;
  268. };
  269. struct CxxModuleBmiInstall
  270. {
  271. std::string Component;
  272. std::string Destination;
  273. bool ExcludeFromAll;
  274. bool Optional;
  275. std::string Permissions;
  276. std::string MessageLevel;
  277. std::string ScriptLocation;
  278. };
  279. struct CxxModuleExport
  280. {
  281. std::string Name;
  282. std::string FilesystemName;
  283. std::string Destination;
  284. std::string Prefix;
  285. std::string CxxModuleInfoDir;
  286. std::string Namespace;
  287. bool Install;
  288. };
  289. struct cmCxxModuleExportInfo
  290. {
  291. std::map<std::string, CxxModuleFileSet> ObjectToFileSet;
  292. cm::optional<CxxModuleBmiInstall> BmiInstallation;
  293. std::vector<CxxModuleExport> Exports;
  294. std::string Config;
  295. };
  296. void cmCxxModuleExportInfoDeleter::operator()(cmCxxModuleExportInfo* ei) const
  297. {
  298. delete ei;
  299. }
  300. std::unique_ptr<cmCxxModuleExportInfo, cmCxxModuleExportInfoDeleter>
  301. cmDyndepCollation::ParseExportInfo(Json::Value const& tdi)
  302. {
  303. auto export_info =
  304. std::unique_ptr<cmCxxModuleExportInfo, cmCxxModuleExportInfoDeleter>(
  305. new cmCxxModuleExportInfo);
  306. export_info->Config = tdi["config"].asString();
  307. if (export_info->Config.empty()) {
  308. export_info->Config = "noconfig";
  309. }
  310. Json::Value const& tdi_exports = tdi["exports"];
  311. if (tdi_exports.isArray()) {
  312. for (auto const& tdi_export : tdi_exports) {
  313. CxxModuleExport exp;
  314. exp.Install = tdi_export["install"].asBool();
  315. exp.Name = tdi_export["export-name"].asString();
  316. exp.FilesystemName = tdi_export["filesystem-export-name"].asString();
  317. exp.Destination = tdi_export["destination"].asString();
  318. exp.Prefix = tdi_export["export-prefix"].asString();
  319. exp.CxxModuleInfoDir = tdi_export["cxx-module-info-dir"].asString();
  320. exp.Namespace = tdi_export["namespace"].asString();
  321. export_info->Exports.push_back(exp);
  322. }
  323. }
  324. auto const& bmi_installation = tdi["bmi-installation"];
  325. if (bmi_installation.isObject()) {
  326. CxxModuleBmiInstall bmi_install;
  327. bmi_install.Component = bmi_installation["component"].asString();
  328. bmi_install.Destination = bmi_installation["destination"].asString();
  329. bmi_install.ExcludeFromAll = bmi_installation["exclude-from-all"].asBool();
  330. bmi_install.Optional = bmi_installation["optional"].asBool();
  331. bmi_install.Permissions = bmi_installation["permissions"].asString();
  332. bmi_install.MessageLevel = bmi_installation["message-level"].asString();
  333. bmi_install.ScriptLocation =
  334. bmi_installation["script-location"].asString();
  335. export_info->BmiInstallation = bmi_install;
  336. }
  337. Json::Value const& tdi_cxx_modules = tdi["cxx-modules"];
  338. if (tdi_cxx_modules.isObject()) {
  339. for (auto i = tdi_cxx_modules.begin(); i != tdi_cxx_modules.end(); ++i) {
  340. CxxModuleFileSet& fsi = export_info->ObjectToFileSet[i.key().asString()];
  341. auto const& tdi_cxx_module_info = *i;
  342. fsi.Name = tdi_cxx_module_info["name"].asString();
  343. fsi.BmiOnly = tdi_cxx_module_info["bmi-only"].asBool();
  344. fsi.RelativeDirectory =
  345. tdi_cxx_module_info["relative-directory"].asString();
  346. if (!fsi.RelativeDirectory.empty() &&
  347. fsi.RelativeDirectory.back() != '/') {
  348. fsi.RelativeDirectory = cmStrCat(fsi.RelativeDirectory, '/');
  349. }
  350. fsi.SourcePath = tdi_cxx_module_info["source"].asString();
  351. fsi.Type = tdi_cxx_module_info["type"].asString();
  352. fsi.Visibility = cmFileSetVisibilityFromName(
  353. tdi_cxx_module_info["visibility"].asString(), nullptr);
  354. auto const& tdi_fs_dest = tdi_cxx_module_info["destination"];
  355. if (tdi_fs_dest.isString()) {
  356. fsi.Destination = tdi_fs_dest.asString();
  357. }
  358. }
  359. }
  360. return export_info;
  361. }
  362. bool cmDyndepCollation::WriteDyndepMetadata(
  363. std::string const& lang, std::vector<cmScanDepInfo> const& objects,
  364. cmCxxModuleExportInfo const& export_info,
  365. cmDyndepMetadataCallbacks const& cb)
  366. {
  367. // Only C++ supports any of the file-set or BMI installation considered
  368. // below.
  369. if (lang != "CXX"_s) {
  370. return true;
  371. }
  372. bool result = true;
  373. // Prepare the export information blocks.
  374. std::string const config_upper =
  375. cmSystemTools::UpperCase(export_info.Config);
  376. std::vector<
  377. std::pair<std::unique_ptr<cmGeneratedFileStream>, CxxModuleExport const*>>
  378. exports;
  379. for (auto const& exp : export_info.Exports) {
  380. std::unique_ptr<cmGeneratedFileStream> properties;
  381. std::string const export_dir =
  382. cmStrCat(exp.Prefix, '/', exp.CxxModuleInfoDir, '/');
  383. std::string const property_file_path =
  384. cmStrCat(export_dir, "target-", exp.FilesystemName, '-',
  385. export_info.Config, ".cmake");
  386. properties = cm::make_unique<cmGeneratedFileStream>(property_file_path);
  387. // Set up the preamble.
  388. *properties << "set_property(TARGET \"" << exp.Namespace << exp.Name
  389. << "\"\n"
  390. << " PROPERTY IMPORTED_CXX_MODULES_" << config_upper << '\n';
  391. exports.emplace_back(std::move(properties), &exp);
  392. }
  393. std::unique_ptr<cmGeneratedFileStream> bmi_install_script;
  394. if (export_info.BmiInstallation) {
  395. bmi_install_script = cm::make_unique<cmGeneratedFileStream>(
  396. export_info.BmiInstallation->ScriptLocation);
  397. }
  398. auto cmEscape = [](cm::string_view str) {
  399. return cmOutputConverter::EscapeForCMake(
  400. str, cmOutputConverter::WrapQuotes::NoWrap);
  401. };
  402. auto install_destination =
  403. [&cmEscape](std::string const& dest) -> std::pair<bool, std::string> {
  404. if (cmSystemTools::FileIsFullPath(dest)) {
  405. return std::make_pair(true, cmEscape(dest));
  406. }
  407. return std::make_pair(false,
  408. cmStrCat("${_IMPORT_PREFIX}/", cmEscape(dest)));
  409. };
  410. // public/private requirement tracking.
  411. std::set<std::string> private_modules;
  412. std::map<std::string, std::set<std::string>> public_source_requires;
  413. for (cmScanDepInfo const& object : objects) {
  414. // Convert to forward slashes.
  415. auto output_path = object.PrimaryOutput;
  416. #ifdef _WIN32
  417. cmSystemTools::ConvertToUnixSlashes(output_path);
  418. #endif
  419. // Find the fileset for this object.
  420. auto fileset_info_itr = export_info.ObjectToFileSet.find(output_path);
  421. bool const has_provides = !object.Provides.empty();
  422. if (fileset_info_itr == export_info.ObjectToFileSet.end()) {
  423. // If it provides anything, it should have type `CXX_MODULES`
  424. // and be present.
  425. if (has_provides) {
  426. // Take the first module provided to provide context.
  427. auto const& provides = object.Provides[0];
  428. cmSystemTools::Error(
  429. cmStrCat("Output ", object.PrimaryOutput, " provides the `",
  430. provides.LogicalName,
  431. "` module but it is not found in a `FILE_SET` of type "
  432. "`CXX_MODULES`"));
  433. result = false;
  434. }
  435. // This object file does not provide anything, so nothing more needs to
  436. // be done.
  437. continue;
  438. }
  439. auto const& file_set = fileset_info_itr->second;
  440. // Verify the fileset type for the object.
  441. if (file_set.Type == "CXX_MODULES"_s) {
  442. if (!has_provides) {
  443. cmSystemTools::Error(
  444. cmStrCat("Output ", object.PrimaryOutput,
  445. " is of type `CXX_MODULES` but does not provide a module "
  446. "interface unit or partition"));
  447. result = false;
  448. continue;
  449. }
  450. } else if (file_set.Type == "CXX_MODULE_HEADERS"_s) {
  451. // TODO.
  452. } else {
  453. if (has_provides) {
  454. auto const& provides = object.Provides[0];
  455. cmSystemTools::Error(cmStrCat(
  456. "Source ", file_set.SourcePath, " provides the `",
  457. provides.LogicalName, "` C++ module but is of type `", file_set.Type,
  458. "` module but must be of type `CXX_MODULES`"));
  459. result = false;
  460. }
  461. // Not a C++ module; ignore.
  462. continue;
  463. }
  464. if (!cmFileSetVisibilityIsForInterface(file_set.Visibility)) {
  465. // Nothing needs to be conveyed about non-`PUBLIC` modules.
  466. for (auto const& p : object.Provides) {
  467. private_modules.insert(p.LogicalName);
  468. }
  469. continue;
  470. }
  471. // The module is public. Record what it directly requires.
  472. {
  473. auto& reqs = public_source_requires[file_set.SourcePath];
  474. for (auto const& r : object.Requires) {
  475. reqs.insert(r.LogicalName);
  476. }
  477. }
  478. // Write out properties and install rules for any exports.
  479. for (auto const& p : object.Provides) {
  480. bool bmi_dest_is_abs = false;
  481. std::string bmi_destination;
  482. if (export_info.BmiInstallation) {
  483. auto dest =
  484. install_destination(export_info.BmiInstallation->Destination);
  485. bmi_dest_is_abs = dest.first;
  486. bmi_destination = cmStrCat(dest.second, '/');
  487. }
  488. std::string install_bmi_path;
  489. std::string build_bmi_path;
  490. auto m = cb.ModuleFile(p.LogicalName);
  491. if (m) {
  492. install_bmi_path = cmStrCat(
  493. bmi_destination, cmEscape(cmSystemTools::GetFilenameName(*m)));
  494. build_bmi_path = cmEscape(*m);
  495. }
  496. for (auto const& exp : exports) {
  497. std::string iface_source;
  498. if (exp.second->Install && file_set.Destination) {
  499. auto dest = install_destination(*file_set.Destination);
  500. iface_source = cmStrCat(
  501. dest.second, '/', cmEscape(file_set.RelativeDirectory),
  502. cmEscape(cmSystemTools::GetFilenameName(file_set.SourcePath)));
  503. } else {
  504. iface_source = cmEscape(file_set.SourcePath);
  505. }
  506. std::string bmi_path;
  507. if (exp.second->Install && export_info.BmiInstallation) {
  508. bmi_path = install_bmi_path;
  509. } else if (!exp.second->Install) {
  510. bmi_path = build_bmi_path;
  511. }
  512. if (iface_source.empty()) {
  513. // No destination for the C++ module source; ignore this property
  514. // value.
  515. continue;
  516. }
  517. *exp.first << " \"" << cmEscape(p.LogicalName) << '='
  518. << iface_source;
  519. if (!bmi_path.empty()) {
  520. *exp.first << ',' << bmi_path;
  521. }
  522. *exp.first << "\"\n";
  523. }
  524. if (bmi_install_script) {
  525. auto const& bmi_install = *export_info.BmiInstallation;
  526. *bmi_install_script << "if (CMAKE_INSTALL_COMPONENT STREQUAL \""
  527. << cmEscape(bmi_install.Component) << '\"';
  528. if (!bmi_install.ExcludeFromAll) {
  529. *bmi_install_script << " OR NOT CMAKE_INSTALL_COMPONENT";
  530. }
  531. *bmi_install_script << ")\n";
  532. *bmi_install_script << " file(INSTALL\n"
  533. " DESTINATION \"";
  534. if (!bmi_dest_is_abs) {
  535. *bmi_install_script << "${CMAKE_INSTALL_PREFIX}/";
  536. }
  537. *bmi_install_script << cmEscape(bmi_install.Destination)
  538. << "\"\n"
  539. " TYPE FILE\n";
  540. if (bmi_install.Optional) {
  541. *bmi_install_script << " OPTIONAL\n";
  542. }
  543. if (!bmi_install.MessageLevel.empty()) {
  544. *bmi_install_script << " " << bmi_install.MessageLevel << "\n";
  545. }
  546. if (!bmi_install.Permissions.empty()) {
  547. *bmi_install_script << " PERMISSIONS" << bmi_install.Permissions
  548. << "\n";
  549. }
  550. *bmi_install_script << " FILES \"" << *m << "\")\n";
  551. if (bmi_dest_is_abs) {
  552. *bmi_install_script
  553. << " list(APPEND CMAKE_ABSOLUTE_DESTINATION_FILES\n"
  554. " \""
  555. << cmEscape(cmSystemTools::GetFilenameName(*m))
  556. << "\")\n"
  557. " if (CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION)\n"
  558. " message(WARNING\n"
  559. " \"ABSOLUTE path INSTALL DESTINATION : "
  560. "${CMAKE_ABSOLUTE_DESTINATION_FILES}\")\n"
  561. " endif ()\n"
  562. " if (CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION)\n"
  563. " message(FATAL_ERROR\n"
  564. " \"ABSOLUTE path INSTALL DESTINATION forbidden (by "
  565. "caller): ${CMAKE_ABSOLUTE_DESTINATION_FILES}\")\n"
  566. " endif ()\n";
  567. }
  568. *bmi_install_script << "endif ()\n";
  569. }
  570. }
  571. }
  572. // Add trailing parenthesis for the `set_property` call.
  573. for (auto const& exp : exports) {
  574. *exp.first << ")\n";
  575. }
  576. // Check that public sources only require public modules.
  577. for (auto const& pub_reqs : public_source_requires) {
  578. for (auto const& req : pub_reqs.second) {
  579. if (private_modules.count(req)) {
  580. cmSystemTools::Error(cmStrCat(
  581. "Public C++ module source `", pub_reqs.first, "` requires the `",
  582. req, "` C++ module which is provided by a private source"));
  583. result = false;
  584. }
  585. }
  586. }
  587. return result;
  588. }
  589. bool cmDyndepCollation::IsObjectPrivate(
  590. std::string const& object, cmCxxModuleExportInfo const& export_info)
  591. {
  592. #ifdef _WIN32
  593. std::string output_path = object;
  594. cmSystemTools::ConvertToUnixSlashes(output_path);
  595. #else
  596. std::string const& output_path = object;
  597. #endif
  598. auto fileset_info_itr = export_info.ObjectToFileSet.find(output_path);
  599. if (fileset_info_itr == export_info.ObjectToFileSet.end()) {
  600. return false;
  601. }
  602. auto const& file_set = fileset_info_itr->second;
  603. return !cmFileSetVisibilityIsForInterface(file_set.Visibility);
  604. }
  605. bool cmDyndepCollation::IsBmiOnly(cmCxxModuleExportInfo const& exportInfo,
  606. std::string const& object)
  607. {
  608. #ifdef _WIN32
  609. auto object_path = object;
  610. cmSystemTools::ConvertToUnixSlashes(object_path);
  611. #else
  612. auto const& object_path = object;
  613. #endif
  614. auto fs = exportInfo.ObjectToFileSet.find(object_path);
  615. return (fs != exportInfo.ObjectToFileSet.end()) && fs->second.BmiOnly;
  616. }