diff options
author | Brad King <brad.king@kitware.com> | 2019-07-21 11:57:08 (GMT) |
---|---|---|
committer | Brad King <brad.king@kitware.com> | 2019-07-23 10:46:34 (GMT) |
commit | 11fa818ecda0b50446aef891b06976973005e94b (patch) | |
tree | 96346a94e65e0aae090da50ad8862b9fec4b2aab /Source | |
parent | 0239bf8ac88bb8ae9d8945be506cee2c9adb08f5 (diff) | |
download | CMake-11fa818ecda0b50446aef891b06976973005e94b.zip CMake-11fa818ecda0b50446aef891b06976973005e94b.tar.gz CMake-11fa818ecda0b50446aef891b06976973005e94b.tar.bz2 |
Genex: Optimize usage requirement TARGET_PROPERTY recursion
In large projects the generation process spends a lot of time evaluating
usage requirements through transitive interface properties on targets.
This can be seen in a contrived example with deep dependencies:
set(prev "")
foreach(i RANGE 1 500)
add_library(a${i} a.c)
target_compile_definitions(a${i} PUBLIC A${i})
target_link_libraries(a${i} PUBLIC ${prev})
set(prev a${i})
endforeach()
For each usage requirement (such as `INTERFACE_COMPILE_DEFINITIONS` or
`INTERFACE_INCLUDE_DIRECTORIES`), the value of the generator expression
`$<TARGET_PROPERTY:target,prop>` includes the values of the same
property from the transitive closure of link libraries of the target.
Previously we computed this by constructing a generator expression
string like `$<TARGET_PROPERTY:lib,INTERFACE_COMPILE_DEFINITIONS>` and
recursively evaluating it with the generator expression engine. Avoid
the string construction and parsing by creating and using a dedicated
evaluation method `cmGeneratorTarget::EvaluateInterfaceProperty` that
looks up the properties directly.
Issue: #18964, #18965
Diffstat (limited to 'Source')
-rw-r--r-- | Source/cmGeneratorExpressionNode.cxx | 35 | ||||
-rw-r--r-- | Source/cmGeneratorTarget.cxx | 75 | ||||
-rw-r--r-- | Source/cmGeneratorTarget.h | 7 |
3 files changed, 95 insertions, 22 deletions
diff --git a/Source/cmGeneratorExpressionNode.cxx b/Source/cmGeneratorExpressionNode.cxx index b268ea2..8d02b68 100644 --- a/Source/cmGeneratorExpressionNode.cxx +++ b/Source/cmGeneratorExpressionNode.cxx @@ -1243,6 +1243,11 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode } } + if (isInterfaceProperty) { + return target->EvaluateInterfaceProperty(propertyName, context, + dagCheckerParent); + } + cmGeneratorExpressionDAGChecker dagChecker( context->Backtrace, target, propertyName, content, dagCheckerParent); @@ -1254,10 +1259,8 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode // No error. We just skip cyclic references. return std::string(); case cmGeneratorExpressionDAGChecker::ALREADY_SEEN: - if (isInterfaceProperty) { - // No error. We're not going to find anything new here. - return std::string(); - } + // We handle transitive properties above. For non-transitive + // properties we accept repeats anyway. case cmGeneratorExpressionDAGChecker::DAG: break; } @@ -1328,27 +1331,15 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode } if (!interfacePropertyName.empty()) { - cmGeneratorTarget const* headTarget = - context->HeadTarget && isInterfaceProperty ? context->HeadTarget - : target; + cmGeneratorTarget const* headTarget = target; result = this->EvaluateDependentExpression( result, context->LG, context, headTarget, target, &dagChecker); std::string linkedTargetsContent; - if (isInterfaceProperty) { - if (cmLinkInterfaceLibraries const* iface = - target->GetLinkInterfaceLibraries(context->Config, headTarget, - true)) { - linkedTargetsContent = getLinkedTargetsContent( - iface->Libraries, target, headTarget, context, &dagChecker, - interfacePropertyName); - } - } else { - if (cmLinkImplementationLibraries const* impl = - target->GetLinkImplementationLibraries(context->Config)) { - linkedTargetsContent = - getLinkedTargetsContent(impl->Libraries, target, target, context, - &dagChecker, interfacePropertyName); - } + if (cmLinkImplementationLibraries const* impl = + target->GetLinkImplementationLibraries(context->Config)) { + linkedTargetsContent = + getLinkedTargetsContent(impl->Libraries, target, target, context, + &dagChecker, interfacePropertyName); } if (!linkedTargetsContent.empty()) { result += (result.empty() ? "" : ";") + linkedTargetsContent; diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx index 6e6c917..21da2fd 100644 --- a/Source/cmGeneratorTarget.cxx +++ b/Source/cmGeneratorTarget.cxx @@ -22,7 +22,9 @@ #include "cmCustomCommandGenerator.h" #include "cmCustomCommandLines.h" #include "cmGeneratorExpression.h" +#include "cmGeneratorExpressionContext.h" #include "cmGeneratorExpressionDAGChecker.h" +#include "cmGeneratorExpressionNode.h" #include "cmGlobalGenerator.h" #include "cmLocalGenerator.h" #include "cmMakefile.h" @@ -1141,6 +1143,79 @@ bool cmGeneratorTarget::GetPropertyAsBool(const std::string& prop) const return this->Target->GetPropertyAsBool(prop); } +std::string cmGeneratorTarget::EvaluateInterfaceProperty( + std::string const& prop, cmGeneratorExpressionContext* context, + cmGeneratorExpressionDAGChecker* dagCheckerParent) const +{ + std::string 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(context->Backtrace, this, prop, + nullptr, dagCheckerParent); + 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. + return result; + 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 (const char* p = this->GetProperty(prop)) { + result = cmGeneratorExpressionNode::EvaluateDependentExpression( + p, context->LG, context, headTarget, this, &dagChecker); + } + + if (cmLinkInterfaceLibraries const* iface = + this->GetLinkInterfaceLibraries(context->Config, headTarget, true)) { + 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)); + 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; +} + namespace { void AddInterfaceEntries(cmGeneratorTarget const* headTarget, std::string const& config, std::string const& prop, diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h index e86535d..b875c40 100644 --- a/Source/cmGeneratorTarget.h +++ b/Source/cmGeneratorTarget.h @@ -25,6 +25,9 @@ class cmMakefile; class cmSourceFile; class cmTarget; +struct cmGeneratorExpressionContext; +struct cmGeneratorExpressionDAGChecker; + class cmGeneratorTarget { public: @@ -674,6 +677,10 @@ public: class TargetPropertyEntry; + std::string EvaluateInterfaceProperty( + std::string const& prop, cmGeneratorExpressionContext* context, + cmGeneratorExpressionDAGChecker* dagCheckerParent) const; + bool HaveInstallTreeRPATH(const std::string& config) const; bool GetBuildRPATH(const std::string& config, std::string& rpath) const; |