cmExportCMakeConfigGenerator.cxx 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686
  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 "cmExportCMakeConfigGenerator.h"
  4. #include <algorithm>
  5. #include <cassert>
  6. #include <sstream>
  7. #include <utility>
  8. #include <vector>
  9. #include <cm/optional>
  10. #include <cm/string_view>
  11. #include <cmext/string_view>
  12. #include "cmExportSet.h"
  13. #include "cmFileSet.h"
  14. #include "cmFindPackageStack.h"
  15. #include "cmGeneratedFileStream.h"
  16. #include "cmGeneratorTarget.h"
  17. #include "cmLinkItem.h"
  18. #include "cmLocalGenerator.h"
  19. #include "cmMakefile.h"
  20. #include "cmMessageType.h"
  21. #include "cmOutputConverter.h"
  22. #include "cmPolicies.h"
  23. #include "cmStateTypes.h"
  24. #include "cmStringAlgorithms.h"
  25. #include "cmSystemTools.h"
  26. #include "cmTarget.h"
  27. #include "cmValue.h"
  28. #include "cmVersion.h"
  29. static std::string cmExportFileGeneratorEscape(std::string const& str)
  30. {
  31. // Escape a property value for writing into a .cmake file.
  32. std::string result = cmOutputConverter::EscapeForCMake(str);
  33. // Un-escape variable references generated by our own export code.
  34. cmSystemTools::ReplaceString(result, "\\${_IMPORT_PREFIX}",
  35. "${_IMPORT_PREFIX}");
  36. cmSystemTools::ReplaceString(result, "\\${CMAKE_IMPORT_LIBRARY_SUFFIX}",
  37. "${CMAKE_IMPORT_LIBRARY_SUFFIX}");
  38. return result;
  39. }
  40. cmExportCMakeConfigGenerator::cmExportCMakeConfigGenerator() = default;
  41. cm::string_view cmExportCMakeConfigGenerator::GetImportPrefixWithSlash() const
  42. {
  43. return "${_IMPORT_PREFIX}/"_s;
  44. }
  45. bool cmExportCMakeConfigGenerator::GenerateImportFile(std::ostream& os)
  46. {
  47. std::stringstream mainFileWithHeadersAndFootersBuffer;
  48. // Start with the import file header.
  49. this->GenerateImportHeaderCode(mainFileWithHeadersAndFootersBuffer);
  50. // Create all the imported targets.
  51. std::stringstream mainFileBuffer;
  52. bool result = this->GenerateMainFile(mainFileBuffer);
  53. // Export find_dependency() calls. Must be done after GenerateMainFile(),
  54. // because that's when target dependencies are gathered, which we need for
  55. // the find_dependency() calls.
  56. if (!this->AppendMode && this->GetExportSet() &&
  57. this->ExportPackageDependencies) {
  58. this->SetRequiredCMakeVersion(3, 9, 0);
  59. this->GenerateFindDependencyCalls(mainFileWithHeadersAndFootersBuffer);
  60. }
  61. // Write cached import code.
  62. mainFileWithHeadersAndFootersBuffer << mainFileBuffer.rdbuf();
  63. // End with the import file footer.
  64. this->GenerateImportFooterCode(mainFileWithHeadersAndFootersBuffer);
  65. this->GeneratePolicyFooterCode(mainFileWithHeadersAndFootersBuffer);
  66. // This has to be done last, after the minimum CMake version has been
  67. // determined.
  68. this->GeneratePolicyHeaderCode(os);
  69. os << mainFileWithHeadersAndFootersBuffer.rdbuf();
  70. return result;
  71. }
  72. void cmExportCMakeConfigGenerator::GenerateInterfaceProperties(
  73. cmGeneratorTarget const* target, std::ostream& os,
  74. ImportPropertyMap const& properties)
  75. {
  76. if (!properties.empty()) {
  77. std::string targetName =
  78. cmStrCat(this->Namespace, target->GetExportName());
  79. os << "set_target_properties(" << targetName << " PROPERTIES\n";
  80. for (auto const& property : properties) {
  81. os << " " << property.first << " "
  82. << cmExportFileGeneratorEscape(property.second) << "\n";
  83. }
  84. os << ")\n\n";
  85. }
  86. }
  87. void cmExportCMakeConfigGenerator::SetImportLinkInterface(
  88. std::string const& config, std::string const& suffix,
  89. cmGeneratorExpression::PreprocessContext preprocessRule,
  90. cmGeneratorTarget const* target, ImportPropertyMap& properties)
  91. {
  92. // Add the transitive link dependencies for this configuration.
  93. cmLinkInterface const* iface = target->GetLinkInterface(config, target);
  94. if (!iface) {
  95. return;
  96. }
  97. if (iface->ImplementationIsInterface) {
  98. // Policy CMP0022 must not be NEW.
  99. this->SetImportLinkProperty(
  100. suffix, target, "IMPORTED_LINK_INTERFACE_LIBRARIES", iface->Libraries,
  101. properties, ImportLinkPropertyTargetNames::Yes);
  102. return;
  103. }
  104. cmValue propContent;
  105. if (cmValue prop_suffixed =
  106. target->GetProperty("LINK_INTERFACE_LIBRARIES" + suffix)) {
  107. propContent = prop_suffixed;
  108. } else if (cmValue prop = target->GetProperty("LINK_INTERFACE_LIBRARIES")) {
  109. propContent = prop;
  110. } else {
  111. return;
  112. }
  113. bool const newCMP0022Behavior =
  114. target->GetPolicyStatusCMP0022() != cmPolicies::WARN &&
  115. target->GetPolicyStatusCMP0022() != cmPolicies::OLD;
  116. if (newCMP0022Behavior && !this->ExportOld) {
  117. cmLocalGenerator* lg = target->GetLocalGenerator();
  118. std::ostringstream e;
  119. e << "Target \"" << target->GetName()
  120. << "\" has policy CMP0022 enabled, "
  121. "but also has old-style LINK_INTERFACE_LIBRARIES properties "
  122. "populated, but it was exported without the "
  123. "EXPORT_LINK_INTERFACE_LIBRARIES to export the old-style properties";
  124. lg->IssueMessage(MessageType::FATAL_ERROR, e.str());
  125. return;
  126. }
  127. if (propContent->empty()) {
  128. properties["IMPORTED_LINK_INTERFACE_LIBRARIES" + suffix].clear();
  129. return;
  130. }
  131. std::string prepro =
  132. cmGeneratorExpression::Preprocess(*propContent, preprocessRule);
  133. if (!prepro.empty()) {
  134. this->ResolveTargetsInGeneratorExpressions(prepro, target,
  135. ReplaceFreeTargets);
  136. properties["IMPORTED_LINK_INTERFACE_LIBRARIES" + suffix] = prepro;
  137. }
  138. }
  139. void cmExportCMakeConfigGenerator::GeneratePolicyHeaderCode(std::ostream& os)
  140. {
  141. // Protect that file against use with older CMake versions.
  142. /* clang-format off */
  143. os << "# Generated by CMake\n\n";
  144. os << "if(\"${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}\" LESS 2.8)\n"
  145. << " message(FATAL_ERROR \"CMake >= "
  146. << this->RequiredCMakeVersionMajor << '.'
  147. << this->RequiredCMakeVersionMinor << '.'
  148. << this->RequiredCMakeVersionPatch << " required\")\n"
  149. << "endif()\n"
  150. << "if(CMAKE_VERSION VERSION_LESS \""
  151. << this->RequiredCMakeVersionMajor << '.'
  152. << this->RequiredCMakeVersionMinor << '.'
  153. << this->RequiredCMakeVersionPatch << "\")\n"
  154. << " message(FATAL_ERROR \"CMake >= "
  155. << this->RequiredCMakeVersionMajor << '.'
  156. << this->RequiredCMakeVersionMinor << '.'
  157. << this->RequiredCMakeVersionPatch << " required\")\n"
  158. << "endif()\n";
  159. /* clang-format on */
  160. // Isolate the file policy level.
  161. // Support CMake versions as far back as the
  162. // RequiredCMakeVersion{Major,Minor,Patch}, but also support using NEW
  163. // policy settings for up to CMake 3.29 (this upper limit may be reviewed
  164. // and increased from time to time). This reduces the opportunity for CMake
  165. // warnings when an older export file is later used with newer CMake
  166. // versions.
  167. /* clang-format off */
  168. os << "cmake_policy(PUSH)\n"
  169. << "cmake_policy(VERSION "
  170. << this->RequiredCMakeVersionMajor << '.'
  171. << this->RequiredCMakeVersionMinor << '.'
  172. << this->RequiredCMakeVersionPatch << "...3.29)\n";
  173. /* clang-format on */
  174. }
  175. void cmExportCMakeConfigGenerator::GeneratePolicyFooterCode(std::ostream& os)
  176. {
  177. os << "cmake_policy(POP)\n";
  178. }
  179. void cmExportCMakeConfigGenerator::GenerateImportHeaderCode(
  180. std::ostream& os, std::string const& config)
  181. {
  182. os << "#----------------------------------------------------------------\n"
  183. << "# Generated CMake target import file";
  184. if (!config.empty()) {
  185. os << " for configuration \"" << config << "\".\n";
  186. } else {
  187. os << ".\n";
  188. }
  189. os << "#----------------------------------------------------------------\n"
  190. << "\n";
  191. this->GenerateImportVersionCode(os);
  192. }
  193. void cmExportCMakeConfigGenerator::GenerateImportFooterCode(std::ostream& os)
  194. {
  195. os << "# Commands beyond this point should not need to know the version.\n"
  196. << "set(CMAKE_IMPORT_FILE_VERSION)\n";
  197. }
  198. void cmExportCMakeConfigGenerator::GenerateImportVersionCode(std::ostream& os)
  199. {
  200. // Store an import file format version. This will let us change the
  201. // format later while still allowing old import files to work.
  202. /* clang-format off */
  203. os << "# Commands may need to know the format version.\n"
  204. << "set(CMAKE_IMPORT_FILE_VERSION 1)\n"
  205. << "\n";
  206. /* clang-format on */
  207. }
  208. void cmExportCMakeConfigGenerator::GenerateExpectedTargetsCode(
  209. std::ostream& os, std::string const& expectedTargets)
  210. {
  211. /* clang-format off */
  212. os << "# Protect against multiple inclusion, which would fail when already "
  213. "imported targets are added once more.\n"
  214. "set(_cmake_targets_defined \"\")\n"
  215. "set(_cmake_targets_not_defined \"\")\n"
  216. "set(_cmake_expected_targets \"\")\n"
  217. "foreach(_cmake_expected_target IN ITEMS " << expectedTargets << ")\n"
  218. " list(APPEND _cmake_expected_targets \"${_cmake_expected_target}\")\n"
  219. " if(TARGET \"${_cmake_expected_target}\")\n"
  220. " list(APPEND _cmake_targets_defined \"${_cmake_expected_target}\")\n"
  221. " else()\n"
  222. " list(APPEND _cmake_targets_not_defined \"${_cmake_expected_target}\")\n"
  223. " endif()\n"
  224. "endforeach()\n"
  225. "unset(_cmake_expected_target)\n"
  226. "if(_cmake_targets_defined STREQUAL _cmake_expected_targets)\n"
  227. " unset(_cmake_targets_defined)\n"
  228. " unset(_cmake_targets_not_defined)\n"
  229. " unset(_cmake_expected_targets)\n"
  230. " unset(CMAKE_IMPORT_FILE_VERSION)\n"
  231. " cmake_policy(POP)\n"
  232. " return()\n"
  233. "endif()\n"
  234. "if(NOT _cmake_targets_defined STREQUAL \"\")\n"
  235. " string(REPLACE \";\" \", \" _cmake_targets_defined_text \"${_cmake_targets_defined}\")\n"
  236. " string(REPLACE \";\" \", \" _cmake_targets_not_defined_text \"${_cmake_targets_not_defined}\")\n"
  237. " message(FATAL_ERROR \"Some (but not all) targets in this export "
  238. "set were already defined.\\nTargets Defined: ${_cmake_targets_defined_text}\\n"
  239. "Targets not yet defined: ${_cmake_targets_not_defined_text}\\n\")\n"
  240. "endif()\n"
  241. "unset(_cmake_targets_defined)\n"
  242. "unset(_cmake_targets_not_defined)\n"
  243. "unset(_cmake_expected_targets)\n"
  244. "\n\n";
  245. /* clang-format on */
  246. }
  247. void cmExportCMakeConfigGenerator::GenerateImportTargetCode(
  248. std::ostream& os, cmGeneratorTarget const* target,
  249. cmStateEnums::TargetType targetType)
  250. {
  251. // Construct the imported target name.
  252. std::string targetName = this->Namespace;
  253. targetName += target->GetExportName();
  254. // Create the imported target.
  255. os << "# Create imported target " << targetName << "\n";
  256. switch (targetType) {
  257. case cmStateEnums::EXECUTABLE:
  258. os << "add_executable(" << targetName << " IMPORTED)\n";
  259. break;
  260. case cmStateEnums::STATIC_LIBRARY:
  261. os << "add_library(" << targetName << " STATIC IMPORTED)\n";
  262. break;
  263. case cmStateEnums::SHARED_LIBRARY:
  264. os << "add_library(" << targetName << " SHARED IMPORTED)\n";
  265. break;
  266. case cmStateEnums::MODULE_LIBRARY:
  267. os << "add_library(" << targetName << " MODULE IMPORTED)\n";
  268. break;
  269. case cmStateEnums::UNKNOWN_LIBRARY:
  270. os << "add_library(" << targetName << " UNKNOWN IMPORTED)\n";
  271. break;
  272. case cmStateEnums::OBJECT_LIBRARY:
  273. os << "add_library(" << targetName << " OBJECT IMPORTED)\n";
  274. break;
  275. case cmStateEnums::INTERFACE_LIBRARY:
  276. os << "add_library(" << targetName << " INTERFACE IMPORTED)\n";
  277. break;
  278. default: // should never happen
  279. break;
  280. }
  281. // Mark the imported executable if it has exports.
  282. if (target->IsExecutableWithExports() ||
  283. (target->IsSharedLibraryWithExports() && target->HasImportLibrary(""))) {
  284. os << "set_property(TARGET " << targetName
  285. << " PROPERTY ENABLE_EXPORTS 1)\n";
  286. }
  287. // Mark the imported library if it is a framework.
  288. if (target->IsFrameworkOnApple()) {
  289. os << "set_property(TARGET " << targetName << " PROPERTY FRAMEWORK 1)\n";
  290. }
  291. // Mark the imported executable if it is an application bundle.
  292. if (target->IsAppBundleOnApple()) {
  293. os << "set_property(TARGET " << targetName
  294. << " PROPERTY MACOSX_BUNDLE 1)\n";
  295. }
  296. if (target->IsCFBundleOnApple()) {
  297. os << "set_property(TARGET " << targetName << " PROPERTY BUNDLE 1)\n";
  298. }
  299. // generate DEPRECATION
  300. if (target->IsDeprecated()) {
  301. os << "set_property(TARGET " << targetName << " PROPERTY DEPRECATION "
  302. << cmExportFileGeneratorEscape(target->GetDeprecation()) << ")\n";
  303. }
  304. if (target->GetPropertyAsBool("IMPORTED_NO_SYSTEM")) {
  305. os << "set_property(TARGET " << targetName
  306. << " PROPERTY IMPORTED_NO_SYSTEM 1)\n";
  307. }
  308. if (target->GetPropertyAsBool("EXPORT_NO_SYSTEM")) {
  309. os << "set_property(TARGET " << targetName << " PROPERTY SYSTEM 0)\n";
  310. }
  311. os << "\n";
  312. }
  313. void cmExportCMakeConfigGenerator::GenerateImportPropertyCode(
  314. std::ostream& os, std::string const& config, std::string const& suffix,
  315. cmGeneratorTarget const* target, ImportPropertyMap const& properties,
  316. std::string const& importedXcFrameworkLocation)
  317. {
  318. // Construct the imported target name.
  319. std::string targetName = this->Namespace;
  320. targetName += target->GetExportName();
  321. // Set the import properties.
  322. os << "# Import target \"" << targetName << "\" for configuration \""
  323. << config << "\"\n";
  324. os << "set_property(TARGET " << targetName
  325. << " APPEND PROPERTY IMPORTED_CONFIGURATIONS ";
  326. if (!config.empty()) {
  327. os << cmSystemTools::UpperCase(config);
  328. } else {
  329. os << "NOCONFIG";
  330. }
  331. os << ")\n";
  332. os << "set_target_properties(" << targetName << " PROPERTIES\n";
  333. std::string importedLocationProp = cmStrCat("IMPORTED_LOCATION", suffix);
  334. for (auto const& property : properties) {
  335. if (importedXcFrameworkLocation.empty() ||
  336. property.first != importedLocationProp) {
  337. os << " " << property.first << " "
  338. << cmExportFileGeneratorEscape(property.second) << "\n";
  339. }
  340. }
  341. os << " )\n";
  342. if (!importedXcFrameworkLocation.empty()) {
  343. auto importedLocationIt = properties.find(importedLocationProp);
  344. if (importedLocationIt != properties.end()) {
  345. os << "if(NOT CMAKE_VERSION VERSION_LESS \"3.28\" AND IS_DIRECTORY "
  346. << cmExportFileGeneratorEscape(importedXcFrameworkLocation)
  347. << ")\n"
  348. " set_property(TARGET "
  349. << targetName << " PROPERTY " << importedLocationProp << " "
  350. << cmExportFileGeneratorEscape(importedXcFrameworkLocation)
  351. << ")\nelse()\n set_property(TARGET " << targetName << " PROPERTY "
  352. << importedLocationProp << " "
  353. << cmExportFileGeneratorEscape(importedLocationIt->second)
  354. << ")\nendif()\n";
  355. }
  356. }
  357. os << "\n";
  358. }
  359. void cmExportCMakeConfigGenerator::GenerateFindDependencyCalls(
  360. std::ostream& os)
  361. {
  362. os << "include(CMakeFindDependencyMacro)\n";
  363. std::map<std::string, cmExportSet::PackageDependency> packageDependencies;
  364. auto* exportSet = this->GetExportSet();
  365. if (exportSet) {
  366. packageDependencies = exportSet->GetPackageDependencies();
  367. }
  368. for (cmGeneratorTarget const* gt : this->ExternalTargets) {
  369. std::string findPackageName;
  370. auto exportFindPackageName = gt->GetProperty("EXPORT_FIND_PACKAGE_NAME");
  371. cmFindPackageStack pkgStack = gt->Target->GetFindPackageStack();
  372. if (!exportFindPackageName.IsEmpty()) {
  373. findPackageName = *exportFindPackageName;
  374. } else {
  375. if (!pkgStack.Empty()) {
  376. cmFindPackageCall const& fpc = pkgStack.Top();
  377. findPackageName = fpc.Name;
  378. }
  379. }
  380. if (!findPackageName.empty()) {
  381. auto& dep = packageDependencies[findPackageName];
  382. if (!pkgStack.Empty()) {
  383. dep.FindPackageIndex = pkgStack.Top().Index;
  384. }
  385. if (dep.Enabled == cmExportSet::PackageDependencyExportEnabled::Auto) {
  386. dep.Enabled = cmExportSet::PackageDependencyExportEnabled::On;
  387. }
  388. }
  389. }
  390. std::vector<std::pair<std::string, cmExportSet::PackageDependency>>
  391. packageDependenciesSorted(packageDependencies.begin(),
  392. packageDependencies.end());
  393. std::sort(
  394. packageDependenciesSorted.begin(), packageDependenciesSorted.end(),
  395. [](std::pair<std::string, cmExportSet::PackageDependency> const& lhs,
  396. std::pair<std::string, cmExportSet::PackageDependency> const& rhs)
  397. -> bool {
  398. if (lhs.second.SpecifiedIndex) {
  399. if (rhs.second.SpecifiedIndex) {
  400. return lhs.second.SpecifiedIndex < rhs.second.SpecifiedIndex;
  401. }
  402. assert(rhs.second.FindPackageIndex);
  403. return true;
  404. }
  405. assert(lhs.second.FindPackageIndex);
  406. if (rhs.second.SpecifiedIndex) {
  407. return false;
  408. }
  409. assert(rhs.second.FindPackageIndex);
  410. return lhs.second.FindPackageIndex < rhs.second.FindPackageIndex;
  411. });
  412. for (auto const& it : packageDependenciesSorted) {
  413. if (it.second.Enabled == cmExportSet::PackageDependencyExportEnabled::On) {
  414. os << "find_dependency(" << it.first;
  415. for (auto const& arg : it.second.ExtraArguments) {
  416. os << " " << cmOutputConverter::EscapeForCMake(arg);
  417. }
  418. os << ")\n";
  419. }
  420. }
  421. os << "\n\n";
  422. }
  423. void cmExportCMakeConfigGenerator::GenerateMissingTargetsCheckCode(
  424. std::ostream& os)
  425. {
  426. if (this->MissingTargets.empty()) {
  427. /* clang-format off */
  428. os << "# This file does not depend on other imported targets which have\n"
  429. "# been exported from the same project but in a separate "
  430. "export set.\n\n";
  431. /* clang-format on */
  432. return;
  433. }
  434. /* clang-format off */
  435. os << "# Make sure the targets which have been exported in some other\n"
  436. "# export set exist.\n"
  437. "unset(${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE_targets)\n"
  438. "foreach(_target ";
  439. /* clang-format on */
  440. std::set<std::string> emitted;
  441. for (std::string const& missingTarget : this->MissingTargets) {
  442. if (emitted.insert(missingTarget).second) {
  443. os << "\"" << missingTarget << "\" ";
  444. }
  445. }
  446. /* clang-format off */
  447. os << ")\n"
  448. " if(NOT TARGET \"${_target}\" )\n"
  449. " set(${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE_targets \""
  450. "${${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE_targets} ${_target}\")"
  451. "\n"
  452. " endif()\n"
  453. "endforeach()\n"
  454. "\n"
  455. "if(DEFINED ${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE_targets)\n"
  456. " if(CMAKE_FIND_PACKAGE_NAME)\n"
  457. " set( ${CMAKE_FIND_PACKAGE_NAME}_FOUND FALSE)\n"
  458. " set( ${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE "
  459. "\"The following imported targets are "
  460. "referenced, but are missing: "
  461. "${${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE_targets}\")\n"
  462. " else()\n"
  463. " message(FATAL_ERROR \"The following imported targets are "
  464. "referenced, but are missing: "
  465. "${${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE_targets}\")\n"
  466. " endif()\n"
  467. "endif()\n"
  468. "unset(${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE_targets)\n"
  469. "\n";
  470. /* clang-format on */
  471. }
  472. void cmExportCMakeConfigGenerator::GenerateImportedFileCheckLoop(
  473. std::ostream& os)
  474. {
  475. // Add code which verifies at cmake time that the file which is being
  476. // imported actually exists on disk. This should in theory always be theory
  477. // case, but still when packages are split into normal and development
  478. // packages this might get broken (e.g. the Config.cmake could be part of
  479. // the non-development package, something similar happened to me without
  480. // on SUSE with a mysql pkg-config file, which claimed everything is fine,
  481. // but the development package was not installed.).
  482. /* clang-format off */
  483. os << "# Loop over all imported files and verify that they actually exist\n"
  484. "foreach(_cmake_target IN LISTS _cmake_import_check_targets)\n"
  485. " if(CMAKE_VERSION VERSION_LESS \"3.28\"\n"
  486. " OR NOT DEFINED "
  487. "_cmake_import_check_xcframework_for_${_cmake_target}\n"
  488. " OR NOT IS_DIRECTORY "
  489. "\"${_cmake_import_check_xcframework_for_${_cmake_target}}\")\n"
  490. " foreach(_cmake_file IN LISTS "
  491. "\"_cmake_import_check_files_for_${_cmake_target}\")\n"
  492. " if(NOT EXISTS \"${_cmake_file}\")\n"
  493. " message(FATAL_ERROR \"The imported target "
  494. "\\\"${_cmake_target}\\\" references the file\n"
  495. " \\\"${_cmake_file}\\\"\n"
  496. "but this file does not exist. Possible reasons include:\n"
  497. "* The file was deleted, renamed, or moved to another location.\n"
  498. "* An install or uninstall procedure did not complete successfully.\n"
  499. "* The installation package was faulty and contained\n"
  500. " \\\"${CMAKE_CURRENT_LIST_FILE}\\\"\n"
  501. "but not all the files it references.\n"
  502. "\")\n"
  503. " endif()\n"
  504. " endforeach()\n"
  505. " endif()\n"
  506. " unset(_cmake_file)\n"
  507. " unset(\"_cmake_import_check_files_for_${_cmake_target}\")\n"
  508. "endforeach()\n"
  509. "unset(_cmake_target)\n"
  510. "unset(_cmake_import_check_targets)\n"
  511. "\n";
  512. /* clang-format on */
  513. }
  514. void cmExportCMakeConfigGenerator::GenerateImportedFileChecksCode(
  515. std::ostream& os, cmGeneratorTarget const* target,
  516. ImportPropertyMap const& properties,
  517. std::set<std::string> const& importedLocations,
  518. std::string const& importedXcFrameworkLocation)
  519. {
  520. // Construct the imported target name.
  521. std::string targetName = cmStrCat(this->Namespace, target->GetExportName());
  522. os << "list(APPEND _cmake_import_check_targets " << targetName << " )\n";
  523. if (!importedXcFrameworkLocation.empty()) {
  524. os << "set(_cmake_import_check_xcframework_for_" << targetName << ' '
  525. << cmExportFileGeneratorEscape(importedXcFrameworkLocation) << ")\n";
  526. }
  527. os << "list(APPEND _cmake_import_check_files_for_" << targetName << " ";
  528. for (std::string const& li : importedLocations) {
  529. auto pi = properties.find(li);
  530. if (pi != properties.end()) {
  531. os << cmExportFileGeneratorEscape(pi->second) << " ";
  532. }
  533. }
  534. os << ")\n\n";
  535. }
  536. void cmExportCMakeConfigGenerator::GenerateTargetFileSets(
  537. cmGeneratorTarget* gte, std::ostream& os, cmTargetExport const* te)
  538. {
  539. auto interfaceFileSets = gte->Target->GetAllInterfaceFileSets();
  540. if (!interfaceFileSets.empty()) {
  541. std::string targetName = cmStrCat(this->Namespace, gte->GetExportName());
  542. os << "if(NOT CMAKE_VERSION VERSION_LESS \"3.23.0\")\n"
  543. " target_sources("
  544. << targetName << "\n";
  545. for (auto const& name : interfaceFileSets) {
  546. auto* fileSet = gte->Target->GetFileSet(name);
  547. if (!fileSet) {
  548. gte->Makefile->IssueMessage(
  549. MessageType::FATAL_ERROR,
  550. cmStrCat("File set \"", name,
  551. "\" is listed in interface file sets of ", gte->GetName(),
  552. " but has not been created"));
  553. return;
  554. }
  555. os << " INTERFACE"
  556. << "\n FILE_SET " << cmOutputConverter::EscapeForCMake(name)
  557. << "\n TYPE "
  558. << cmOutputConverter::EscapeForCMake(fileSet->GetType())
  559. << "\n BASE_DIRS "
  560. << this->GetFileSetDirectories(gte, fileSet, te) << "\n FILES "
  561. << this->GetFileSetFiles(gte, fileSet, te) << "\n";
  562. }
  563. os << " )\nelse()\n set_property(TARGET " << targetName
  564. << "\n APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES";
  565. for (auto const& name : interfaceFileSets) {
  566. auto* fileSet = gte->Target->GetFileSet(name);
  567. if (!fileSet) {
  568. gte->Makefile->IssueMessage(
  569. MessageType::FATAL_ERROR,
  570. cmStrCat("File set \"", name,
  571. "\" is listed in interface file sets of ", gte->GetName(),
  572. " but has not been created"));
  573. return;
  574. }
  575. if (fileSet->GetType() == "HEADERS"_s) {
  576. os << "\n " << this->GetFileSetDirectories(gte, fileSet, te);
  577. }
  578. }
  579. os << "\n )\nendif()\n\n";
  580. }
  581. }
  582. std::string cmExportCMakeConfigGenerator::GetCxxModuleFile(
  583. std::string const& name) const
  584. {
  585. auto const& cxxModuleDirname = this->GetCxxModulesDirectory();
  586. if (cxxModuleDirname.empty()) {
  587. return {};
  588. }
  589. return cmStrCat(cmSystemTools::GetFilenamePath(this->MainImportFile), '/',
  590. cxxModuleDirname, "/cxx-modules-", name, ".cmake");
  591. }
  592. void cmExportCMakeConfigGenerator::GenerateCxxModuleInformation(
  593. std::string const& name, std::ostream& os)
  594. {
  595. auto const cxx_module_dirname = this->GetCxxModulesDirectory();
  596. if (cxx_module_dirname.empty()) {
  597. return;
  598. }
  599. // Write the include.
  600. os << "# Include C++ module properties\n"
  601. << "include(\"${CMAKE_CURRENT_LIST_DIR}/" << cxx_module_dirname
  602. << "/cxx-modules-" << name << ".cmake\")\n\n";
  603. // Include all configuration-specific include files.
  604. cmGeneratedFileStream ap(this->GetCxxModuleFile(name), true);
  605. ap.SetCopyIfDifferent(true);
  606. this->GenerateCxxModuleConfigInformation(name, ap);
  607. }
  608. void cmExportCMakeConfigGenerator::SetRequiredCMakeVersion(unsigned int major,
  609. unsigned int minor,
  610. unsigned int patch)
  611. {
  612. if (CMake_VERSION_ENCODE(major, minor, patch) >
  613. CMake_VERSION_ENCODE(this->RequiredCMakeVersionMajor,
  614. this->RequiredCMakeVersionMinor,
  615. this->RequiredCMakeVersionPatch)) {
  616. this->RequiredCMakeVersionMajor = major;
  617. this->RequiredCMakeVersionMinor = minor;
  618. this->RequiredCMakeVersionPatch = patch;
  619. }
  620. }