mirror of
https://github.com/Kitware/CMake.git
synced 2026-07-12 10:34:32 +00:00
Genex: Memoize usage requirement TARGET_PROPERTY existence
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. In cases that a target's transitive closure of dependencies does not depend on the target being linked (the "head" target), we can memoize whether or not a usage requirement property exists at all for that target. When a usage requirement does not exist for a target, we can skip evaluating it for every consuming target. Fixes: #18964, #18965
This commit is contained in:
@@ -1143,12 +1143,59 @@ bool cmGeneratorTarget::GetPropertyAsBool(const std::string& prop) const
|
||||
return this->Target->GetPropertyAsBool(prop);
|
||||
}
|
||||
|
||||
bool cmGeneratorTarget::MaybeHaveInterfaceProperty(
|
||||
std::string const& prop, cmGeneratorExpressionContext* context) const
|
||||
{
|
||||
std::string const key = prop + '@' + context->Config;
|
||||
auto i = this->MaybeInterfacePropertyExists.find(key);
|
||||
if (i == this->MaybeInterfacePropertyExists.end()) {
|
||||
// Insert an entry now in case there is a cycle.
|
||||
i = this->MaybeInterfacePropertyExists.emplace(key, false).first;
|
||||
bool& maybeInterfaceProp = i->second;
|
||||
|
||||
// If this target itself has a non-empty property value, we are done.
|
||||
const char* p = this->GetProperty(prop);
|
||||
maybeInterfaceProp = p && *p;
|
||||
|
||||
// Otherwise, recurse to interface dependencies.
|
||||
if (!maybeInterfaceProp) {
|
||||
cmGeneratorTarget const* headTarget =
|
||||
context->HeadTarget ? context->HeadTarget : this;
|
||||
if (cmLinkInterfaceLibraries const* iface =
|
||||
this->GetLinkInterfaceLibraries(context->Config, headTarget,
|
||||
true)) {
|
||||
if (iface->HadHeadSensitiveCondition) {
|
||||
// With a different head target we may get to a library with
|
||||
// this interface property.
|
||||
maybeInterfaceProp = true;
|
||||
} else {
|
||||
// The transitive interface libraries do not depend on the
|
||||
// head target, so we can follow them.
|
||||
for (cmLinkItem const& lib : iface->Libraries) {
|
||||
if (lib.Target &&
|
||||
lib.Target->MaybeHaveInterfaceProperty(prop, context)) {
|
||||
maybeInterfaceProp = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return i->second;
|
||||
}
|
||||
|
||||
std::string cmGeneratorTarget::EvaluateInterfaceProperty(
|
||||
std::string const& prop, cmGeneratorExpressionContext* context,
|
||||
cmGeneratorExpressionDAGChecker* dagCheckerParent) const
|
||||
{
|
||||
std::string result;
|
||||
|
||||
// If the property does not appear transitively at all, we are done.
|
||||
if (!this->MaybeHaveInterfaceProperty(prop, context)) {
|
||||
return 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.
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <set>
|
||||
#include <stddef.h>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
@@ -856,6 +857,10 @@ private:
|
||||
mutable std::vector<AllConfigSource> AllConfigSources;
|
||||
void ComputeAllConfigSources() const;
|
||||
|
||||
mutable std::unordered_map<std::string, bool> MaybeInterfacePropertyExists;
|
||||
bool MaybeHaveInterfaceProperty(std::string const& prop,
|
||||
cmGeneratorExpressionContext* context) const;
|
||||
|
||||
std::vector<TargetPropertyEntry*> IncludeDirectoriesEntries;
|
||||
std::vector<TargetPropertyEntry*> CompileOptionsEntries;
|
||||
std::vector<TargetPropertyEntry*> CompileFeaturesEntries;
|
||||
|
||||
Reference in New Issue
Block a user