cmExportBuildCMakeConfigGenerator.cxx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  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 "cmExportBuildCMakeConfigGenerator.h"
  4. #include <algorithm>
  5. #include <cstddef>
  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 "cmCryptoHash.h"
  15. #include "cmExportSet.h"
  16. #include "cmFileSet.h"
  17. #include "cmGeneratedFileStream.h"
  18. #include "cmGeneratorExpression.h"
  19. #include "cmGeneratorTarget.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 "cmTarget.h"
  29. cmExportBuildCMakeConfigGenerator::cmExportBuildCMakeConfigGenerator()
  30. {
  31. this->LG = nullptr;
  32. this->ExportSet = nullptr;
  33. }
  34. bool cmExportBuildCMakeConfigGenerator::GenerateMainFile(std::ostream& os)
  35. {
  36. {
  37. std::string expectedTargets;
  38. std::string sep;
  39. bool generatedInterfaceRequired = false;
  40. auto visitor = [&](cmGeneratorTarget const* te) {
  41. expectedTargets += sep + this->Namespace + te->GetExportName();
  42. sep = " ";
  43. generatedInterfaceRequired |=
  44. this->GetExportTargetType(te) == cmStateEnums::INTERFACE_LIBRARY;
  45. };
  46. if (!this->CollectExports(visitor)) {
  47. return false;
  48. }
  49. if (generatedInterfaceRequired) {
  50. this->SetRequiredCMakeVersion(3, 0, 0);
  51. }
  52. this->GenerateExpectedTargetsCode(os, expectedTargets);
  53. }
  54. // Create all the imported targets.
  55. for (auto const& exp : this->Exports) {
  56. cmGeneratorTarget* gte = exp.Target;
  57. this->GenerateImportTargetCode(os, gte, this->GetExportTargetType(gte));
  58. gte->Target->AppendBuildInterfaceIncludes();
  59. ImportPropertyMap properties;
  60. if (!this->PopulateInterfaceProperties(gte, properties)) {
  61. return false;
  62. }
  63. bool const newCMP0022Behavior =
  64. gte->GetPolicyStatusCMP0022() != cmPolicies::WARN &&
  65. gte->GetPolicyStatusCMP0022() != cmPolicies::OLD;
  66. if (newCMP0022Behavior) {
  67. this->PopulateInterfaceLinkLibrariesProperty(
  68. gte, cmGeneratorExpression::BuildInterface, properties);
  69. }
  70. this->GenerateInterfaceProperties(gte, os, properties);
  71. this->GenerateTargetFileSets(gte, os);
  72. }
  73. std::string cxx_modules_name;
  74. if (this->ExportSet) {
  75. cxx_modules_name = this->ExportSet->GetName();
  76. } else {
  77. cmCryptoHash hasher(cmCryptoHash::AlgoSHA3_512);
  78. constexpr std::size_t HASH_TRUNCATION = 12;
  79. for (auto const& target : this->Targets) {
  80. hasher.Append(target.Name);
  81. }
  82. cxx_modules_name = hasher.FinalizeHex().substr(0, HASH_TRUNCATION);
  83. }
  84. this->GenerateCxxModuleInformation(cxx_modules_name, os);
  85. // Generate import file content for each configuration.
  86. for (std::string const& c : this->Configurations) {
  87. this->GenerateImportConfig(os, c);
  88. }
  89. // Generate import file content for each configuration.
  90. for (std::string const& c : this->Configurations) {
  91. this->GenerateImportCxxModuleConfigTargetInclusion(cxx_modules_name, c);
  92. }
  93. this->GenerateMissingTargetsCheckCode(os);
  94. return true;
  95. }
  96. void cmExportBuildCMakeConfigGenerator::GenerateImportTargetsConfig(
  97. std::ostream& os, std::string const& config, std::string const& suffix)
  98. {
  99. for (auto const& exp : this->Exports) {
  100. cmGeneratorTarget* target = exp.Target;
  101. // Collect import properties for this target.
  102. ImportPropertyMap properties;
  103. if (this->GetExportTargetType(target) != cmStateEnums::INTERFACE_LIBRARY) {
  104. this->SetImportLocationProperty(config, suffix, target, properties);
  105. }
  106. if (!properties.empty()) {
  107. // Get the rest of the target details.
  108. if (this->GetExportTargetType(target) !=
  109. cmStateEnums::INTERFACE_LIBRARY) {
  110. this->SetImportDetailProperties(config, suffix, target, properties);
  111. this->SetImportLinkInterface(config, suffix,
  112. cmGeneratorExpression::BuildInterface,
  113. target, properties);
  114. }
  115. // TODO: PUBLIC_HEADER_LOCATION
  116. // This should wait until the build feature propagation stuff
  117. // is done. Then this can be a propagated include directory.
  118. // this->GenerateImportProperty(config, te->HeaderGenerator,
  119. // properties);
  120. // Generate code in the export file.
  121. std::string importedXcFrameworkLocation = exp.XcFrameworkLocation;
  122. if (!importedXcFrameworkLocation.empty()) {
  123. importedXcFrameworkLocation = cmGeneratorExpression::Preprocess(
  124. importedXcFrameworkLocation,
  125. cmGeneratorExpression::PreprocessContext::BuildInterface);
  126. importedXcFrameworkLocation = cmGeneratorExpression::Evaluate(
  127. importedXcFrameworkLocation, exp.Target->GetLocalGenerator(), config,
  128. exp.Target, nullptr, exp.Target);
  129. if (!importedXcFrameworkLocation.empty() &&
  130. !cmSystemTools::FileIsFullPath(importedXcFrameworkLocation)) {
  131. importedXcFrameworkLocation =
  132. cmStrCat(this->LG->GetCurrentBinaryDirectory(), '/',
  133. importedXcFrameworkLocation);
  134. }
  135. }
  136. this->GenerateImportPropertyCode(os, config, suffix, target, properties,
  137. importedXcFrameworkLocation);
  138. }
  139. }
  140. }
  141. namespace {
  142. bool EntryIsContextSensitive(
  143. std::unique_ptr<cmCompiledGeneratorExpression> const& cge)
  144. {
  145. return cge->GetHadContextSensitiveCondition();
  146. }
  147. }
  148. std::string cmExportBuildCMakeConfigGenerator::GetFileSetDirectories(
  149. cmGeneratorTarget* gte, cmFileSet* fileSet, cmTargetExport const* /*te*/)
  150. {
  151. std::vector<std::string> resultVector;
  152. auto configs =
  153. gte->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
  154. auto directoryEntries = fileSet->CompileDirectoryEntries();
  155. for (auto const& config : configs) {
  156. auto directories = fileSet->EvaluateDirectoryEntries(
  157. directoryEntries, gte->LocalGenerator, config, gte);
  158. bool const contextSensitive =
  159. std::any_of(directoryEntries.begin(), directoryEntries.end(),
  160. EntryIsContextSensitive);
  161. auto const& type = fileSet->GetType();
  162. // C++ modules do not support interface file sets which are dependent upon
  163. // the configuration.
  164. if (contextSensitive && type == "CXX_MODULES"_s) {
  165. auto* mf = this->LG->GetMakefile();
  166. std::ostringstream e;
  167. e << "The \"" << gte->GetName() << "\" target's interface file set \""
  168. << fileSet->GetName() << "\" of type \"" << type
  169. << "\" contains context-sensitive base directory entries which is not "
  170. "supported.";
  171. mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
  172. return std::string{};
  173. }
  174. for (auto const& directory : directories) {
  175. auto dest = cmOutputConverter::EscapeForCMake(
  176. directory, cmOutputConverter::WrapQuotes::NoWrap);
  177. if (contextSensitive && configs.size() != 1) {
  178. resultVector.push_back(
  179. cmStrCat("\"$<$<CONFIG:", config, ">:", dest, ">\""));
  180. } else {
  181. resultVector.emplace_back(cmStrCat('"', dest, '"'));
  182. break;
  183. }
  184. }
  185. }
  186. return cmJoin(resultVector, " ");
  187. }
  188. std::string cmExportBuildCMakeConfigGenerator::GetFileSetFiles(
  189. cmGeneratorTarget* gte, cmFileSet* fileSet, cmTargetExport const* /*te*/)
  190. {
  191. std::vector<std::string> resultVector;
  192. auto configs =
  193. gte->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
  194. auto fileEntries = fileSet->CompileFileEntries();
  195. auto directoryEntries = fileSet->CompileDirectoryEntries();
  196. for (auto const& config : configs) {
  197. auto directories = fileSet->EvaluateDirectoryEntries(
  198. directoryEntries, gte->LocalGenerator, config, gte);
  199. std::map<std::string, std::vector<std::string>> files;
  200. for (auto const& entry : fileEntries) {
  201. fileSet->EvaluateFileEntry(directories, files, entry,
  202. gte->LocalGenerator, config, gte);
  203. }
  204. bool const contextSensitive =
  205. std::any_of(directoryEntries.begin(), directoryEntries.end(),
  206. EntryIsContextSensitive) ||
  207. std::any_of(fileEntries.begin(), fileEntries.end(),
  208. EntryIsContextSensitive);
  209. auto const& type = fileSet->GetType();
  210. // C++ modules do not support interface file sets which are dependent upon
  211. // the configuration.
  212. if (contextSensitive && type == "CXX_MODULES"_s) {
  213. auto* mf = this->LG->GetMakefile();
  214. std::ostringstream e;
  215. e << "The \"" << gte->GetName() << "\" target's interface file set \""
  216. << fileSet->GetName() << "\" of type \"" << type
  217. << "\" contains context-sensitive file entries which is not "
  218. "supported.";
  219. mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
  220. return std::string{};
  221. }
  222. for (auto const& it : files) {
  223. for (auto const& filename : it.second) {
  224. auto escapedFile = cmOutputConverter::EscapeForCMake(
  225. filename, cmOutputConverter::WrapQuotes::NoWrap);
  226. if (contextSensitive && configs.size() != 1) {
  227. resultVector.push_back(
  228. cmStrCat("\"$<$<CONFIG:", config, ">:", escapedFile, ">\""));
  229. } else {
  230. resultVector.emplace_back(cmStrCat('"', escapedFile, '"'));
  231. }
  232. }
  233. }
  234. if (!(contextSensitive && configs.size() != 1)) {
  235. break;
  236. }
  237. }
  238. return cmJoin(resultVector, " ");
  239. }
  240. void cmExportBuildCMakeConfigGenerator::GenerateCxxModuleConfigInformation(
  241. std::string const& name, std::ostream& os) const
  242. {
  243. char const* opt = "";
  244. if (this->Configurations.size() > 1) {
  245. // With more than one configuration, each individual file is optional.
  246. opt = " OPTIONAL";
  247. }
  248. // Generate import file content for each configuration.
  249. for (std::string c : this->Configurations) {
  250. if (c.empty()) {
  251. c = "noconfig";
  252. }
  253. os << "include(\"${CMAKE_CURRENT_LIST_DIR}/cxx-modules-" << name << '-'
  254. << c << ".cmake\"" << opt << ")\n";
  255. }
  256. }
  257. bool cmExportBuildCMakeConfigGenerator::
  258. GenerateImportCxxModuleConfigTargetInclusion(std::string const& name,
  259. std::string config) const
  260. {
  261. auto cxx_modules_dirname = this->GetCxxModulesDirectory();
  262. if (cxx_modules_dirname.empty()) {
  263. return true;
  264. }
  265. if (config.empty()) {
  266. config = "noconfig";
  267. }
  268. std::string fileName =
  269. cmStrCat(this->FileDir, '/', cxx_modules_dirname, "/cxx-modules-", name,
  270. '-', config, ".cmake");
  271. cmGeneratedFileStream os(fileName, true);
  272. if (!os) {
  273. std::string se = cmSystemTools::GetLastSystemError();
  274. std::ostringstream e;
  275. e << "cannot write to file \"" << fileName << "\": " << se;
  276. cmSystemTools::Error(e.str());
  277. return false;
  278. }
  279. os.SetCopyIfDifferent(true);
  280. for (auto const* tgt : this->ExportedTargets) {
  281. // Only targets with C++ module sources will have a
  282. // collator-generated install script.
  283. if (!tgt->HaveCxx20ModuleSources()) {
  284. continue;
  285. }
  286. os << "include(\"${CMAKE_CURRENT_LIST_DIR}/target-"
  287. << tgt->GetFilesystemExportName() << '-' << config << ".cmake\")\n";
  288. }
  289. return true;
  290. }