summaryrefslogtreecommitdiffstats
path: root/Source
diff options
context:
space:
mode:
authorBrad King <brad.king@kitware.com>2019-07-21 11:57:08 (GMT)
committerBrad King <brad.king@kitware.com>2019-07-23 10:46:34 (GMT)
commit11fa818ecda0b50446aef891b06976973005e94b (patch)
tree96346a94e65e0aae090da50ad8862b9fec4b2aab /Source
parent0239bf8ac88bb8ae9d8945be506cee2c9adb08f5 (diff)
downloadCMake-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.cxx35
-rw-r--r--Source/cmGeneratorTarget.cxx75
-rw-r--r--Source/cmGeneratorTarget.h7
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;