mirror of
https://github.com/reactos/CMake.git
synced 2024-12-11 21:34:32 +00:00
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
This commit is contained in:
parent
0239bf8ac8
commit
11fa818ecd
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user