cmExportInstallCMakeConfigGenerator.cxx 16 KB

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