| /* 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 "cmListFileCache.h" | |
| #include "cmLocalGenerator.h" | |
| #include "cmPolicies.h" | |
| #include "cmStringAlgorithms.h" | |
| #include "cmValue.h" | |
| namespace { | |
| using UseTo = cmGeneratorTarget::UseTo; | |
| using TransitiveProperty = cmGeneratorTarget::TransitiveProperty; | |
| bool ComputingLinkLibraries(cmGeneratorExpressionDAGChecker const* dagChecker) | |
| { | |
| return dagChecker && dagChecker->IsComputingLinkLibraries(); | |
| } | |
| } | |
| 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 } }, | |
| { "COMPILE_WRAPPERS"_s, | |
| { "INTERFACE_COMPILE_WRAPPERS"_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 } }, | |
| { "LINK_WRAPPERS"_s, { "INTERFACE_LINK_WRAPPERS"_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 = cmStrCat(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, eval->Backtrace, | |
| }; | |
| switch (dagChecker.Check()) { | |
| case cmGeneratorExpressionDAGChecker::SELF_REFERENCE: | |
| dagChecker.ReportError( | |
| eval, cmStrCat("$<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, cm::GenEx::Context const& context, | |
| 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. | |
| context.GetCMP0189() != 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 = | |
| context.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 (!ComputingLinkLibraries(dagChecker)) { | |
| // 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(context.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; | |
| } |