cmExportInstallCMakeConfigGenerator.cxx 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474
  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 "cmExportInstallCMakeConfigGenerator.h"
  4. #include <algorithm>
  5. #include <functional>
  6. #include <map>
  7. #include <memory>
  8. #include <set>
  9. #include <sstream>
  10. #include <utility>
  11. #include <vector>
  12. #include <cm/string_view>
  13. #include <cmext/string_view>
  14. #include "cmExportFileGenerator.h"
  15. #include "cmExportSet.h"
  16. #include "cmFileSet.h"
  17. #include "cmGenExContext.h"
  18. #include "cmGeneratedFileStream.h"
  19. #include "cmGeneratorExpression.h"
  20. #include "cmGeneratorTarget.h"
  21. #include "cmInstallExportGenerator.h"
  22. #include "cmInstallFileSetGenerator.h"
  23. #include "cmLocalGenerator.h"
  24. #include "cmMakefile.h"
  25. #include "cmMessageType.h"
  26. #include "cmOutputConverter.h"
  27. #include "cmStateTypes.h"
  28. #include "cmStringAlgorithms.h"
  29. #include "cmSystemTools.h"
  30. #include "cmTargetExport.h"
  31. #include "cmValue.h"
  32. cmExportInstallCMakeConfigGenerator::cmExportInstallCMakeConfigGenerator(
  33. cmInstallExportGenerator* iegen)
  34. : cmExportInstallFileGenerator(iegen)
  35. {
  36. }
  37. std::string cmExportInstallCMakeConfigGenerator::GetConfigImportFileGlob()
  38. const
  39. {
  40. std::string glob = cmStrCat(this->FileBase, "-*", this->FileExt);
  41. return glob;
  42. }
  43. bool cmExportInstallCMakeConfigGenerator::GenerateMainFile(std::ostream& os)
  44. {
  45. std::vector<cmTargetExport const*> allTargets;
  46. {
  47. std::string expectedTargets;
  48. std::string sep;
  49. auto visitor = [&](cmTargetExport const* te) {
  50. allTargets.push_back(te);
  51. expectedTargets += sep + this->Namespace + te->Target->GetExportName();
  52. sep = " ";
  53. };
  54. if (!this->CollectExports(visitor)) {
  55. return false;
  56. }
  57. this->GenerateExpectedTargetsCode(os, expectedTargets);
  58. }
  59. // Compute the relative import prefix for the file
  60. this->GenerateImportPrefix(os);
  61. bool requiresConfigFiles = false;
  62. // Create all the imported targets.
  63. for (cmTargetExport const* te : allTargets) {
  64. cmGeneratorTarget* gt = te->Target;
  65. cmStateEnums::TargetType targetType = this->GetExportTargetType(te);
  66. requiresConfigFiles =
  67. requiresConfigFiles || targetType != cmStateEnums::INTERFACE_LIBRARY;
  68. this->GenerateImportTargetCode(os, gt, targetType);
  69. ImportPropertyMap properties;
  70. if (!this->PopulateInterfaceProperties(te, properties)) {
  71. return false;
  72. }
  73. if (this->PopulateInterfaceLinkLibrariesProperty(
  74. gt, cmGeneratorExpression::InstallInterface, properties) &&
  75. !this->ExportOld) {
  76. this->SetRequiredCMakeVersion(2, 8, 12);
  77. }
  78. if (targetType == cmStateEnums::INTERFACE_LIBRARY) {
  79. this->SetRequiredCMakeVersion(3, 0, 0);
  80. }
  81. if (gt->GetProperty("INTERFACE_SOURCES")) {
  82. // We can only generate INTERFACE_SOURCES in CMake 3.3, but CMake 3.1
  83. // can consume them.
  84. this->SetRequiredCMakeVersion(3, 1, 0);
  85. }
  86. this->GenerateInterfaceProperties(gt, os, properties);
  87. this->GenerateTargetFileSets(gt, os, te);
  88. }
  89. this->LoadConfigFiles(os);
  90. bool result = true;
  91. std::string cxx_modules_name = this->GetExportSet()->GetName();
  92. this->GenerateCxxModuleInformation(cxx_modules_name, os);
  93. if (requiresConfigFiles) {
  94. for (std::string const& c : this->Configurations) {
  95. if (!this->GenerateImportCxxModuleConfigTargetInclusion(cxx_modules_name,
  96. c)) {
  97. result = false;
  98. }
  99. }
  100. }
  101. this->CleanupTemporaryVariables(os);
  102. this->GenerateImportedFileCheckLoop(os);
  103. // Generate an import file for each configuration.
  104. // Don't do this if we only export INTERFACE_LIBRARY targets.
  105. if (requiresConfigFiles) {
  106. for (std::string const& c : this->Configurations) {
  107. if (!this->GenerateImportFileConfig(c)) {
  108. result = false;
  109. }
  110. }
  111. }
  112. this->GenerateMissingTargetsCheckCode(os);
  113. return result;
  114. }
  115. void cmExportInstallCMakeConfigGenerator::GenerateImportPrefix(
  116. std::ostream& os)
  117. {
  118. // Set an _IMPORT_PREFIX variable for import location properties
  119. // to reference if they are relative to the install prefix.
  120. std::string installPrefix =
  121. this->IEGen->GetLocalGenerator()->GetMakefile()->GetSafeDefinition(
  122. "CMAKE_INSTALL_PREFIX");
  123. std::string const& expDest = this->IEGen->GetDestination();
  124. if (cmSystemTools::FileIsFullPath(expDest)) {
  125. // The export file is being installed to an absolute path so the
  126. // package is not relocatable. Use the configured install prefix.
  127. /* clang-format off */
  128. os <<
  129. "# The installation prefix configured by this project.\n"
  130. "set(_IMPORT_PREFIX \"" << installPrefix << "\")\n"
  131. "\n";
  132. /* clang-format on */
  133. } else {
  134. // Add code to compute the installation prefix relative to the
  135. // import file location.
  136. std::string absDest = installPrefix + "/" + expDest;
  137. std::string absDestS = absDest + "/";
  138. os << "# Compute the installation prefix relative to this file.\n"
  139. << "get_filename_component(_IMPORT_PREFIX"
  140. << " \"${CMAKE_CURRENT_LIST_FILE}\" PATH)\n";
  141. if (cmHasLiteralPrefix(absDestS, "/lib/") ||
  142. cmHasLiteralPrefix(absDestS, "/lib64/") ||
  143. cmHasLiteralPrefix(absDestS, "/libx32/") ||
  144. cmHasLiteralPrefix(absDestS, "/usr/lib/") ||
  145. cmHasLiteralPrefix(absDestS, "/usr/lib64/") ||
  146. cmHasLiteralPrefix(absDestS, "/usr/libx32/")) {
  147. // Handle "/usr move" symlinks created by some Linux distros.
  148. /* clang-format off */
  149. os <<
  150. "# Use original install prefix when loaded through a\n"
  151. "# cross-prefix symbolic link such as /lib -> /usr/lib.\n"
  152. "get_filename_component(_realCurr \"${_IMPORT_PREFIX}\" REALPATH)\n"
  153. "get_filename_component(_realOrig \"" << absDest << "\" REALPATH)\n"
  154. "if(_realCurr STREQUAL _realOrig)\n"
  155. " set(_IMPORT_PREFIX \"" << absDest << "\")\n"
  156. "endif()\n"
  157. "unset(_realOrig)\n"
  158. "unset(_realCurr)\n";
  159. /* clang-format on */
  160. }
  161. std::string dest = expDest;
  162. while (!dest.empty()) {
  163. os << "get_filename_component(_IMPORT_PREFIX \"${_IMPORT_PREFIX}\" "
  164. "PATH)\n";
  165. dest = cmSystemTools::GetFilenamePath(dest);
  166. }
  167. os << "if(_IMPORT_PREFIX STREQUAL \"/\")\n"
  168. << " set(_IMPORT_PREFIX \"\")\n"
  169. << "endif()\n"
  170. << "\n";
  171. }
  172. }
  173. void cmExportInstallCMakeConfigGenerator::CleanupTemporaryVariables(
  174. std::ostream& os)
  175. {
  176. /* clang-format off */
  177. os << "# Cleanup temporary variables.\n"
  178. << "set(_IMPORT_PREFIX)\n"
  179. << "\n";
  180. /* clang-format on */
  181. }
  182. void cmExportInstallCMakeConfigGenerator::LoadConfigFiles(std::ostream& os)
  183. {
  184. // Now load per-configuration properties for them.
  185. /* clang-format off */
  186. os << "# Load information for each installed configuration.\n"
  187. << "file(GLOB _cmake_config_files \"${CMAKE_CURRENT_LIST_DIR}/"
  188. << this->GetConfigImportFileGlob() << "\")\n"
  189. << "foreach(_cmake_config_file IN LISTS _cmake_config_files)\n"
  190. << " include(\"${_cmake_config_file}\")\n"
  191. << "endforeach()\n"
  192. << "unset(_cmake_config_file)\n"
  193. << "unset(_cmake_config_files)\n"
  194. << "\n";
  195. /* clang-format on */
  196. }
  197. void cmExportInstallCMakeConfigGenerator::GenerateImportConfig(
  198. std::ostream& os, std::string const& config)
  199. {
  200. // Start with the import file header.
  201. this->GenerateImportHeaderCode(os, config);
  202. // Generate the per-config target information.
  203. this->cmExportFileGenerator::GenerateImportConfig(os, config);
  204. // End with the import file footer.
  205. this->GenerateImportFooterCode(os);
  206. }
  207. void cmExportInstallCMakeConfigGenerator::GenerateImportTargetsConfig(
  208. std::ostream& os, std::string const& config, std::string const& suffix)
  209. {
  210. // Add each target in the set to the export.
  211. for (std::unique_ptr<cmTargetExport> const& te :
  212. this->GetExportSet()->GetTargetExports()) {
  213. // Collect import properties for this target.
  214. if (this->GetExportTargetType(te.get()) ==
  215. cmStateEnums::INTERFACE_LIBRARY) {
  216. continue;
  217. }
  218. ImportPropertyMap properties;
  219. std::set<std::string> importedLocations;
  220. this->PopulateImportProperties(config, suffix, te.get(), properties,
  221. importedLocations);
  222. // If any file location was set for the target add it to the
  223. // import file.
  224. if (!properties.empty()) {
  225. cmGeneratorTarget const* const gtgt = te->Target;
  226. std::string const importedXcFrameworkLocation =
  227. this->GetImportXcFrameworkLocation(config, te.get());
  228. this->SetImportLinkInterface(config, suffix,
  229. cmGeneratorExpression::InstallInterface,
  230. gtgt, properties);
  231. this->GenerateImportPropertyCode(os, config, suffix, gtgt, properties,
  232. importedXcFrameworkLocation);
  233. this->GenerateImportedFileChecksCode(
  234. os, gtgt, properties, importedLocations, importedXcFrameworkLocation);
  235. }
  236. }
  237. }
  238. namespace {
  239. bool EntryIsContextSensitive(
  240. std::unique_ptr<cmCompiledGeneratorExpression> const& cge)
  241. {
  242. return cge->GetHadContextSensitiveCondition();
  243. }
  244. }
  245. std::string cmExportInstallCMakeConfigGenerator::GetFileSetDirectories(
  246. cmGeneratorTarget* gte, cmFileSet* fileSet, cmTargetExport const* te)
  247. {
  248. std::vector<std::string> resultVector;
  249. auto configs =
  250. gte->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
  251. cmGeneratorExpression ge(*gte->Makefile->GetCMakeInstance());
  252. auto cge =
  253. ge.Parse(te->FileSetGenerators.at(fileSet->GetName())->GetDestination());
  254. for (auto const& config : configs) {
  255. auto unescapedDest = cge->Evaluate(gte->LocalGenerator, config, gte);
  256. auto dest = cmOutputConverter::EscapeForCMake(
  257. unescapedDest, cmOutputConverter::WrapQuotes::NoWrap);
  258. if (!cmSystemTools::FileIsFullPath(unescapedDest)) {
  259. dest = cmStrCat("${_IMPORT_PREFIX}/", dest);
  260. }
  261. auto const& type = fileSet->GetType();
  262. // C++ modules do not support interface file sets which are dependent upon
  263. // the configuration.
  264. if (cge->GetHadContextSensitiveCondition() && type == "CXX_MODULES"_s) {
  265. auto* mf = this->IEGen->GetLocalGenerator()->GetMakefile();
  266. std::ostringstream e;
  267. e << "The \"" << gte->GetName() << "\" target's interface file set \""
  268. << fileSet->GetName() << "\" of type \"" << type
  269. << "\" contains context-sensitive base file entries which is not "
  270. "supported.";
  271. mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
  272. return std::string{};
  273. }
  274. if (cge->GetHadContextSensitiveCondition() && configs.size() != 1) {
  275. resultVector.push_back(
  276. cmStrCat("\"$<$<CONFIG:", config, ">:", dest, ">\""));
  277. } else {
  278. resultVector.emplace_back(cmStrCat('"', dest, '"'));
  279. break;
  280. }
  281. }
  282. return cmJoin(resultVector, " ");
  283. }
  284. std::string cmExportInstallCMakeConfigGenerator::GetFileSetFiles(
  285. cmGeneratorTarget* gte, cmFileSet* fileSet, cmTargetExport const* te)
  286. {
  287. std::vector<std::string> resultVector;
  288. auto configs =
  289. gte->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
  290. auto fileEntries = fileSet->CompileFileEntries();
  291. auto directoryEntries = fileSet->CompileDirectoryEntries();
  292. cmGeneratorExpression destGe(*gte->Makefile->GetCMakeInstance());
  293. auto destCge = destGe.Parse(
  294. te->FileSetGenerators.at(fileSet->GetName())->GetDestination());
  295. for (auto const& config : configs) {
  296. cm::GenEx::Context context(gte->LocalGenerator, config);
  297. auto directories =
  298. fileSet->EvaluateDirectoryEntries(directoryEntries, context, gte);
  299. std::map<std::string, std::vector<std::string>> files;
  300. for (auto const& entry : fileEntries) {
  301. fileSet->EvaluateFileEntry(directories, files, entry, context, gte);
  302. }
  303. auto unescapedDest = destCge->Evaluate(gte->LocalGenerator, config, gte);
  304. auto dest =
  305. cmStrCat(cmOutputConverter::EscapeForCMake(
  306. unescapedDest, cmOutputConverter::WrapQuotes::NoWrap),
  307. '/');
  308. if (!cmSystemTools::FileIsFullPath(unescapedDest)) {
  309. dest = cmStrCat("${_IMPORT_PREFIX}/", dest);
  310. }
  311. bool const contextSensitive = destCge->GetHadContextSensitiveCondition() ||
  312. std::any_of(directoryEntries.begin(), directoryEntries.end(),
  313. EntryIsContextSensitive) ||
  314. std::any_of(fileEntries.begin(), fileEntries.end(),
  315. EntryIsContextSensitive);
  316. auto const& type = fileSet->GetType();
  317. // C++ modules do not support interface file sets which are dependent upon
  318. // the configuration.
  319. if (contextSensitive && type == "CXX_MODULES"_s) {
  320. auto* mf = this->IEGen->GetLocalGenerator()->GetMakefile();
  321. std::ostringstream e;
  322. e << "The \"" << gte->GetName() << "\" target's interface file set \""
  323. << fileSet->GetName() << "\" of type \"" << type
  324. << "\" contains context-sensitive base file entries which is not "
  325. "supported.";
  326. mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
  327. return std::string{};
  328. }
  329. for (auto const& it : files) {
  330. auto prefix = it.first.empty() ? "" : cmStrCat(it.first, '/');
  331. for (auto const& filename : it.second) {
  332. auto relFile =
  333. cmStrCat(prefix, cmSystemTools::GetFilenameName(filename));
  334. auto escapedFile =
  335. cmStrCat(dest,
  336. cmOutputConverter::EscapeForCMake(
  337. relFile, cmOutputConverter::WrapQuotes::NoWrap));
  338. if (contextSensitive && configs.size() != 1) {
  339. resultVector.push_back(
  340. cmStrCat("\"$<$<CONFIG:", config, ">:", escapedFile, ">\""));
  341. } else {
  342. resultVector.emplace_back(cmStrCat('"', escapedFile, '"'));
  343. }
  344. }
  345. }
  346. if (!(contextSensitive && configs.size() != 1)) {
  347. break;
  348. }
  349. }
  350. return cmJoin(resultVector, " ");
  351. }
  352. std::string cmExportInstallCMakeConfigGenerator::GetCxxModulesDirectory() const
  353. {
  354. return IEGen->GetCxxModuleDirectory();
  355. }
  356. void cmExportInstallCMakeConfigGenerator::GenerateCxxModuleConfigInformation(
  357. std::string const& name, std::ostream& os) const
  358. {
  359. // Now load per-configuration properties for them.
  360. /* clang-format off */
  361. os << "# Load information for each installed configuration.\n"
  362. "file(GLOB _cmake_cxx_module_includes \"${CMAKE_CURRENT_LIST_DIR}/cxx-modules-" << name << "-*.cmake\")\n"
  363. "foreach(_cmake_cxx_module_include IN LISTS _cmake_cxx_module_includes)\n"
  364. " include(\"${_cmake_cxx_module_include}\")\n"
  365. "endforeach()\n"
  366. "unset(_cmake_cxx_module_include)\n"
  367. "unset(_cmake_cxx_module_includes)\n";
  368. /* clang-format on */
  369. }
  370. bool cmExportInstallCMakeConfigGenerator::
  371. GenerateImportCxxModuleConfigTargetInclusion(std::string const& name,
  372. std::string const& config)
  373. {
  374. auto cxx_modules_dirname = this->GetCxxModulesDirectory();
  375. if (cxx_modules_dirname.empty()) {
  376. return true;
  377. }
  378. std::string filename_config = config;
  379. if (filename_config.empty()) {
  380. filename_config = "noconfig";
  381. }
  382. std::string const dest =
  383. cmStrCat(this->FileDir, '/', cxx_modules_dirname, '/');
  384. std::string fileName =
  385. cmStrCat(dest, "cxx-modules-", name, '-', filename_config, ".cmake");
  386. cmGeneratedFileStream os(fileName, true);
  387. if (!os) {
  388. std::string se = cmSystemTools::GetLastSystemError();
  389. std::ostringstream e;
  390. e << "cannot write to file \"" << fileName << "\": " << se;
  391. cmSystemTools::Error(e.str());
  392. return false;
  393. }
  394. os.SetCopyIfDifferent(true);
  395. // Record this per-config import file.
  396. this->ConfigCxxModuleFiles[config] = fileName;
  397. auto& prop_files = this->ConfigCxxModuleTargetFiles[config];
  398. for (auto const* tgt : this->ExportedTargets) {
  399. // Only targets with C++ module sources will have a
  400. // collator-generated install script.
  401. if (!tgt->HaveCxx20ModuleSources()) {
  402. continue;
  403. }
  404. auto prop_filename = cmStrCat("target-", tgt->GetFilesystemExportName(),
  405. '-', filename_config, ".cmake");
  406. prop_files.emplace_back(cmStrCat(dest, prop_filename));
  407. os << "include(\"${CMAKE_CURRENT_LIST_DIR}/" << prop_filename << "\")\n";
  408. }
  409. return true;
  410. }