cmExportInstallPackageInfoGenerator.cxx 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  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 "cmExportInstallPackageInfoGenerator.h"
  4. #include <functional>
  5. #include <map>
  6. #include <memory>
  7. #include <set>
  8. #include <sstream>
  9. #include <utility>
  10. #include <vector>
  11. #include <cm/optional>
  12. #include <cm/string_view>
  13. #include <cmext/algorithm>
  14. #include <cmext/string_view>
  15. #include <cm3p/json/value.h>
  16. #include "cmAlgorithms.h"
  17. #include "cmExportSet.h"
  18. #include "cmFileSet.h"
  19. #include "cmGeneratorExpression.h"
  20. #include "cmGeneratorTarget.h"
  21. #include "cmInstallExportGenerator.h"
  22. #include "cmInstallFileSetGenerator.h"
  23. #include "cmList.h"
  24. #include "cmLocalGenerator.h"
  25. #include "cmMakefile.h"
  26. #include "cmMessageType.h"
  27. #include "cmOutputConverter.h"
  28. #include "cmPackageInfoArguments.h"
  29. #include "cmStateTypes.h"
  30. #include "cmStringAlgorithms.h"
  31. #include "cmSystemTools.h"
  32. #include "cmTarget.h"
  33. #include "cmTargetExport.h"
  34. cmExportInstallPackageInfoGenerator::cmExportInstallPackageInfoGenerator(
  35. cmInstallExportGenerator* iegen, cmPackageInfoArguments arguments)
  36. : cmExportPackageInfoGenerator(std::move(arguments))
  37. , cmExportInstallFileGenerator(iegen)
  38. {
  39. }
  40. std::string cmExportInstallPackageInfoGenerator::GetConfigImportFileGlob()
  41. const
  42. {
  43. std::string glob = cmStrCat(this->FileBase, "@*", this->FileExt);
  44. return glob;
  45. }
  46. std::string const& cmExportInstallPackageInfoGenerator::GetExportName() const
  47. {
  48. return this->GetPackageName();
  49. }
  50. bool cmExportInstallPackageInfoGenerator::GenerateMainFile(std::ostream& os)
  51. {
  52. std::vector<cmTargetExport const*> allTargets;
  53. {
  54. auto visitor = [&](cmTargetExport const* te) { allTargets.push_back(te); };
  55. if (!this->CollectExports(visitor)) {
  56. return false;
  57. }
  58. }
  59. if (!this->CheckPackage()) {
  60. return false;
  61. }
  62. Json::Value root = this->GeneratePackageInfo();
  63. Json::Value& components = root["components"];
  64. // Compute the relative import prefix for the file
  65. std::string const& packagePath = this->GenerateImportPrefix();
  66. if (packagePath.empty()) {
  67. return false;
  68. }
  69. root["cps_path"] = packagePath;
  70. // Create all the imported targets.
  71. for (cmTargetExport const* te : allTargets) {
  72. cmGeneratorTarget* gt = te->Target;
  73. cmStateEnums::TargetType targetType = this->GetExportTargetType(te);
  74. Json::Value* const component =
  75. this->GenerateImportTarget(components, gt, targetType);
  76. if (!component) {
  77. return false;
  78. }
  79. ImportPropertyMap properties;
  80. if (!this->PopulateInterfaceProperties(te, properties)) {
  81. return false;
  82. }
  83. this->PopulateInterfaceLinkLibrariesProperty(
  84. gt, cmGeneratorExpression::InstallInterface, properties);
  85. if (targetType != cmStateEnums::INTERFACE_LIBRARY) {
  86. this->RequiresConfigFiles = true;
  87. }
  88. // De-duplicate include directories prior to generation.
  89. auto it = properties.find("INTERFACE_INCLUDE_DIRECTORIES");
  90. if (it != properties.end()) {
  91. auto list = cmList{ it->second };
  92. list = cmList{ list.begin(), cmRemoveDuplicates(list) };
  93. properties["INTERFACE_INCLUDE_DIRECTORIES"] = list.to_string();
  94. }
  95. // Set configuration-agnostic properties for component.
  96. this->GenerateInterfaceProperties(*component, gt, properties);
  97. if (!this->GenerateFileSetProperties(*component, gt, te)) {
  98. return false;
  99. }
  100. }
  101. this->GeneratePackageRequires(root);
  102. // Write the primary packing information file.
  103. this->WritePackageInfo(root, os);
  104. bool result = true;
  105. // Generate an import file for each configuration.
  106. if (this->RequiresConfigFiles) {
  107. for (std::string const& c : this->Configurations) {
  108. if (!this->GenerateImportFileConfig(c)) {
  109. result = false;
  110. }
  111. }
  112. }
  113. return result;
  114. }
  115. void cmExportInstallPackageInfoGenerator::GenerateImportTargetsConfig(
  116. std::ostream& os, std::string const& config, std::string const& suffix)
  117. {
  118. Json::Value root;
  119. root["name"] = this->GetPackageName();
  120. root["configuration"] = (config.empty() ? "noconfig" : config);
  121. Json::Value& components = root["components"];
  122. for (auto const& te : this->GetExportSet()->GetTargetExports()) {
  123. // Collect import properties for this target.
  124. ImportPropertyMap properties;
  125. std::set<std::string> importedLocations;
  126. if (this->GetExportTargetType(te.get()) !=
  127. cmStateEnums::INTERFACE_LIBRARY) {
  128. this->PopulateImportProperties(config, suffix, te.get(), properties,
  129. importedLocations);
  130. }
  131. Json::Value component =
  132. this->GenerateInterfaceConfigProperties(suffix, properties);
  133. this->GenerateFileSetProperties(component, te->Target, te.get(), config);
  134. if (!component.empty()) {
  135. components[te->Target->GetExportName()] = std::move(component);
  136. }
  137. }
  138. this->WritePackageInfo(root, os);
  139. }
  140. std::string cmExportInstallPackageInfoGenerator::GenerateImportPrefix() const
  141. {
  142. std::string expDest = this->IEGen->GetDestination();
  143. if (cmSystemTools::FileIsFullPath(expDest)) {
  144. std::string const& installPrefix =
  145. this->IEGen->GetLocalGenerator()->GetMakefile()->GetSafeDefinition(
  146. "CMAKE_INSTALL_PREFIX");
  147. if (cmHasPrefix(expDest, installPrefix)) {
  148. auto n = installPrefix.length();
  149. while (n < expDest.length() && expDest[n] == '/') {
  150. ++n;
  151. }
  152. expDest = expDest.substr(n);
  153. } else {
  154. this->ReportError(
  155. cmStrCat("install(PACKAGE_INFO \"", this->GetExportName(),
  156. "\" ...) specifies DESTINATION \"", expDest,
  157. "\" which is not a subdirectory of the install prefix."));
  158. return {};
  159. }
  160. }
  161. if (expDest.empty()) {
  162. return this->GetInstallPrefix();
  163. }
  164. return cmStrCat(this->GetImportPrefixWithSlash(), expDest);
  165. }
  166. std::string cmExportInstallPackageInfoGenerator::InstallNameDir(
  167. cmGeneratorTarget const* target, std::string const& config)
  168. {
  169. std::string install_name_dir;
  170. cmMakefile* mf = target->Target->GetMakefile();
  171. if (mf->IsOn("CMAKE_PLATFORM_HAS_INSTALLNAME")) {
  172. install_name_dir =
  173. target->GetInstallNameDirForInstallTree(config, "@prefix@");
  174. }
  175. return install_name_dir;
  176. }
  177. std::string cmExportInstallPackageInfoGenerator::GetCxxModulesDirectory() const
  178. {
  179. // TODO: Implement a not-CMake-specific mechanism for providing module
  180. // information.
  181. // return IEGen->GetCxxModuleDirectory();
  182. return {};
  183. }
  184. cm::optional<std::string>
  185. cmExportInstallPackageInfoGenerator::GetFileSetDirectory(
  186. cmGeneratorTarget* gte, cmTargetExport const* te, cmFileSet* fileSet,
  187. cm::optional<std::string> const& config)
  188. {
  189. cmGeneratorExpression ge(*gte->Makefile->GetCMakeInstance());
  190. auto cge =
  191. ge.Parse(te->FileSetGenerators.at(fileSet->GetName())->GetDestination());
  192. std::string const unescapedDest =
  193. cge->Evaluate(gte->LocalGenerator, config.value_or(""), gte);
  194. bool const isConfigDependent = cge->GetHadContextSensitiveCondition();
  195. if (config && !isConfigDependent) {
  196. return {};
  197. }
  198. if (!config && isConfigDependent) {
  199. this->RequiresConfigFiles = true;
  200. return {};
  201. }
  202. std::string const& type = fileSet->GetType();
  203. if (config && (type == "CXX_MODULES"_s)) {
  204. // C++ modules do not support interface file sets which are dependent
  205. // upon the configuration.
  206. cmMakefile* mf = gte->LocalGenerator->GetMakefile();
  207. std::ostringstream e;
  208. e << "The \"" << gte->GetName() << "\" target's interface file set \""
  209. << fileSet->GetName() << "\" of type \"" << type
  210. << "\" contains context-sensitive base file entries which is not "
  211. "supported.";
  212. mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
  213. return {};
  214. }
  215. cm::optional<std::string> dest = cmOutputConverter::EscapeForCMake(
  216. unescapedDest, cmOutputConverter::WrapQuotes::NoWrap);
  217. if (!cmSystemTools::FileIsFullPath(unescapedDest)) {
  218. dest = cmStrCat("@prefix@/"_s, *dest);
  219. }
  220. return dest;
  221. }
  222. bool cmExportInstallPackageInfoGenerator::GenerateFileSetProperties(
  223. Json::Value& component, cmGeneratorTarget* gte, cmTargetExport const* te,
  224. cm::optional<std::string> config)
  225. {
  226. std::set<std::string> seenIncludeDirectories;
  227. for (auto const& name : gte->Target->GetAllInterfaceFileSets()) {
  228. cmFileSet* fileSet = gte->Target->GetFileSet(name);
  229. if (!fileSet) {
  230. gte->Makefile->IssueMessage(
  231. MessageType::FATAL_ERROR,
  232. cmStrCat("File set \"", name,
  233. "\" is listed in interface file sets of ", gte->GetName(),
  234. " but has not been created"));
  235. return false;
  236. }
  237. cm::optional<std::string> const& fileSetDirectory =
  238. this->GetFileSetDirectory(gte, te, fileSet, config);
  239. if (fileSet->GetType() == "HEADERS"_s) {
  240. if (fileSetDirectory &&
  241. !cm::contains(seenIncludeDirectories, *fileSetDirectory)) {
  242. component["includes"].append(*fileSetDirectory);
  243. seenIncludeDirectories.insert(*fileSetDirectory);
  244. }
  245. } else if (fileSet->GetType() == "CXX_MODULES"_s) {
  246. /* TODO: Handle the CXX_MODULE directory */
  247. }
  248. }
  249. return true;
  250. }