cmGeneratorTarget_TransitiveProperty.cxx 11 KB

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