You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
cmake/Source/cmGeneratorTarget_Transitiv...

307 lines
12 KiB

/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt 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 "cmGeneratorExpression.h"
#include "cmGeneratorExpressionContext.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;
}
const std::map<cm::string_view, TransitiveProperty>
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_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, cmGeneratorExpressionContext* context,
UseTo usage) const
{
std::string const key = prop + '@' + 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 =
context->HeadTarget ? context->HeadTarget : this;
if (cmLinkInterfaceLibraries const* iface =
this->GetLinkInterfaceLibraries(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, context, usage)) {
maybeInterfaceProp = true;
break;
}
}
}
}
}
}
return i->second;
}
std::string cmGeneratorTarget::EvaluateInterfaceProperty(
std::string const& prop, cmGeneratorExpressionContext* context,
cmGeneratorExpressionDAGChecker* dagCheckerParent, UseTo usage) const
{
return EvaluateInterfaceProperty(prop, context, dagCheckerParent, usage,
TransitiveClosure::Inherit);
}
std::string cmGeneratorTarget::EvaluateInterfaceProperty(
std::string const& prop, cmGeneratorExpressionContext* context,
cmGeneratorExpressionDAGChecker* dagCheckerParent, UseTo usage,
TransitiveClosure closure) const
{
std::string result;
// If the property does not appear transitively at all, we are done.
if (!this->MaybeHaveInterfaceProperty(prop, context, usage)) {
return result;
}
auto const dagClosure = closure == TransitiveClosure::Inherit
? cmGeneratorExpressionDAGChecker::INHERIT
: cmGeneratorExpressionDAGChecker::SUBGRAPH;
// 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(
context->Backtrace, this, prop, nullptr, dagCheckerParent,
this->LocalGenerator, context->Config, dagClosure);
switch (dagChecker.Check()) {
case cmGeneratorExpressionDAGChecker::SELF_REFERENCE:
dagChecker.ReportError(
context, "$<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 =
context->HeadTarget ? context->HeadTarget : this;
if (cmValue p = this->GetProperty(prop)) {
result = cmGeneratorExpressionNode::EvaluateDependentExpression(
*p, context->LG, context, headTarget, &dagChecker, this);
}
if (cmLinkInterfaceLibraries const* iface =
this->GetLinkInterfaceLibraries(context->Config, headTarget, usage)) {
context->HadContextSensitiveCondition =
context->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.
cmGeneratorExpressionContext libContext(
context->LG, context->Config, context->Quiet, headTarget, this,
context->EvaluateForBuildsystem, context->Backtrace,
context->Language);
std::string libResult = cmGeneratorExpression::StripEmptyListElements(
lib.Target->EvaluateInterfaceProperty(prop, &libContext, &dagChecker,
usage));
if (!libResult.empty()) {
if (result.empty()) {
result = std::move(libResult);
} else {
result.reserve(result.size() + 1 + libResult.size());
result += ";";
result += libResult;
}
}
context->HadContextSensitiveCondition =
context->HadContextSensitiveCondition ||
libContext.HadContextSensitiveCondition;
context->HadHeadSensitiveCondition =
context->HadHeadSensitiveCondition ||
libContext.HadHeadSensitiveCondition;
}
}
}
return result;
}
cm::optional<cmGeneratorTarget::TransitiveProperty>
cmGeneratorTarget::IsTransitiveProperty(cm::string_view prop,
cmLocalGenerator const* lg,
std::string const& config,
bool evaluatingLinkLibraries) const
{
cm::optional<TransitiveProperty> result;
static const cm::string_view 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()) {
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 (cmHasLiteralPrefix(prop, "COMPILE_DEFINITIONS_")) {
cmPolicies::PolicyStatus cmp0043 =
lg->GetPolicyStatus(cmPolicies::CMP0043);
if (cmp0043 == cmPolicies::WARN || cmp0043 == cmPolicies::OLD) {
result = TransitiveProperty{ "INTERFACE_COMPILE_DEFINITIONS"_s,
UseTo::Compile };
}
} else if (!evaluatingLinkLibraries) {
// 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 const cm::string_view 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;
}