1
0
mirror of https://github.com/Kitware/CMake.git synced 2025-10-14 19:08:07 +08:00

GenEx: Limit TARGET_PROPERTY transitive closure optimization to subgraphs

Fixes: #25728
This commit is contained in:
Michael Herwig
2024-05-31 18:15:55 +02:00
parent 9673f61be1
commit 4a11772618
8 changed files with 78 additions and 8 deletions

View File

@@ -1877,6 +1877,14 @@ These expressions look up the values of
rather than the directory of the consuming target for which the rather than the directory of the consuming target for which the
expression is being evaluated. expression is being evaluated.
.. versionchanged:: 3.31
Generator expressions for transitive interface properties, such as
``$<TARGET_PROPERTY:target,INTERFACE_*>``, now correctly handle
repeated evaluations within nested generator expressions.
Previously, these repeated evaluations returned empty values due
to an optimization for transitive closures.
This change ensures consistent evaluation for non-union operations.
.. genex:: $<TARGET_PROPERTY:prop> .. genex:: $<TARGET_PROPERTY:prop>
:target: TARGET_PROPERTY:prop :target: TARGET_PROPERTY:prop

View File

@@ -24,7 +24,7 @@ cmGeneratorExpressionDAGChecker::cmGeneratorExpressionDAGChecker(
std::string const& contextConfig) std::string const& contextConfig)
: cmGeneratorExpressionDAGChecker(cmListFileBacktrace(), target, : cmGeneratorExpressionDAGChecker(cmListFileBacktrace(), target,
std::move(property), content, parent, std::move(property), content, parent,
contextLG, contextConfig) contextLG, contextConfig, INHERIT)
{ {
} }
@@ -33,8 +33,20 @@ cmGeneratorExpressionDAGChecker::cmGeneratorExpressionDAGChecker(
std::string property, const GeneratorExpressionContent* content, std::string property, const GeneratorExpressionContent* content,
cmGeneratorExpressionDAGChecker* parent, cmLocalGenerator const* contextLG, cmGeneratorExpressionDAGChecker* parent, cmLocalGenerator const* contextLG,
std::string const& contextConfig) std::string const& contextConfig)
: cmGeneratorExpressionDAGChecker(std::move(backtrace), target,
std::move(property), content, parent,
contextLG, contextConfig, INHERIT)
{
}
cmGeneratorExpressionDAGChecker::cmGeneratorExpressionDAGChecker(
cmListFileBacktrace backtrace, cmGeneratorTarget const* target,
std::string property, const GeneratorExpressionContent* content,
cmGeneratorExpressionDAGChecker* parent, cmLocalGenerator const* contextLG,
std::string const& contextConfig, TransitiveClosure closure)
: Parent(parent) : Parent(parent)
, Top(parent ? parent->Top : this) , Top(parent ? parent->Top : this)
, Closure((closure == SUBGRAPH || !parent) ? this : parent->Closure)
, Target(target) , Target(target)
, Property(std::move(property)) , Property(std::move(property))
, Content(content) , Content(content)
@@ -53,16 +65,16 @@ cmGeneratorExpressionDAGChecker::cmGeneratorExpressionDAGChecker(
this->CheckResult = this->CheckGraph(); this->CheckResult = this->CheckGraph();
if (this->CheckResult == DAG && this->EvaluatingTransitiveProperty()) { if (this->CheckResult == DAG && this->EvaluatingTransitiveProperty()) {
const auto* top = this->Top; const auto* transitiveClosure = this->Closure;
auto it = top->Seen.find(this->Target); auto it = transitiveClosure->Seen.find(this->Target);
if (it != top->Seen.end()) { if (it != transitiveClosure->Seen.end()) {
const std::set<std::string>& propSet = it->second; const std::set<std::string>& propSet = it->second;
if (propSet.find(this->Property) != propSet.end()) { if (propSet.find(this->Property) != propSet.end()) {
this->CheckResult = ALREADY_SEEN; this->CheckResult = ALREADY_SEEN;
return; return;
} }
} }
top->Seen[this->Target].insert(this->Property); transitiveClosure->Seen[this->Target].insert(this->Property);
} }
} }

View File

@@ -17,6 +17,17 @@ class cmLocalGenerator;
struct cmGeneratorExpressionDAGChecker struct cmGeneratorExpressionDAGChecker
{ {
enum TransitiveClosure
{
INHERIT,
SUBGRAPH,
};
cmGeneratorExpressionDAGChecker(
cmListFileBacktrace backtrace, cmGeneratorTarget const* target,
std::string property, const GeneratorExpressionContent* content,
cmGeneratorExpressionDAGChecker* parent, cmLocalGenerator const* contextLG,
std::string const& contextConfig, TransitiveClosure closure);
cmGeneratorExpressionDAGChecker(cmListFileBacktrace backtrace, cmGeneratorExpressionDAGChecker(cmListFileBacktrace backtrace,
cmGeneratorTarget const* target, cmGeneratorTarget const* target,
std::string property, std::string property,
@@ -76,6 +87,7 @@ private:
const cmGeneratorExpressionDAGChecker* const Parent; const cmGeneratorExpressionDAGChecker* const Parent;
const cmGeneratorExpressionDAGChecker* const Top; const cmGeneratorExpressionDAGChecker* const Top;
const cmGeneratorExpressionDAGChecker* const Closure;
cmGeneratorTarget const* Target; cmGeneratorTarget const* Target;
const std::string Property; const std::string Property;
mutable std::map<cmGeneratorTarget const*, std::set<std::string>> Seen; mutable std::map<cmGeneratorTarget const*, std::set<std::string>> Seen;

View File

@@ -2983,8 +2983,9 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode
if (isInterfaceProperty) { if (isInterfaceProperty) {
return cmGeneratorExpression::StripEmptyListElements( return cmGeneratorExpression::StripEmptyListElements(
target->EvaluateInterfaceProperty(propertyName, context, target->EvaluateInterfaceProperty(
dagCheckerParent, usage)); propertyName, context, dagCheckerParent, usage,
cmGeneratorTarget::TransitiveClosure::Subgraph));
} }
cmGeneratorExpressionDAGChecker dagChecker( cmGeneratorExpressionDAGChecker dagChecker(

View File

@@ -918,11 +918,23 @@ public:
const std::string& report, const std::string& report,
const std::string& compatibilityType) const; const std::string& compatibilityType) const;
/** Configures TransitiveClosureOptimization. Re-evaluation of a
transitive property will only be optimized within a subgraph. */
enum class TransitiveClosure
{
Inherit, // node is in the same subgraph as its' parent
Subgraph, // the current node spans a new subgraph
};
class TargetPropertyEntry; class TargetPropertyEntry;
std::string EvaluateInterfaceProperty( std::string EvaluateInterfaceProperty(
std::string const& prop, cmGeneratorExpressionContext* context, std::string const& prop, cmGeneratorExpressionContext* context,
cmGeneratorExpressionDAGChecker* dagCheckerParent, UseTo usage) const; cmGeneratorExpressionDAGChecker* dagCheckerParent, UseTo usage) const;
std::string EvaluateInterfaceProperty(
std::string const& prop, cmGeneratorExpressionContext* context,
cmGeneratorExpressionDAGChecker* dagCheckerParent, UseTo usage,
TransitiveClosure closure) const;
struct TransitiveProperty struct TransitiveProperty
{ {

View File

@@ -98,6 +98,15 @@ bool cmGeneratorTarget::MaybeHaveInterfaceProperty(
std::string cmGeneratorTarget::EvaluateInterfaceProperty( std::string cmGeneratorTarget::EvaluateInterfaceProperty(
std::string const& prop, cmGeneratorExpressionContext* context, std::string const& prop, cmGeneratorExpressionContext* context,
cmGeneratorExpressionDAGChecker* dagCheckerParent, UseTo usage) const 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; std::string result;
@@ -106,12 +115,15 @@ std::string cmGeneratorTarget::EvaluateInterfaceProperty(
return result; return result;
} }
auto const dagClosure = closure == TransitiveClosure::Inherit
? cmGeneratorExpressionDAGChecker::INHERIT
: cmGeneratorExpressionDAGChecker::SUBGRAPH;
// Evaluate $<TARGET_PROPERTY:this,prop> as if it were compiled. This is // Evaluate $<TARGET_PROPERTY:this,prop> as if it were compiled. This is
// a subset of TargetPropertyNode::Evaluate without stringify/parse steps // a subset of TargetPropertyNode::Evaluate without stringify/parse steps
// but sufficient for transitive interface properties. // but sufficient for transitive interface properties.
cmGeneratorExpressionDAGChecker dagChecker( cmGeneratorExpressionDAGChecker dagChecker(
context->Backtrace, this, prop, nullptr, dagCheckerParent, context->Backtrace, this, prop, nullptr, dagCheckerParent,
this->LocalGenerator, context->Config); this->LocalGenerator, context->Config, dagClosure);
switch (dagChecker.Check()) { switch (dagChecker.Check()) {
case cmGeneratorExpressionDAGChecker::SELF_REFERENCE: case cmGeneratorExpressionDAGChecker::SELF_REFERENCE:
dagChecker.ReportError( dagChecker.ReportError(

View File

@@ -112,6 +112,15 @@ target_link_libraries(empty3 LINK_PUBLIC empty2 empty4)
add_library(empty5 empty.cpp) add_library(empty5 empty.cpp)
target_include_directories(empty5 PRIVATE /empty5/private1 /empty5/private2) target_include_directories(empty5 PRIVATE /empty5/private1 /empty5/private2)
add_library(interface1 INTERFACE)
target_sources(interface1 INTERFACE foo.c bar.cpp)
set(interface1Sources $<TARGET_PROPERTY:interface1,INTERFACE_SOURCES>)
set(interface1MergeSources $<LIST:APPEND,$<LIST:FILTER,${interface1Sources},INCLUDE,.*\\.c$>,$<LIST:FILTER,${interface1Sources},EXCLUDE,.*\\.c$>>)
add_library(interface2 INTERFACE)
target_sources(interface2 INTERFACE ${interface1MergeSources})
add_custom_target(check-part2 ALL add_custom_target(check-part2 ALL
COMMAND ${msys2_no_conv} ${CMAKE_COMMAND} COMMAND ${msys2_no_conv} ${CMAKE_COMMAND}
-Dtest_incomplete_1=$< -Dtest_incomplete_1=$<
@@ -147,6 +156,8 @@ add_custom_target(check-part2 ALL
-Dtest_target_includes6=$<TARGET_PROPERTY:empty3,INCLUDE_DIRECTORIES> -Dtest_target_includes6=$<TARGET_PROPERTY:empty3,INCLUDE_DIRECTORIES>
-Dtest_target_includes7=$<TARGET_PROPERTY:empty1,INTERFACE_INCLUDE_DIRECTORIES> -Dtest_target_includes7=$<TARGET_PROPERTY:empty1,INTERFACE_INCLUDE_DIRECTORIES>
-Dtest_target_includes8=$<TARGET_PROPERTY:empty5,INCLUDE_DIRECTORIES> -Dtest_target_includes8=$<TARGET_PROPERTY:empty5,INCLUDE_DIRECTORIES>
-Dtest_target_closure1=$<JOIN:$<LIST:SORT,${interface1MergeSources}>,$<COMMA>>
-Dtest_target_closure2=$<JOIN:$<LIST:SORT,$<TARGET_PROPERTY:interface2,INTERFACE_SOURCES>>,$<COMMA>>
-Dtest_arbitrary_content_comma_1=$<1:a,> -Dtest_arbitrary_content_comma_1=$<1:a,>
-Dtest_arbitrary_content_comma_2=$<1:,a> -Dtest_arbitrary_content_comma_2=$<1:,a>
-Dtest_arbitrary_content_comma_3=$<1:a,,> -Dtest_arbitrary_content_comma_3=$<1:a,,>

View File

@@ -34,6 +34,8 @@ check(test_target_includes5 "/empty2/public;/empty3/public;/empty2/public;/empty
check(test_target_includes6 "/empty3/public;/empty3/private;/empty2/public;/empty3/public;/empty4/public") check(test_target_includes6 "/empty3/public;/empty3/private;/empty2/public;/empty3/public;/empty4/public")
check(test_target_includes7 "/empty1/public;/empty2/public;/empty3/public;/empty4/public") check(test_target_includes7 "/empty1/public;/empty2/public;/empty3/public;/empty4/public")
check(test_target_includes8 "/empty5/private1;/empty5/private2") check(test_target_includes8 "/empty5/private1;/empty5/private2")
check(test_target_closure1 "bar.cpp,foo.c")
check(test_target_closure2 "bar.cpp,foo.c")
check(test_arbitrary_content_comma_1 "a,") check(test_arbitrary_content_comma_1 "a,")
check(test_arbitrary_content_comma_2 ",a") check(test_arbitrary_content_comma_2 ",a")
check(test_arbitrary_content_comma_3 "a,,") check(test_arbitrary_content_comma_3 "a,,")