Bladeren bron

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
Brad King 6 jaren geleden
bovenliggende
commit
1d3841b600
2 gewijzigde bestanden met toevoegingen van 52 en 0 verwijderingen
  1. 47 0
      Source/cmGeneratorTarget.cxx
  2. 5 0
      Source/cmGeneratorTarget.h

+ 47 - 0
Source/cmGeneratorTarget.cxx

@@ -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.

+ 5 - 0
Source/cmGeneratorTarget.h

@@ -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;