123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298 |
- /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
- file LICENSE.rst or https://cmake.org/licensing for details. */
- /* clang-format off */
- #include "cmGeneratorTarget.h"
- /* clang-format on */
- #include <map>
- #include <string>
- #include <unordered_map>
- #include <utility>
- #include <vector>
- #include <cm/memory>
- #include <cm/optional>
- #include <cm/string_view>
- #include <cmext/string_view>
- #include "cmGenExContext.h"
- #include "cmGenExEvaluation.h"
- #include "cmGeneratorExpression.h"
- #include "cmGeneratorExpressionDAGChecker.h"
- #include "cmGeneratorExpressionNode.h"
- #include "cmLinkItem.h"
- #include "cmList.h"
- #include "cmLocalGenerator.h"
- #include "cmPolicies.h"
- #include "cmStringAlgorithms.h"
- #include "cmValue.h"
- namespace {
- using UseTo = cmGeneratorTarget::UseTo;
- using TransitiveProperty = cmGeneratorTarget::TransitiveProperty;
- }
- std::map<cm::string_view, TransitiveProperty> const
- cmGeneratorTarget::BuiltinTransitiveProperties = {
- { "AUTOMOC_MACRO_NAMES"_s,
- { "INTERFACE_AUTOMOC_MACRO_NAMES"_s, UseTo::Compile } },
- { "AUTOUIC_OPTIONS"_s, { "INTERFACE_AUTOUIC_OPTIONS"_s, UseTo::Compile } },
- { "COMPILE_DEFINITIONS"_s,
- { "INTERFACE_COMPILE_DEFINITIONS"_s, UseTo::Compile } },
- { "COMPILE_FEATURES"_s,
- { "INTERFACE_COMPILE_FEATURES"_s, UseTo::Compile } },
- { "COMPILE_OPTIONS"_s, { "INTERFACE_COMPILE_OPTIONS"_s, UseTo::Compile } },
- { "INCLUDE_DIRECTORIES"_s,
- { "INTERFACE_INCLUDE_DIRECTORIES"_s, UseTo::Compile } },
- { "LINK_DEPENDS"_s, { "INTERFACE_LINK_DEPENDS"_s, UseTo::Link } },
- { "LINK_DIRECTORIES"_s, { "INTERFACE_LINK_DIRECTORIES"_s, UseTo::Link } },
- { "LINK_LIBRARIES"_s, { "INTERFACE_LINK_LIBRARIES"_s, UseTo::Link } },
- { "LINK_OPTIONS"_s, { "INTERFACE_LINK_OPTIONS"_s, UseTo::Link } },
- { "PRECOMPILE_HEADERS"_s,
- { "INTERFACE_PRECOMPILE_HEADERS"_s, UseTo::Compile } },
- { "SOURCES"_s, { "INTERFACE_SOURCES"_s, UseTo::Compile } },
- { "SYSTEM_INCLUDE_DIRECTORIES"_s,
- { "INTERFACE_SYSTEM_INCLUDE_DIRECTORIES"_s, UseTo::Compile } },
- };
- bool cmGeneratorTarget::MaybeHaveInterfaceProperty(std::string const& prop,
- cm::GenEx::Evaluation* eval,
- UseTo usage) const
- {
- std::string const key = prop + '@' + eval->Context.Config;
- auto i = this->MaybeInterfacePropertyExists.find(key);
- if (i == this->MaybeInterfacePropertyExists.end()) {
- // Insert an entry now in case there is a cycle.
- i = this->MaybeInterfacePropertyExists.emplace(key, false).first;
- bool& maybeInterfaceProp = i->second;
- // If this target itself has a non-empty property value, we are done.
- maybeInterfaceProp = cmNonempty(this->GetProperty(prop));
- // Otherwise, recurse to interface dependencies.
- if (!maybeInterfaceProp) {
- cmGeneratorTarget const* headTarget =
- eval->HeadTarget ? eval->HeadTarget : this;
- if (cmLinkInterfaceLibraries const* iface =
- this->GetLinkInterfaceLibraries(eval->Context.Config, headTarget,
- usage)) {
- if (iface->HadHeadSensitiveCondition) {
- // With a different head target we may get to a library with
- // this interface property.
- maybeInterfaceProp = true;
- } else {
- // The transitive interface libraries do not depend on the
- // head target, so we can follow them.
- for (cmLinkItem const& lib : iface->Libraries) {
- if (lib.Target &&
- lib.Target->MaybeHaveInterfaceProperty(prop, eval, usage)) {
- maybeInterfaceProp = true;
- break;
- }
- }
- }
- }
- }
- }
- return i->second;
- }
- std::string cmGeneratorTarget::EvaluateInterfaceProperty(
- std::string const& prop, cm::GenEx::Evaluation* eval,
- cmGeneratorExpressionDAGChecker* dagCheckerParent, UseTo usage) const
- {
- std::string result;
- // If the property does not appear transitively at all, we are done.
- if (!this->MaybeHaveInterfaceProperty(prop, eval, usage)) {
- return result;
- }
- // Evaluate $<TARGET_PROPERTY:this,prop> as if it were compiled. This is
- // a subset of TargetPropertyNode::Evaluate without stringify/parse steps
- // but sufficient for transitive interface properties.
- cmGeneratorExpressionDAGChecker dagChecker{
- this,
- prop,
- nullptr,
- dagCheckerParent,
- eval->Context.LG,
- eval->Context.Config,
- eval->Backtrace,
- };
- switch (dagChecker.Check()) {
- case cmGeneratorExpressionDAGChecker::SELF_REFERENCE:
- dagChecker.ReportError(
- eval, "$<TARGET_PROPERTY:" + this->GetName() + "," + prop + ">");
- return result;
- case cmGeneratorExpressionDAGChecker::CYCLIC_REFERENCE:
- // No error. We just skip cyclic references.
- case cmGeneratorExpressionDAGChecker::ALREADY_SEEN:
- // No error. We have already seen this transitive property.
- return result;
- case cmGeneratorExpressionDAGChecker::DAG:
- break;
- }
- cmGeneratorTarget const* headTarget =
- eval->HeadTarget ? eval->HeadTarget : this;
- if (cmValue p = this->GetProperty(prop)) {
- result = cmGeneratorExpressionNode::EvaluateDependentExpression(
- *p, eval, headTarget, &dagChecker, this);
- }
- if (cmLinkInterfaceLibraries const* iface = this->GetLinkInterfaceLibraries(
- eval->Context.Config, headTarget, usage)) {
- eval->HadContextSensitiveCondition = eval->HadContextSensitiveCondition ||
- iface->HadContextSensitiveCondition;
- for (cmLinkItem const& lib : iface->Libraries) {
- // Broken code can have a target in its own link interface.
- // Don't follow such link interface entries so as not to create a
- // self-referencing loop.
- if (lib.Target && lib.Target != this) {
- // Pretend $<TARGET_PROPERTY:lib.Target,prop> appeared in the
- // above property and hand-evaluate it as if it were compiled.
- // Create a context as cmCompiledGeneratorExpression::Evaluate does.
- cm::GenEx::Evaluation libEval(eval->Context, eval->Quiet, headTarget,
- this, eval->EvaluateForBuildsystem,
- eval->Backtrace);
- std::string libResult = cmGeneratorExpression::StripEmptyListElements(
- lib.Target->EvaluateInterfaceProperty(prop, &libEval, &dagChecker,
- usage));
- if (!libResult.empty()) {
- if (result.empty()) {
- result = std::move(libResult);
- } else {
- result.reserve(result.size() + 1 + libResult.size());
- result += ";";
- result += libResult;
- }
- }
- eval->HadContextSensitiveCondition =
- eval->HadContextSensitiveCondition ||
- libEval.HadContextSensitiveCondition;
- eval->HadHeadSensitiveCondition =
- eval->HadHeadSensitiveCondition || libEval.HadHeadSensitiveCondition;
- }
- }
- }
- return result;
- }
- cm::optional<cmGeneratorTarget::TransitiveProperty>
- cmGeneratorTarget::IsTransitiveProperty(
- cm::string_view prop, cmLocalGenerator const* lg, std::string const& config,
- cmGeneratorExpressionDAGChecker const* dagChecker) const
- {
- cm::optional<TransitiveProperty> result;
- static cm::string_view const kINTERFACE_ = "INTERFACE_"_s;
- PropertyFor const propertyFor = cmHasPrefix(prop, kINTERFACE_)
- ? PropertyFor::Interface
- : PropertyFor::Build;
- if (propertyFor == PropertyFor::Interface) {
- prop = prop.substr(kINTERFACE_.length());
- }
- auto i = BuiltinTransitiveProperties.find(prop);
- if (i != BuiltinTransitiveProperties.end() &&
- // Look up CMP0189 in the context where evaluation occurs,
- // not where the target was created.
- lg->GetPolicyStatus(cmPolicies::CMP0189) != cmPolicies::NEW &&
- prop == "LINK_LIBRARIES"_s) {
- i = BuiltinTransitiveProperties.end();
- }
- if (i != BuiltinTransitiveProperties.end()) {
- result = i->second;
- if (result->Usage != cmGeneratorTarget::UseTo::Compile) {
- cmPolicies::PolicyStatus cmp0166 =
- lg->GetPolicyStatus(cmPolicies::CMP0166);
- if ((cmp0166 == cmPolicies::WARN || cmp0166 == cmPolicies::OLD) &&
- (prop == "LINK_DIRECTORIES"_s || prop == "LINK_DEPENDS"_s ||
- prop == "LINK_OPTIONS"_s)) {
- result->Usage = cmGeneratorTarget::UseTo::Compile;
- }
- }
- } else if (!dagChecker || !dagChecker->IsComputingLinkLibraries()) {
- // Honor TRANSITIVE_COMPILE_PROPERTIES and TRANSITIVE_LINK_PROPERTIES
- // from the link closure when we are not evaluating the closure itself.
- CustomTransitiveProperties const& ctp =
- this->GetCustomTransitiveProperties(config, propertyFor);
- auto ci = ctp.find(std::string(prop));
- if (ci != ctp.end()) {
- result = ci->second;
- }
- }
- return result;
- }
- cmGeneratorTarget::CustomTransitiveProperty::CustomTransitiveProperty(
- std::string interfaceName, UseTo usage)
- : CustomTransitiveProperty(
- cm::make_unique<std::string>(std::move(interfaceName)), usage)
- {
- }
- cmGeneratorTarget::CustomTransitiveProperty::CustomTransitiveProperty(
- std::unique_ptr<std::string> interfaceNameBuf, UseTo usage)
- : TransitiveProperty{ *interfaceNameBuf, usage }
- , InterfaceNameBuf(std::move(interfaceNameBuf))
- {
- }
- void cmGeneratorTarget::CustomTransitiveProperties::Add(cmValue props,
- UseTo usage)
- {
- if (props) {
- cmList propsList(*props);
- for (std::string p : propsList) {
- std::string ip;
- static cm::string_view const kINTERFACE_ = "INTERFACE_"_s;
- if (cmHasPrefix(p, kINTERFACE_)) {
- ip = std::move(p);
- p = ip.substr(kINTERFACE_.length());
- } else {
- ip = cmStrCat(kINTERFACE_, p);
- }
- this->emplace(std::move(p),
- CustomTransitiveProperty(std::move(ip), usage));
- }
- }
- }
- cmGeneratorTarget::CustomTransitiveProperties const&
- cmGeneratorTarget::GetCustomTransitiveProperties(std::string const& config,
- PropertyFor propertyFor) const
- {
- std::map<std::string, CustomTransitiveProperties>& ctpm =
- propertyFor == PropertyFor::Build
- ? this->CustomTransitiveBuildPropertiesMap
- : this->CustomTransitiveInterfacePropertiesMap;
- auto i = ctpm.find(config);
- if (i == ctpm.end()) {
- CustomTransitiveProperties ctp;
- auto addTransitiveProperties = [this, &config, propertyFor,
- &ctp](std::string const& tp, UseTo usage) {
- // Add transitive properties named by the target itself.
- ctp.Add(this->GetProperty(tp), usage);
- // Add transitive properties named by the target's link dependencies.
- if (propertyFor == PropertyFor::Build) {
- for (cmGeneratorTarget const* gt :
- this->GetLinkImplementationClosure(config, usage)) {
- ctp.Add(gt->GetProperty(tp), usage);
- }
- } else {
- // The set of custom transitive INTERFACE_ properties does not
- // depend on the consumer. Use the target as its own head.
- cmGeneratorTarget const* headTarget = this;
- for (cmGeneratorTarget const* gt :
- this->GetLinkInterfaceClosure(config, headTarget, usage)) {
- ctp.Add(gt->GetProperty(tp), usage);
- }
- }
- };
- addTransitiveProperties("TRANSITIVE_LINK_PROPERTIES", UseTo::Link);
- addTransitiveProperties("TRANSITIVE_COMPILE_PROPERTIES", UseTo::Compile);
- i = ctpm.emplace(config, std::move(ctp)).first;
- }
- return i->second;
- }
|