cmGeneratorTarget_TransitiveProperty.cxx 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  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/optional>
  12. #include <cm/string_view>
  13. #include <cmext/string_view>
  14. #include "cmGeneratorExpression.h"
  15. #include "cmGeneratorExpressionContext.h"
  16. #include "cmGeneratorExpressionDAGChecker.h"
  17. #include "cmGeneratorExpressionNode.h"
  18. #include "cmLinkItem.h"
  19. #include "cmLocalGenerator.h"
  20. #include "cmPolicies.h"
  21. #include "cmStringAlgorithms.h"
  22. #include "cmValue.h"
  23. namespace {
  24. using UseTo = cmGeneratorTarget::UseTo;
  25. using TransitiveProperty = cmGeneratorTarget::TransitiveProperty;
  26. }
  27. const std::map<cm::string_view, TransitiveProperty>
  28. cmGeneratorTarget::BuiltinTransitiveProperties = {
  29. { "AUTOMOC_MACRO_NAMES"_s,
  30. { "INTERFACE_AUTOMOC_MACRO_NAMES"_s, UseTo::Compile } },
  31. { "AUTOUIC_OPTIONS"_s, { "INTERFACE_AUTOUIC_OPTIONS"_s, UseTo::Compile } },
  32. { "COMPILE_DEFINITIONS"_s,
  33. { "INTERFACE_COMPILE_DEFINITIONS"_s, UseTo::Compile } },
  34. { "COMPILE_FEATURES"_s,
  35. { "INTERFACE_COMPILE_FEATURES"_s, UseTo::Compile } },
  36. { "COMPILE_OPTIONS"_s, { "INTERFACE_COMPILE_OPTIONS"_s, UseTo::Compile } },
  37. { "INCLUDE_DIRECTORIES"_s,
  38. { "INTERFACE_INCLUDE_DIRECTORIES"_s, UseTo::Compile } },
  39. { "LINK_DEPENDS"_s, { "INTERFACE_LINK_DEPENDS"_s, UseTo::Link } },
  40. { "LINK_DIRECTORIES"_s, { "INTERFACE_LINK_DIRECTORIES"_s, UseTo::Link } },
  41. { "LINK_OPTIONS"_s, { "INTERFACE_LINK_OPTIONS"_s, UseTo::Link } },
  42. { "PRECOMPILE_HEADERS"_s,
  43. { "INTERFACE_PRECOMPILE_HEADERS"_s, UseTo::Compile } },
  44. { "SOURCES"_s, { "INTERFACE_SOURCES"_s, UseTo::Compile } },
  45. { "SYSTEM_INCLUDE_DIRECTORIES"_s,
  46. { "INTERFACE_SYSTEM_INCLUDE_DIRECTORIES"_s, UseTo::Compile } },
  47. };
  48. bool cmGeneratorTarget::MaybeHaveInterfaceProperty(
  49. std::string const& prop, cmGeneratorExpressionContext* context,
  50. UseTo usage) const
  51. {
  52. std::string const key = prop + '@' + context->Config;
  53. auto i = this->MaybeInterfacePropertyExists.find(key);
  54. if (i == this->MaybeInterfacePropertyExists.end()) {
  55. // Insert an entry now in case there is a cycle.
  56. i = this->MaybeInterfacePropertyExists.emplace(key, false).first;
  57. bool& maybeInterfaceProp = i->second;
  58. // If this target itself has a non-empty property value, we are done.
  59. maybeInterfaceProp = cmNonempty(this->GetProperty(prop));
  60. // Otherwise, recurse to interface dependencies.
  61. if (!maybeInterfaceProp) {
  62. cmGeneratorTarget const* headTarget =
  63. context->HeadTarget ? context->HeadTarget : this;
  64. if (cmLinkInterfaceLibraries const* iface =
  65. this->GetLinkInterfaceLibraries(context->Config, headTarget,
  66. usage)) {
  67. if (iface->HadHeadSensitiveCondition) {
  68. // With a different head target we may get to a library with
  69. // this interface property.
  70. maybeInterfaceProp = true;
  71. } else {
  72. // The transitive interface libraries do not depend on the
  73. // head target, so we can follow them.
  74. for (cmLinkItem const& lib : iface->Libraries) {
  75. if (lib.Target &&
  76. lib.Target->MaybeHaveInterfaceProperty(prop, context, usage)) {
  77. maybeInterfaceProp = true;
  78. break;
  79. }
  80. }
  81. }
  82. }
  83. }
  84. }
  85. return i->second;
  86. }
  87. std::string cmGeneratorTarget::EvaluateInterfaceProperty(
  88. std::string const& prop, cmGeneratorExpressionContext* context,
  89. cmGeneratorExpressionDAGChecker* dagCheckerParent, UseTo usage) const
  90. {
  91. std::string result;
  92. // If the property does not appear transitively at all, we are done.
  93. if (!this->MaybeHaveInterfaceProperty(prop, context, usage)) {
  94. return result;
  95. }
  96. // Evaluate $<TARGET_PROPERTY:this,prop> as if it were compiled. This is
  97. // a subset of TargetPropertyNode::Evaluate without stringify/parse steps
  98. // but sufficient for transitive interface properties.
  99. cmGeneratorExpressionDAGChecker dagChecker(
  100. context->Backtrace, this, prop, nullptr, dagCheckerParent,
  101. this->LocalGenerator, context->Config);
  102. switch (dagChecker.Check()) {
  103. case cmGeneratorExpressionDAGChecker::SELF_REFERENCE:
  104. dagChecker.ReportError(
  105. context, "$<TARGET_PROPERTY:" + this->GetName() + "," + prop + ">");
  106. return result;
  107. case cmGeneratorExpressionDAGChecker::CYCLIC_REFERENCE:
  108. // No error. We just skip cyclic references.
  109. case cmGeneratorExpressionDAGChecker::ALREADY_SEEN:
  110. // No error. We have already seen this transitive property.
  111. return result;
  112. case cmGeneratorExpressionDAGChecker::DAG:
  113. break;
  114. }
  115. cmGeneratorTarget const* headTarget =
  116. context->HeadTarget ? context->HeadTarget : this;
  117. if (cmValue p = this->GetProperty(prop)) {
  118. result = cmGeneratorExpressionNode::EvaluateDependentExpression(
  119. *p, context->LG, context, headTarget, &dagChecker, this);
  120. }
  121. if (cmLinkInterfaceLibraries const* iface =
  122. this->GetLinkInterfaceLibraries(context->Config, headTarget, usage)) {
  123. context->HadContextSensitiveCondition =
  124. context->HadContextSensitiveCondition ||
  125. iface->HadContextSensitiveCondition;
  126. for (cmLinkItem const& lib : iface->Libraries) {
  127. // Broken code can have a target in its own link interface.
  128. // Don't follow such link interface entries so as not to create a
  129. // self-referencing loop.
  130. if (lib.Target && lib.Target != this) {
  131. // Pretend $<TARGET_PROPERTY:lib.Target,prop> appeared in the
  132. // above property and hand-evaluate it as if it were compiled.
  133. // Create a context as cmCompiledGeneratorExpression::Evaluate does.
  134. cmGeneratorExpressionContext libContext(
  135. context->LG, context->Config, context->Quiet, headTarget, this,
  136. context->EvaluateForBuildsystem, context->Backtrace,
  137. context->Language);
  138. std::string libResult = cmGeneratorExpression::StripEmptyListElements(
  139. lib.Target->EvaluateInterfaceProperty(prop, &libContext, &dagChecker,
  140. usage));
  141. if (!libResult.empty()) {
  142. if (result.empty()) {
  143. result = std::move(libResult);
  144. } else {
  145. result.reserve(result.size() + 1 + libResult.size());
  146. result += ";";
  147. result += libResult;
  148. }
  149. }
  150. context->HadContextSensitiveCondition =
  151. context->HadContextSensitiveCondition ||
  152. libContext.HadContextSensitiveCondition;
  153. context->HadHeadSensitiveCondition =
  154. context->HadHeadSensitiveCondition ||
  155. libContext.HadHeadSensitiveCondition;
  156. }
  157. }
  158. }
  159. return result;
  160. }
  161. cm::optional<cmGeneratorTarget::TransitiveProperty>
  162. cmGeneratorTarget::IsTransitiveProperty(cm::string_view prop,
  163. cmLocalGenerator const* lg) const
  164. {
  165. cm::optional<TransitiveProperty> result;
  166. static const cm::string_view kINTERFACE_ = "INTERFACE_"_s;
  167. if (cmHasPrefix(prop, kINTERFACE_)) {
  168. prop = prop.substr(kINTERFACE_.length());
  169. }
  170. auto i = BuiltinTransitiveProperties.find(prop);
  171. if (i != BuiltinTransitiveProperties.end()) {
  172. result = i->second;
  173. if (result->Usage != cmGeneratorTarget::UseTo::Compile) {
  174. cmPolicies::PolicyStatus cmp0166 =
  175. lg->GetPolicyStatus(cmPolicies::CMP0166);
  176. if ((cmp0166 == cmPolicies::WARN || cmp0166 == cmPolicies::OLD) &&
  177. (prop == "LINK_DIRECTORIES"_s || prop == "LINK_DEPENDS"_s ||
  178. prop == "LINK_OPTIONS"_s)) {
  179. result->Usage = cmGeneratorTarget::UseTo::Compile;
  180. }
  181. }
  182. } else if (cmHasLiteralPrefix(prop, "COMPILE_DEFINITIONS_")) {
  183. cmPolicies::PolicyStatus cmp0043 =
  184. lg->GetPolicyStatus(cmPolicies::CMP0043);
  185. if (cmp0043 == cmPolicies::WARN || cmp0043 == cmPolicies::OLD) {
  186. result = TransitiveProperty{ "INTERFACE_COMPILE_DEFINITIONS"_s,
  187. UseTo::Compile };
  188. }
  189. }
  190. return result;
  191. }