GenEx: Add support for custom transitive compile properties

Teach the `$<TARGET_PROPERTY:...>` generator expression to check for a
new `TRANSITIVE_COMPILE_PROPERTIES` property in the target's link
closure to enable transitive evaluation of named properties through
the link closure, excluding entries guarded by `$<LINK_ONLY:...>`.

Issue: #20416
This commit is contained in:
Brad King
2024-05-09 13:38:35 -04:00
parent 633afa0b2e
commit b9ee79b8a1
27 changed files with 643 additions and 10 deletions
+2
View File
@@ -152,6 +152,8 @@ bool cmExportBuildFileGenerator::GenerateMainFile(std::ostream& os)
gte, cmGeneratorExpression::BuildInterface, properties);
}
this->PopulateCompatibleInterfaceProperties(gte, properties);
this->PopulateCustomTransitiveInterfaceProperties(
gte, cmGeneratorExpression::BuildInterface, properties);
this->GenerateInterfaceProperties(gte, os, properties);
+19
View File
@@ -604,6 +604,25 @@ void cmExportFileGenerator::PopulateCompatibleInterfaceProperties(
}
}
void cmExportFileGenerator::PopulateCustomTransitiveInterfaceProperties(
cmGeneratorTarget const* target,
cmGeneratorExpression::PreprocessContext preprocessRule,
ImportPropertyMap& properties)
{
this->PopulateInterfaceProperty("TRANSITIVE_COMPILE_PROPERTIES", target,
properties);
std::set<std::string> ifaceProperties;
for (std::string const& config : this->Configurations) {
for (auto const& i : target->GetCustomTransitiveProperties(
config, cmGeneratorTarget::PropertyFor::Interface)) {
ifaceProperties.emplace(i.second.InterfaceName);
}
}
for (std::string const& ip : ifaceProperties) {
this->PopulateInterfaceProperty(ip, target, preprocessRule, properties);
}
}
void cmExportFileGenerator::GenerateInterfaceProperties(
const cmGeneratorTarget* target, std::ostream& os,
const ImportPropertyMap& properties)
+4
View File
@@ -145,6 +145,10 @@ protected:
ImportPropertyMap& properties);
void PopulateCompatibleInterfaceProperties(cmGeneratorTarget const* target,
ImportPropertyMap& properties);
void PopulateCustomTransitiveInterfaceProperties(
cmGeneratorTarget const* target,
cmGeneratorExpression::PreprocessContext preprocessRule,
ImportPropertyMap& properties);
virtual void GenerateInterfaceProperties(
cmGeneratorTarget const* target, std::ostream& os,
const ImportPropertyMap& properties);
+2
View File
@@ -160,6 +160,8 @@ bool cmExportInstallFileGenerator::GenerateMainFile(std::ostream& os)
properties);
this->PopulateCompatibleInterfaceProperties(gt, properties);
this->PopulateCustomTransitiveInterfaceProperties(
gt, cmGeneratorExpression::InstallInterface, properties);
this->GenerateInterfaceProperties(gt, os, properties);
+3 -2
View File
@@ -40,12 +40,13 @@ cmGeneratorExpressionDAGChecker::cmGeneratorExpressionDAGChecker(
, Content(content)
, Backtrace(std::move(backtrace))
{
static_cast<void>(contextConfig);
if (parent) {
this->TopIsTransitiveProperty = parent->TopIsTransitiveProperty;
} else {
this->TopIsTransitiveProperty =
this->Target->IsTransitiveProperty(this->Property, contextLG)
this->Target
->IsTransitiveProperty(this->Property, contextLG, contextConfig,
this->EvaluatingLinkLibraries())
.has_value();
}
+7 -5
View File
@@ -2873,19 +2873,22 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode
return target->GetLinkerLanguage(context->Config);
}
bool const evaluatingLinkLibraries =
dagCheckerParent && dagCheckerParent->EvaluatingLinkLibraries();
std::string interfacePropertyName;
bool isInterfaceProperty = false;
cmGeneratorTarget::UseTo usage = cmGeneratorTarget::UseTo::Compile;
if (cm::optional<cmGeneratorTarget::TransitiveProperty> transitiveProp =
target->IsTransitiveProperty(propertyName, context->LG)) {
target->IsTransitiveProperty(propertyName, context->LG,
context->Config,
evaluatingLinkLibraries)) {
interfacePropertyName = std::string(transitiveProp->InterfaceName);
isInterfaceProperty = transitiveProp->InterfaceName == propertyName;
usage = transitiveProp->Usage;
}
bool evaluatingLinkLibraries = false;
if (dagCheckerParent) {
// This $<TARGET_PROPERTY:...> node has been reached while evaluating
// another target property value. Check that the outermost evaluation
@@ -2894,8 +2897,7 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode
dagCheckerParent->EvaluatingPICExpression() ||
dagCheckerParent->EvaluatingLinkerLauncher()) {
// No check required.
} else if (dagCheckerParent->EvaluatingLinkLibraries()) {
evaluatingLinkLibraries = true;
} else if (evaluatingLinkLibraries) {
if (!interfacePropertyName.empty()) {
reportError(
context, content->GetOriginalExpression(),
+2
View File
@@ -518,6 +518,8 @@ void cmGeneratorTarget::ClearSourcesCache()
this->IncludeDirectoriesCache.clear();
this->CompileOptionsCache.clear();
this->CompileDefinitionsCache.clear();
this->CustomTransitiveBuildPropertiesMap.clear();
this->CustomTransitiveInterfacePropertiesMap.clear();
this->PrecompileHeadersCache.clear();
this->LinkOptionsCache.clear();
this->LinkDirectoriesCache.clear();
+37 -1
View File
@@ -907,7 +907,8 @@ public:
BuiltinTransitiveProperties;
cm::optional<TransitiveProperty> IsTransitiveProperty(
cm::string_view prop, cmLocalGenerator const* lg) const;
cm::string_view prop, cmLocalGenerator const* lg,
std::string const& config, bool evaluatingLinkLibraries) const;
bool HaveInstallTreeRPATH(const std::string& config) const;
@@ -989,6 +990,30 @@ public:
bool DiscoverSyntheticTargets(cmSyntheticTargetCache& cache,
std::string const& config);
class CustomTransitiveProperty : public TransitiveProperty
{
std::unique_ptr<std::string> InterfaceNameBuf;
CustomTransitiveProperty(std::unique_ptr<std::string> interfaceNameBuf,
UseTo usage);
public:
CustomTransitiveProperty(std::string interfaceName, UseTo usage);
};
struct CustomTransitiveProperties
: public std::map<std::string, CustomTransitiveProperty>
{
void Add(cmValue props, UseTo usage);
};
enum class PropertyFor
{
Build,
Interface,
};
CustomTransitiveProperties const& GetCustomTransitiveProperties(
std::string const& config, PropertyFor propertyFor) const;
private:
void AddSourceCommon(const std::string& src, bool before = false);
@@ -1056,6 +1081,11 @@ private:
std::string const& base, std::string const& suffix,
std::string const& name, cmValue version) const;
mutable std::map<std::string, CustomTransitiveProperties>
CustomTransitiveBuildPropertiesMap;
mutable std::map<std::string, CustomTransitiveProperties>
CustomTransitiveInterfacePropertiesMap;
struct CompatibleInterfacesBase
{
std::set<std::string> PropsBool;
@@ -1306,6 +1336,12 @@ private:
void ComputeLinkInterfaceRuntimeLibraries(
const std::string& config, cmOptionalLinkInterface& iface) const;
// If this method is made public, or call sites are added outside of
// methods computing cached members, add dedicated caching members.
std::vector<cmGeneratorTarget const*> GetLinkInterfaceClosure(
std::string const& config, cmGeneratorTarget const* headTarget,
UseTo usage) const;
public:
const std::vector<const cmGeneratorTarget*>& GetLinkImplementationClosure(
const std::string& config, UseTo usage) const;
+17
View File
@@ -282,6 +282,23 @@ static void processILibs(const std::string& config,
}
}
std::vector<cmGeneratorTarget const*>
cmGeneratorTarget::GetLinkInterfaceClosure(std::string const& config,
cmGeneratorTarget const* headTarget,
UseTo usage) const
{
cmGlobalGenerator* gg = this->GetLocalGenerator()->GetGlobalGenerator();
std::vector<cmGeneratorTarget const*> tgts;
std::set<cmGeneratorTarget const*> emitted;
if (cmLinkInterfaceLibraries const* iface =
this->GetLinkInterfaceLibraries(config, headTarget, usage)) {
for (cmLinkItem const& lib : iface->Libraries) {
processILibs(config, headTarget, lib, gg, tgts, emitted, usage);
}
}
return tgts;
}
const std::vector<const cmGeneratorTarget*>&
cmGeneratorTarget::GetLinkImplementationClosure(const std::string& config,
UseTo usage) const
@@ -10,6 +10,7 @@
#include <utility>
#include <vector>
#include <cm/memory>
#include <cm/optional>
#include <cm/string_view>
#include <cmext/string_view>
@@ -19,6 +20,7 @@
#include "cmGeneratorExpressionDAGChecker.h"
#include "cmGeneratorExpressionNode.h"
#include "cmLinkItem.h"
#include "cmList.h"
#include "cmLocalGenerator.h"
#include "cmPolicies.h"
#include "cmStringAlgorithms.h"
@@ -176,11 +178,16 @@ std::string cmGeneratorTarget::EvaluateInterfaceProperty(
cm::optional<cmGeneratorTarget::TransitiveProperty>
cmGeneratorTarget::IsTransitiveProperty(cm::string_view prop,
cmLocalGenerator const* lg) const
cmLocalGenerator const* lg,
std::string const& config,
bool evaluatingLinkLibraries) const
{
cm::optional<TransitiveProperty> result;
static const cm::string_view kINTERFACE_ = "INTERFACE_"_s;
if (cmHasPrefix(prop, kINTERFACE_)) {
PropertyFor const propertyFor = cmHasPrefix(prop, kINTERFACE_)
? PropertyFor::Interface
: PropertyFor::Build;
if (propertyFor == PropertyFor::Interface) {
prop = prop.substr(kINTERFACE_.length());
}
auto i = BuiltinTransitiveProperties.find(prop);
@@ -202,6 +209,85 @@ cmGeneratorTarget::IsTransitiveProperty(cm::string_view prop,
result = TransitiveProperty{ "INTERFACE_COMPILE_DEFINITIONS"_s,
UseTo::Compile };
}
} else if (!evaluatingLinkLibraries) {
// Honor TRANSITIVE_COMPILE_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_COMPILE_PROPERTIES", UseTo::Compile);
i = ctpm.emplace(config, std::move(ctp)).first;
}
return i->second;
}