cmExportCMakeConfigGenerator.cxx 25 KB

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