cmExportInstallCMakeConfigGenerator.cxx 16 KB

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