mirror of
https://github.com/Kitware/CMake.git
synced 2026-04-24 07:08:38 -05:00
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:
@@ -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);
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user