cmGeneratorTarget_TransitiveProperty.cxx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file Copyright.txt or https://cmake.org/licensing for details. */
  3. /* clang-format off */
  4. #include "cmGeneratorTarget.h"
  5. /* clang-format on */
  6. #include <map>
  7. #include <string>
  8. #include <unordered_map>
  9. #include <utility>
  10. #include <vector>
  11. #include <cm/memory>
  12. #include <cm/optional>
  13. #include <cm/string_view>
  14. #include <cmext/string_view>
  15. #include "cmGeneratorExpression.h"
  16. #include "cmGeneratorExpressionContext.h"
  17. #include "cmGeneratorExpressionDAGChecker.h"
  18. #include "cmGeneratorExpressionNode.h"
  19. #include "cmLinkItem.h"
  20. #include "cmList.h"
  21. #include "cmLocalGenerator.h"
  22. #include "cmPolicies.h"
  23. #include "cmStringAlgorithms.h"
  24. #include "cmValue.h"
  25. namespace {
  26. using UseTo = cmGeneratorTarget::UseTo;
  27. using TransitiveProperty = cmGeneratorTarget::TransitiveProperty;
  28. }
  29. const std::map<cm::string_view, TransitiveProperty>
  30. cmGeneratorTarget::BuiltinTransitiveProperties = {
  31. { "AUTOMOC_MACRO_NAMES"_s,
  32. { "INTERFACE_AUTOMOC_MACRO_NAMES"_s, UseTo::Compile } },
  33. { "AUTOUIC_OPTIONS"_s, { "INTERFACE_AUTOUIC_OPTIONS"_s, UseTo::Compile } },
  34. { "COMPILE_DEFINITIONS"_s,
  35. { "INTERFACE_COMPILE_DEFINITIONS"_s, UseTo::Compile } },
  36. { "COMPILE_FEATURES"_s,
  37. { "INTERFACE_COMPILE_FEATURES"_s, UseTo::Compile } },
  38. { "COMPILE_OPTIONS"_s, { "INTERFACE_COMPILE_OPTIONS"_s, UseTo::Compile } },
  39. { "INCLUDE_DIRECTORIES"_s,
  40. { "INTERFACE_INCLUDE_DIRECTORIES"_s, UseTo::Compile } },
  41. { "LINK_DEPENDS"_s, { "INTERFACE_LINK_DEPENDS"_s, UseTo::Link } },
  42. { "LINK_DIRECTORIES"_s, { "INTERFACE_LINK_DIRECTORIES"_s, UseTo::Link } },
  43. { "LINK_OPTIONS"_s, { "INTERFACE_LINK_OPTIONS"_s, UseTo::Link } },
  44. { "PRECOMPILE_HEADERS"_s,
  45. { "INTERFACE_PRECOMPILE_HEADERS"_s, UseTo::Compile } },
  46. { "SOURCES"_s, { "INTERFACE_SOURCES"_s, UseTo::Compile } },
  47. { "SYSTEM_INCLUDE_DIRECTORIES"_s,
  48. { "INTERFACE_SYSTEM_INCLUDE_DIRECTORIES"_s, UseTo::Compile } },
  49. };
  50. bool cmGeneratorTarget::MaybeHaveInterfaceProperty(
  51. std::string const& prop, cmGeneratorExpressionContext* context,
  52. UseTo usage) const
  53. {
  54. std::string const key = prop + '@' + context->Config;
  55. auto i = this->MaybeInterfacePropertyExists.find(key);
  56. if (i == this->MaybeInterfacePropertyExists.end()) {
  57. // Insert an entry now in case there is a cycle.
  58. i = this->MaybeInterfacePropertyExists.emplace(key, false).first;
  59. bool& maybeInterfaceProp = i->second;
  60. // If this target itself has a non-empty property value, we are done.
  61. maybeInterfaceProp = cmNonempty(this->GetProperty(prop));
  62. // Otherwise, recurse to interface dependencies.
  63. if (!maybeInterfaceProp) {
  64. cmGeneratorTarget const* headTarget =
  65. context->HeadTarget ? context->HeadTarget : this;
  66. if (cmLinkInterfaceLibraries const* iface =
  67. this->GetLinkInterfaceLibraries(context->Config, headTarget,
  68. usage)) {
  69. if (iface->HadHeadSensitiveCondition) {
  70. // With a different head target we may get to a library with
  71. // this interface property.
  72. maybeInterfaceProp = true;
  73. } else {
  74. // The transitive interface libraries do not depend on the
  75. // head target, so we can follow them.
  76. for (cmLinkItem const& lib : iface->Libraries) {
  77. if (lib.Target &&
  78. lib.Target->MaybeHaveInterfaceProperty(prop, context, usage)) {
  79. maybeInterfaceProp = true;
  80. break;
  81. }
  82. }
  83. }
  84. }
  85. }
  86. }
  87. return i->second;
  88. }
  89. std::string cmGeneratorTarget::EvaluateInterfaceProperty(
  90. std::string const& prop, cmGeneratorExpressionContext* context,
  91. cmGeneratorExpressionDAGChecker* dagCheckerParent, UseTo usage) const
  92. {
  93. return EvaluateInterfaceProperty(prop, context, dagCheckerParent, usage,
  94. TransitiveClosure::Inherit);
  95. }
  96. std::string cmGeneratorTarget::EvaluateInterfaceProperty(
  97. std::string const& prop, cmGeneratorExpressionContext* context,
  98. cmGeneratorExpressionDAGChecker* dagCheckerParent, UseTo usage,
  99. TransitiveClosure closure) const
  100. {
  101. std::string result;
  102. // If the property does not appear transitively at all, we are done.
  103. if (!this->MaybeHaveInterfaceProperty(prop, context, usage)) {
  104. return result;
  105. }
  106. auto const dagClosure = closure == TransitiveClosure::Inherit
  107. ? cmGeneratorExpressionDAGChecker::INHERIT
  108. : cmGeneratorExpressionDAGChecker::SUBGRAPH;
  109. // Evaluate $<TARGET_PROPERTY:this,prop> as if it were compiled. This is
  110. // a subset of TargetPropertyNode::Evaluate without stringify/parse steps
  111. // but sufficient for transitive interface properties.
  112. cmGeneratorExpressionDAGChecker dagChecker(
  113. context->Backtrace, this, prop, nullptr, dagCheckerParent,
  114. this->LocalGenerator, context->Config, dagClosure);
  115. switch (dagChecker.Check()) {
  116. case cmGeneratorExpressionDAGChecker::SELF_REFERENCE:
  117. dagChecker.ReportError(
  118. context, "$<TARGET_PROPERTY:" + this->GetName() + "," + prop + ">");
  119. return result;
  120. case cmGeneratorExpressionDAGChecker::CYCLIC_REFERENCE:
  121. // No error. We just skip cyclic references.
  122. case cmGeneratorExpressionDAGChecker::ALREADY_SEEN:
  123. // No error. We have already seen this transitive property.
  124. return result;
  125. case cmGeneratorExpressionDAGChecker::DAG:
  126. break;
  127. }
  128. cmGeneratorTarget const* headTarget =
  129. context->HeadTarget ? context->HeadTarget : this;
  130. if (cmValue p = this->GetProperty(prop)) {
  131. result = cmGeneratorExpressionNode::EvaluateDependentExpression(
  132. *p, context->LG, context, headTarget, &dagChecker, this);
  133. }
  134. if (cmLinkInterfaceLibraries const* iface =
  135. this->GetLinkInterfaceLibraries(context->Config, headTarget, usage)) {
  136. context->HadContextSensitiveCondition =
  137. context->HadContextSensitiveCondition ||
  138. iface->HadContextSensitiveCondition;
  139. for (cmLinkItem const& lib : iface->Libraries) {
  140. // Broken code can have a target in its own link interface.
  141. // Don't follow such link interface entries so as not to create a
  142. // self-referencing loop.
  143. if (lib.Target && lib.Target != this) {
  144. // Pretend $<TARGET_PROPERTY:lib.Target,prop> appeared in the
  145. // above property and hand-evaluate it as if it were compiled.
  146. // Create a context as cmCompiledGeneratorExpression::Evaluate does.
  147. cmGeneratorExpressionContext libContext(
  148. context->LG, context->Config, context->Quiet, headTarget, this,
  149. context->EvaluateForBuildsystem, context->Backtrace,
  150. context->Language);
  151. std::string libResult = cmGeneratorExpression::StripEmptyListElements(
  152. lib.Target->EvaluateInterfaceProperty(prop, &libContext, &dagChecker,
  153. usage));
  154. if (!libResult.empty()) {
  155. if (result.empty()) {
  156. result = std::move(libResult);
  157. } else {
  158. result.reserve(result.size() + 1 + libResult.size());
  159. result += ";";
  160. result += libResult;
  161. }
  162. }
  163. context->HadContextSensitiveCondition =
  164. context->HadContextSensitiveCondition ||
  165. libContext.HadContextSensitiveCondition;
  166. context->HadHeadSensitiveCondition =
  167. context->HadHeadSensitiveCondition ||
  168. libContext.HadHeadSensitiveCondition;
  169. }
  170. }
  171. }
  172. return result;
  173. }
  174. cm::optional<cmGeneratorTarget::TransitiveProperty>
  175. cmGeneratorTarget::IsTransitiveProperty(cm::string_view prop,
  176. cmLocalGenerator const* lg,
  177. std::string const& config,
  178. bool evaluatingLinkLibraries) const
  179. {
  180. cm::optional<TransitiveProperty> result;
  181. static const cm::string_view kINTERFACE_ = "INTERFACE_"_s;
  182. PropertyFor const propertyFor = cmHasPrefix(prop, kINTERFACE_)
  183. ? PropertyFor::Interface
  184. : PropertyFor::Build;
  185. if (propertyFor == PropertyFor::Interface) {
  186. prop = prop.substr(kINTERFACE_.length());
  187. }
  188. auto i = BuiltinTransitiveProperties.find(prop);
  189. if (i != BuiltinTransitiveProperties.end()) {
  190. result = i->second;
  191. if (result->Usage != cmGeneratorTarget::UseTo::Compile) {
  192. cmPolicies::PolicyStatus cmp0166 =
  193. lg->GetPolicyStatus(cmPolicies::CMP0166);
  194. if ((cmp0166 == cmPolicies::WARN || cmp0166 == cmPolicies::OLD) &&
  195. (prop == "LINK_DIRECTORIES"_s || prop == "LINK_DEPENDS"_s ||
  196. prop == "LINK_OPTIONS"_s)) {
  197. result->Usage = cmGeneratorTarget::UseTo::Compile;
  198. }
  199. }
  200. } else if (cmHasLiteralPrefix(prop, "COMPILE_DEFINITIONS_")) {
  201. cmPolicies::PolicyStatus cmp0043 =
  202. lg->GetPolicyStatus(cmPolicies::CMP0043);
  203. if (cmp0043 == cmPolicies::WARN || cmp0043 == cmPolicies::OLD) {
  204. result = TransitiveProperty{ "INTERFACE_COMPILE_DEFINITIONS"_s,
  205. UseTo::Compile };
  206. }
  207. } else if (!evaluatingLinkLibraries) {
  208. // Honor TRANSITIVE_COMPILE_PROPERTIES and TRANSITIVE_LINK_PROPERTIES
  209. // from the link closure when we are not evaluating the closure itself.
  210. CustomTransitiveProperties const& ctp =
  211. this->GetCustomTransitiveProperties(config, propertyFor);
  212. auto ci = ctp.find(std::string(prop));
  213. if (ci != ctp.end()) {
  214. result = ci->second;
  215. }
  216. }
  217. return result;
  218. }
  219. cmGeneratorTarget::CustomTransitiveProperty::CustomTransitiveProperty(
  220. std::string interfaceName, UseTo usage)
  221. : CustomTransitiveProperty(
  222. cm::make_unique<std::string>(std::move(interfaceName)), usage)
  223. {
  224. }
  225. cmGeneratorTarget::CustomTransitiveProperty::CustomTransitiveProperty(
  226. std::unique_ptr<std::string> interfaceNameBuf, UseTo usage)
  227. : TransitiveProperty{ *interfaceNameBuf, usage }
  228. , InterfaceNameBuf(std::move(interfaceNameBuf))
  229. {
  230. }
  231. void cmGeneratorTarget::CustomTransitiveProperties::Add(cmValue props,
  232. UseTo usage)
  233. {
  234. if (props) {
  235. cmList propsList(*props);
  236. for (std::string p : propsList) {
  237. std::string ip;
  238. static const cm::string_view kINTERFACE_ = "INTERFACE_"_s;
  239. if (cmHasPrefix(p, kINTERFACE_)) {
  240. ip = std::move(p);
  241. p = ip.substr(kINTERFACE_.length());
  242. } else {
  243. ip = cmStrCat(kINTERFACE_, p);
  244. }
  245. this->emplace(std::move(p),
  246. CustomTransitiveProperty(std::move(ip), usage));
  247. }
  248. }
  249. }
  250. cmGeneratorTarget::CustomTransitiveProperties const&
  251. cmGeneratorTarget::GetCustomTransitiveProperties(std::string const& config,
  252. PropertyFor propertyFor) const
  253. {
  254. std::map<std::string, CustomTransitiveProperties>& ctpm =
  255. propertyFor == PropertyFor::Build
  256. ? this->CustomTransitiveBuildPropertiesMap
  257. : this->CustomTransitiveInterfacePropertiesMap;
  258. auto i = ctpm.find(config);
  259. if (i == ctpm.end()) {
  260. CustomTransitiveProperties ctp;
  261. auto addTransitiveProperties = [this, &config, propertyFor,
  262. &ctp](std::string const& tp, UseTo usage) {
  263. // Add transitive properties named by the target itself.
  264. ctp.Add(this->GetProperty(tp), usage);
  265. // Add transitive properties named by the target's link dependencies.
  266. if (propertyFor == PropertyFor::Build) {
  267. for (cmGeneratorTarget const* gt :
  268. this->GetLinkImplementationClosure(config, usage)) {
  269. ctp.Add(gt->GetProperty(tp), usage);
  270. }
  271. } else {
  272. // The set of custom transitive INTERFACE_ properties does not
  273. // depend on the consumer. Use the target as its own head.
  274. cmGeneratorTarget const* headTarget = this;
  275. for (cmGeneratorTarget const* gt :
  276. this->GetLinkInterfaceClosure(config, headTarget, usage)) {
  277. ctp.Add(gt->GetProperty(tp), usage);
  278. }
  279. }
  280. };
  281. addTransitiveProperties("TRANSITIVE_LINK_PROPERTIES", UseTo::Link);
  282. addTransitiveProperties("TRANSITIVE_COMPILE_PROPERTIES", UseTo::Compile);
  283. i = ctpm.emplace(config, std::move(ctp)).first;
  284. }
  285. return i->second;
  286. }