cmExportInstallCMakeConfigGenerator.cxx 17 KB

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