Browse Source

Merge topic 'genex-link-properties'

ddb9442f48 GenEx: Fix TARGET_PROPERTY evaluation of transitive link properties
862b8e28ad GenEx: Teach TARGET_PROPERTY evaluation to optionally pierce LINK_ONLY
8d1d6a1437 Tests: Cover TARGET_PROPERTY genex evaluation of transitive link properties
abf607c2ec Tests: Cover TARGET_PROPERTY genex evaluation of transitive build properties
7d3d728a72 Help: Clarify CMP0099 documentation and summary text
79a3ae9a0d cmGeneratorExpressionDAGChecker: Simplify transitive property table
e8010b67c7 cmGeneratorExpressionDAGChecker: Make local generator available in constructor
b36fb3f6f1 cmGeneratorExpressionNode: Remove outdated lint suppression

Acked-by: Kitware Robot <[email protected]>
Tested-by: buildbot <[email protected]>
Merge-request: !9473
Brad King 1 year ago
parent
commit
de9faaf0a3
32 changed files with 431 additions and 167 deletions
  1. 13 4
      Help/manual/cmake-generator-expressions.7.rst
  2. 2 1
      Help/manual/cmake-policies.7.rst
  3. 14 5
      Help/policy/CMP0099.rst
  4. 40 0
      Help/policy/CMP0166.rst
  5. 8 0
      Help/release/dev/genex-link-properties.rst
  6. 2 1
      Source/cmCommonTargetGenerator.cxx
  7. 14 7
      Source/cmComputeLinkDepends.cxx
  8. 9 9
      Source/cmExportTryCompileFileGenerator.cxx
  9. 1 1
      Source/cmGeneratorExpression.cxx
  10. 14 44
      Source/cmGeneratorExpressionDAGChecker.cxx
  11. 6 34
      Source/cmGeneratorExpressionDAGChecker.h
  12. 19 29
      Source/cmGeneratorExpressionNode.cxx
  13. 92 25
      Source/cmGeneratorTarget.cxx
  14. 13 0
      Source/cmGeneratorTarget.h
  15. 7 3
      Source/cmPolicies.h
  16. 3 2
      Source/cmQtAutoGenInitializer.cxx
  17. 2 2
      Tests/RunCMake/GenEx-TARGET_PROPERTY/CMakeLists.txt
  18. 3 0
      Tests/RunCMake/GenEx-TARGET_PROPERTY/RunCMakeTest.cmake
  19. 25 0
      Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveBuild-check.cmake
  20. 66 0
      Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveBuild.cmake
  21. 8 0
      Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-CMP0166-NEW-check.cmake
  22. 2 0
      Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-CMP0166-NEW.cmake
  23. 8 0
      Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-CMP0166-OLD-check.cmake
  24. 2 0
      Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-CMP0166-OLD.cmake
  25. 12 0
      Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-check-common.cmake
  26. 42 0
      Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-common.cmake
  27. 0 0
      Tests/RunCMake/GenEx-TARGET_PROPERTY/empty.h
  28. 0 0
      Tests/RunCMake/GenEx-TARGET_PROPERTY/empty1.c
  29. 0 0
      Tests/RunCMake/GenEx-TARGET_PROPERTY/empty1.h
  30. 0 0
      Tests/RunCMake/GenEx-TARGET_PROPERTY/empty2.h
  31. 0 0
      Tests/RunCMake/GenEx-TARGET_PROPERTY/empty3.h
  32. 4 0
      Tests/RunCMake/GenEx-TARGET_PROPERTY/main.c

+ 13 - 4
Help/manual/cmake-generator-expressions.7.rst

@@ -1282,7 +1282,7 @@ Compile Context
   .. versionadded:: 3.27
 
   Content of ``...``, when collecting
-  :ref:`usage requirements <Target Usage Requirements>`,
+  :ref:`transitive build properties <Transitive Build Properties>`,
   otherwise it is the empty string.  This is intended for use in an
   :prop_tgt:`INTERFACE_LINK_LIBRARIES` and :prop_tgt:`LINK_LIBRARIES` target
   properties, typically populated via the :command:`target_link_libraries` command.
@@ -1670,8 +1670,8 @@ Link Context
 
   .. versionadded:: 3.1
 
-  Content of ``...``, except while collecting
-  :ref:`usage requirements <Target Usage Requirements>`,
+  Content of ``...``, except while collecting usage requirements from
+  :ref:`transitive build properties <Transitive Build Properties>`,
   in which case it is the empty string.  This is intended for use in an
   :prop_tgt:`INTERFACE_LINK_LIBRARIES` target property, typically populated
   via the :command:`target_link_libraries` command, to specify private link
@@ -1788,7 +1788,16 @@ The expressions have special evaluation rules for some properties:
   of the value on the target itself with the values of the same properties on
   targets named by the target's :prop_tgt:`INTERFACE_LINK_LIBRARIES`.
   Evaluation is transitive over the closure of the target's
-  :prop_tgt:`INTERFACE_LINK_LIBRARIES`.
+  :prop_tgt:`INTERFACE_LINK_LIBRARIES`:
+
+  * For :ref:`Transitive Build Properties`, the transitive closure
+    *excludes* entries of :prop_tgt:`INTERFACE_LINK_LIBRARIES` guarded
+    by the :genex:`LINK_ONLY` generator expression.
+
+  * For :ref:`Transitive Link Properties`, the transitive closure is
+    *includes* entries of :prop_tgt:`INTERFACE_LINK_LIBRARIES` guarded
+    by the :genex:`LINK_ONLY` generator expression.
+    See policy :policy:`CMP0166`.
 
   Evaluation of :prop_tgt:`INTERFACE_LINK_LIBRARIES` itself is not transitive.
 

+ 2 - 1
Help/manual/cmake-policies.7.rst

@@ -57,6 +57,7 @@ Policies Introduced by CMake 3.30
 .. toctree::
    :maxdepth: 1
 
+   CMP0166: TARGET_PROPERTY evaluates link properties transitively over private dependencies of static libraries. </policy/CMP0166>
    CMP0165: enable_language() must not be called before project(). </policy/CMP0165>
    CMP0164: add_library() rejects SHARED libraries when not supported by the platform. </policy/CMP0164>
    CMP0163: The GENERATED source file property is now visible in all directories. </policy/CMP0163>
@@ -214,7 +215,7 @@ Policies Introduced by CMake 3.17
    CMP0102: mark_as_advanced() does nothing if a cache entry does not exist. </policy/CMP0102>
    CMP0101: target_compile_options honors BEFORE keyword in all scopes. </policy/CMP0101>
    CMP0100: Let AUTOMOC and AUTOUIC process .hh header files. </policy/CMP0100>
-   CMP0099: Link properties are transitive over private dependency on static libraries. </policy/CMP0099>
+   CMP0099: Link properties are transitive over private dependencies of static libraries. </policy/CMP0099>
    CMP0098: FindFLEX runs flex in CMAKE_CURRENT_BINARY_DIR when executing. </policy/CMP0098>
 
 Policies Introduced by CMake 3.16

+ 14 - 5
Help/policy/CMP0099.rst

@@ -3,13 +3,16 @@ CMP0099
 
 .. versionadded:: 3.17
 
-Target link properties :prop_tgt:`INTERFACE_LINK_OPTIONS`,
-:prop_tgt:`INTERFACE_LINK_DIRECTORIES` and :prop_tgt:`INTERFACE_LINK_DEPENDS`
-are now transitive over private dependencies of static libraries.
+Link properties are transitive over private dependencies of static libraries.
 
-In CMake 3.16 and below the interface link properties attached to libraries
-are not propagated for private dependencies of static libraries.
+In CMake 3.16 and below, evaluation of target properties
+:prop_tgt:`INTERFACE_LINK_OPTIONS`, :prop_tgt:`INTERFACE_LINK_DIRECTORIES`,
+and :prop_tgt:`INTERFACE_LINK_DEPENDS` during buildsystem generation does not
+follow private dependencies of static libraries, which appear in their
+:prop_tgt:`INTERFACE_LINK_LIBRARIES` guarded by :genex:`LINK_ONLY` generator
+expressions.
 Only the libraries themselves are propagated to link the dependent binary.
+
 CMake 3.17 and later prefer to propagate all interface link properties.
 This policy provides compatibility for projects that have not been updated
 to expect the new behavior.
@@ -18,6 +21,12 @@ The ``OLD`` behavior for this policy is to not propagate interface link
 properties. The ``NEW`` behavior of this policy is to propagate interface link
 properties.
 
+.. versionadded:: 3.30
+
+  Policy :policy:`CMP0166` makes :genex:`TARGET_PROPERTY` evaluation of
+  these three transitive link properties follow private dependencies of
+  static libraries too.
+
 .. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.17
 .. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
 .. include:: STANDARD_ADVICE.txt

+ 40 - 0
Help/policy/CMP0166.rst

@@ -0,0 +1,40 @@
+CMP0166
+-------
+
+.. versionadded:: 3.30
+
+:genex:`TARGET_PROPERTY` evaluates link properties transitively over private
+dependencies of static libraries.
+
+In CMake 3.29 and below, the :genex:`TARGET_PROPERTY` generator expression
+evaluates properties :prop_tgt:`INTERFACE_LINK_OPTIONS`,
+:prop_tgt:`INTERFACE_LINK_DIRECTORIES`, and :prop_tgt:`INTERFACE_LINK_DEPENDS`
+as if they were :ref:`Transitive Build Properties` rather than
+:ref:`Transitive Link Properties`, even when policy :policy:`CMP0099` is
+set to ``NEW``.  Private dependencies of static libraries, which appear in
+their :prop_tgt:`INTERFACE_LINK_LIBRARIES` guarded by :genex:`LINK_ONLY`
+generator expressions, are not followed.  This is inconsistent with
+evaluation of the same target properties during buildsystem generation.
+
+CMake 3.30 and above prefer that :genex:`TARGET_PROPERTY` evaluates
+properties :prop_tgt:`INTERFACE_LINK_OPTIONS`,
+:prop_tgt:`INTERFACE_LINK_DIRECTORIES`, and :prop_tgt:`INTERFACE_LINK_DEPENDS`
+as :ref:`Transitive Link Properties` such that private dependencies of static
+libraries, which appear in their :prop_tgt:`INTERFACE_LINK_LIBRARIES` guarded
+by :genex:`LINK_ONLY` generator expressions, are followed.
+This policy provides compatibility for projects that have not been updated
+to expect the new behavior.
+
+The ``OLD`` behavior for this policy is for :genex:`TARGET_PROPERTY` to
+evaluate properties :prop_tgt:`INTERFACE_LINK_OPTIONS`,
+:prop_tgt:`INTERFACE_LINK_DIRECTORIES`, and :prop_tgt:`INTERFACE_LINK_DEPENDS`
+as if they were :ref:`Transitive Build Properties` by not following private
+dependencies of static libraries.  The ``NEW`` behavior for this policy is
+to evaluate them as :ref:`Transitive Link Properties` by following private
+dependencies of static libraries.
+
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.30
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
+.. include:: STANDARD_ADVICE.txt
+
+.. include:: DEPRECATED.txt

+ 8 - 0
Help/release/dev/genex-link-properties.rst

@@ -0,0 +1,8 @@
+genex-link-properties
+---------------------
+
+* The :genex:`TARGET_PROPERTY` generator expression now evaluates target
+  properties :prop_tgt:`INTERFACE_LINK_OPTIONS`,
+  :prop_tgt:`INTERFACE_LINK_DIRECTORIES`, and
+  :prop_tgt:`INTERFACE_LINK_DEPENDS` correctly by following private
+  dependencies of static libraries.  See policy :policy:`CMP0166`.

+ 2 - 1
Source/cmCommonTargetGenerator.cxx

@@ -501,7 +501,8 @@ std::string cmCommonTargetGenerator::GetLinkerLauncher(
   cmValue launcherProp = this->GeneratorTarget->GetProperty(propName);
   if (cmNonempty(launcherProp)) {
     cmGeneratorExpressionDAGChecker dagChecker(this->GeneratorTarget, propName,
-                                               nullptr, nullptr);
+                                               nullptr, nullptr,
+                                               this->LocalCommonGenerator);
     std::string evaluatedLinklauncher = cmGeneratorExpression::Evaluate(
       *launcherProp, this->LocalCommonGenerator, config, this->GeneratorTarget,
       &dagChecker, this->GeneratorTarget, lang);

+ 14 - 7
Source/cmComputeLinkDepends.cxx

@@ -451,10 +451,14 @@ cmComputeLinkDepends::cmComputeLinkDepends(const cmGeneratorTarget* target,
         if (cmValue feature = this->Target->GetProperty(key)) {
           if (!feature->empty() && key.length() > lloPrefix.length()) {
             auto item = key.substr(lloPrefix.length());
-            cmGeneratorExpressionDAGChecker dag{ this->Target->GetBacktrace(),
-                                                 this->Target,
-                                                 "LINK_LIBRARY_OVERRIDE",
-                                                 nullptr, nullptr };
+            cmGeneratorExpressionDAGChecker dag{
+              this->Target->GetBacktrace(),
+              this->Target,
+              "LINK_LIBRARY_OVERRIDE",
+              nullptr,
+              nullptr,
+              this->Target->GetLocalGenerator()
+            };
             auto overrideFeature = cmGeneratorExpression::Evaluate(
               *feature, this->Target->GetLocalGenerator(), config,
               this->Target, &dag, this->Target, linkLanguage);
@@ -466,9 +470,12 @@ cmComputeLinkDepends::cmComputeLinkDepends(const cmGeneratorTarget* target,
   // global override property
   if (cmValue linkLibraryOverride =
         this->Target->GetProperty("LINK_LIBRARY_OVERRIDE")) {
-    cmGeneratorExpressionDAGChecker dag{ target->GetBacktrace(), target,
-                                         "LINK_LIBRARY_OVERRIDE", nullptr,
-                                         nullptr };
+    cmGeneratorExpressionDAGChecker dag{ target->GetBacktrace(),
+                                         target,
+                                         "LINK_LIBRARY_OVERRIDE",
+                                         nullptr,
+                                         nullptr,
+                                         target->GetLocalGenerator() };
     auto overrideValue = cmGeneratorExpression::Evaluate(
       *linkLibraryOverride, target->GetLocalGenerator(), config, target, &dag,
       target, linkLanguage);

+ 9 - 9
Source/cmExportTryCompileFileGenerator.cxx

@@ -2,9 +2,11 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmExportTryCompileFileGenerator.h"
 
+#include <map>
 #include <utility>
 
 #include <cm/memory>
+#include <cm/string_view>
 
 #include "cmFileSet.h"
 #include "cmGeneratorExpression.h"
@@ -44,12 +46,10 @@ bool cmExportTryCompileFileGenerator::GenerateMainFile(std::ostream& os)
       ImportPropertyMap properties;
 
       for (std::string const& lang : this->Languages) {
-#define FIND_TARGETS(PROPERTY)                                                \
-  this->FindTargets("INTERFACE_" #PROPERTY, te, lang, emittedDeps);
-
-        CM_FOR_EACH_TRANSITIVE_PROPERTY_NAME(FIND_TARGETS)
-
-#undef FIND_TARGETS
+        for (auto i : cmGeneratorTarget::BuiltinTransitiveProperties) {
+          this->FindTargets(std::string(i.second.InterfaceName), te, lang,
+                            emittedDeps);
+        }
       }
 
       this->PopulateProperties(te, properties, emittedDeps);
@@ -76,10 +76,10 @@ std::string cmExportTryCompileFileGenerator::FindTargets(
     // To please constraint checks of DAGChecker, this property must have
     // LINK_OPTIONS property as parent
     parentDagChecker = cm::make_unique<cmGeneratorExpressionDAGChecker>(
-      tgt, "LINK_OPTIONS", nullptr, nullptr);
+      tgt, "LINK_OPTIONS", nullptr, nullptr, tgt->GetLocalGenerator());
   }
-  cmGeneratorExpressionDAGChecker dagChecker(tgt, propName, nullptr,
-                                             parentDagChecker.get());
+  cmGeneratorExpressionDAGChecker dagChecker(
+    tgt, propName, nullptr, parentDagChecker.get(), tgt->GetLocalGenerator());
 
   std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(*prop);
 

+ 1 - 1
Source/cmGeneratorExpression.cxx

@@ -425,7 +425,7 @@ const std::string& cmGeneratorExpressionInterpreter::Evaluate(
   cmGeneratorExpressionDAGChecker dagChecker(
     this->HeadTarget,
     property == "COMPILE_FLAGS" ? "COMPILE_OPTIONS" : property, nullptr,
-    nullptr);
+    nullptr, this->LocalGenerator);
 
   return this->CompiledGeneratorExpression->Evaluate(
     this->LocalGenerator, this->Config, this->HeadTarget, &dagChecker, nullptr,

+ 14 - 44
Source/cmGeneratorExpressionDAGChecker.cxx

@@ -2,10 +2,10 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmGeneratorExpressionDAGChecker.h"
 
-#include <cstring>
 #include <sstream>
 #include <utility>
 
+#include <cm/optional>
 #include <cm/string_view>
 #include <cmext/string_view>
 
@@ -20,16 +20,17 @@
 cmGeneratorExpressionDAGChecker::cmGeneratorExpressionDAGChecker(
   cmGeneratorTarget const* target, std::string property,
   const GeneratorExpressionContent* content,
-  cmGeneratorExpressionDAGChecker* parent)
+  cmGeneratorExpressionDAGChecker* parent, cmLocalGenerator const* contextLG)
   : cmGeneratorExpressionDAGChecker(cmListFileBacktrace(), target,
-                                    std::move(property), content, parent)
+                                    std::move(property), content, parent,
+                                    contextLG)
 {
 }
 
 cmGeneratorExpressionDAGChecker::cmGeneratorExpressionDAGChecker(
   cmListFileBacktrace backtrace, cmGeneratorTarget const* target,
   std::string property, const GeneratorExpressionContent* content,
-  cmGeneratorExpressionDAGChecker* parent)
+  cmGeneratorExpressionDAGChecker* parent, cmLocalGenerator const* contextLG)
   : Parent(parent)
   , Top(parent ? parent->Top : this)
   , Target(target)
@@ -40,10 +41,9 @@ cmGeneratorExpressionDAGChecker::cmGeneratorExpressionDAGChecker(
   if (parent) {
     this->TopIsTransitiveProperty = parent->TopIsTransitiveProperty;
   } else {
-#define TEST_TRANSITIVE_PROPERTY_METHOD(METHOD) this->METHOD() ||
-    this->TopIsTransitiveProperty = (CM_FOR_EACH_TRANSITIVE_PROPERTY_METHOD(
-      TEST_TRANSITIVE_PROPERTY_METHOD) false); // NOLINT(*)
-#undef TEST_TRANSITIVE_PROPERTY_METHOD
+    this->TopIsTransitiveProperty =
+      this->Target->IsTransitiveProperty(this->Property, contextLG)
+        .has_value();
   }
 
   this->CheckResult = this->CheckGraph();
@@ -169,6 +169,12 @@ bool cmGeneratorExpressionDAGChecker::EvaluatingCompileExpression() const
     property == "COMPILE_DEFINITIONS"_s || property == "COMPILE_OPTIONS"_s;
 }
 
+bool cmGeneratorExpressionDAGChecker::EvaluatingSources() const
+{
+  return this->Property == "SOURCES"_s ||
+    this->Property == "INTERFACE_SOURCES"_s;
+}
+
 bool cmGeneratorExpressionDAGChecker::EvaluatingLinkExpression() const
 {
   cm::string_view property(this->Top->Property);
@@ -222,39 +228,3 @@ cmGeneratorTarget const* cmGeneratorExpressionDAGChecker::TopTarget() const
 {
   return this->Top->Target;
 }
-
-enum class TransitiveProperty
-{
-#define DEFINE_ENUM_ENTRY(NAME) NAME,
-  CM_FOR_EACH_TRANSITIVE_PROPERTY_NAME(DEFINE_ENUM_ENTRY)
-#undef DEFINE_ENUM_ENTRY
-    Terminal
-};
-
-template <TransitiveProperty>
-bool additionalTest(const char* const /*unused*/)
-{
-  return false;
-}
-
-template <>
-bool additionalTest<TransitiveProperty::COMPILE_DEFINITIONS>(
-  const char* const prop)
-{
-  return cmHasLiteralPrefix(prop, "COMPILE_DEFINITIONS_");
-}
-
-#define DEFINE_TRANSITIVE_PROPERTY_METHOD(METHOD, PROPERTY)                   \
-  bool cmGeneratorExpressionDAGChecker::METHOD() const                        \
-  {                                                                           \
-    const char* const prop = this->Property.c_str();                          \
-    if (strcmp(prop, #PROPERTY) == 0 ||                                       \
-        strcmp(prop, "INTERFACE_" #PROPERTY) == 0) {                          \
-      return true;                                                            \
-    }                                                                         \
-    return additionalTest<TransitiveProperty::PROPERTY>(prop);                \
-  }
-
-CM_FOR_EACH_TRANSITIVE_PROPERTY(DEFINE_TRANSITIVE_PROPERTY_METHOD)
-
-#undef DEFINE_TRANSITIVE_PROPERTY_METHOD

+ 6 - 34
Source/cmGeneratorExpressionDAGChecker.h

@@ -13,33 +13,7 @@
 struct GeneratorExpressionContent;
 struct cmGeneratorExpressionContext;
 class cmGeneratorTarget;
-
-#define CM_SELECT_BOTH(F, A1, A2) F(A1, A2)
-#define CM_SELECT_FIRST(F, A1, A2) F(A1)
-#define CM_SELECT_SECOND(F, A1, A2) F(A2)
-
-#define CM_FOR_EACH_TRANSITIVE_PROPERTY_IMPL(F, SELECT)                       \
-  SELECT(F, EvaluatingIncludeDirectories, INCLUDE_DIRECTORIES)                \
-  SELECT(F, EvaluatingSystemIncludeDirectories, SYSTEM_INCLUDE_DIRECTORIES)   \
-  SELECT(F, EvaluatingCompileDefinitions, COMPILE_DEFINITIONS)                \
-  SELECT(F, EvaluatingCompileOptions, COMPILE_OPTIONS)                        \
-  SELECT(F, EvaluatingAutoMocMacroNames, AUTOMOC_MACRO_NAMES)                 \
-  SELECT(F, EvaluatingAutoUicOptions, AUTOUIC_OPTIONS)                        \
-  SELECT(F, EvaluatingSources, SOURCES)                                       \
-  SELECT(F, EvaluatingCompileFeatures, COMPILE_FEATURES)                      \
-  SELECT(F, EvaluatingLinkOptions, LINK_OPTIONS)                              \
-  SELECT(F, EvaluatingLinkDirectories, LINK_DIRECTORIES)                      \
-  SELECT(F, EvaluatingLinkDepends, LINK_DEPENDS)                              \
-  SELECT(F, EvaluatingPrecompileHeaders, PRECOMPILE_HEADERS)
-
-#define CM_FOR_EACH_TRANSITIVE_PROPERTY(F)                                    \
-  CM_FOR_EACH_TRANSITIVE_PROPERTY_IMPL(F, CM_SELECT_BOTH)
-
-#define CM_FOR_EACH_TRANSITIVE_PROPERTY_METHOD(F)                             \
-  CM_FOR_EACH_TRANSITIVE_PROPERTY_IMPL(F, CM_SELECT_FIRST)
-
-#define CM_FOR_EACH_TRANSITIVE_PROPERTY_NAME(F)                               \
-  CM_FOR_EACH_TRANSITIVE_PROPERTY_IMPL(F, CM_SELECT_SECOND)
+class cmLocalGenerator;
 
 struct cmGeneratorExpressionDAGChecker
 {
@@ -47,11 +21,13 @@ struct cmGeneratorExpressionDAGChecker
                                   cmGeneratorTarget const* target,
                                   std::string property,
                                   const GeneratorExpressionContent* content,
-                                  cmGeneratorExpressionDAGChecker* parent);
+                                  cmGeneratorExpressionDAGChecker* parent,
+                                  cmLocalGenerator const* contextLG);
   cmGeneratorExpressionDAGChecker(cmGeneratorTarget const* target,
                                   std::string property,
                                   const GeneratorExpressionContent* content,
-                                  cmGeneratorExpressionDAGChecker* parent);
+                                  cmGeneratorExpressionDAGChecker* parent,
+                                  cmLocalGenerator const* contextLG);
 
   enum Result
   {
@@ -83,11 +59,7 @@ struct cmGeneratorExpressionDAGChecker
   bool EvaluatingLinkLibraries(cmGeneratorTarget const* tgt = nullptr,
                                ForGenex genex = ForGenex::ANY) const;
 
-#define DECLARE_TRANSITIVE_PROPERTY_METHOD(METHOD) bool METHOD() const;
-
-  CM_FOR_EACH_TRANSITIVE_PROPERTY_METHOD(DECLARE_TRANSITIVE_PROPERTY_METHOD)
-
-#undef DECLARE_TRANSITIVE_PROPERTY_METHOD
+  bool EvaluatingSources() const;
 
   bool GetTransitivePropertiesOnly() const;
   void SetTransitivePropertiesOnly() { this->TransitivePropertiesOnly = true; }

+ 19 - 29
Source/cmGeneratorExpressionNode.cxx

@@ -487,7 +487,8 @@ protected:
     if (context->HeadTarget) {
       cmGeneratorExpressionDAGChecker dagChecker(
         context->Backtrace, context->HeadTarget,
-        genexOperator + ":" + expression, content, dagCheckerParent);
+        genexOperator + ":" + expression, content, dagCheckerParent,
+        context->LG);
       switch (dagChecker.Check()) {
         case cmGeneratorExpressionDAGChecker::SELF_REFERENCE:
         case cmGeneratorExpressionDAGChecker::CYCLIC_REFERENCE: {
@@ -2700,7 +2701,8 @@ static const struct DeviceLinkNode : public cmGeneratorExpressionNode
 static std::string getLinkedTargetsContent(
   cmGeneratorTarget const* target, std::string const& prop,
   cmGeneratorExpressionContext* context,
-  cmGeneratorExpressionDAGChecker* dagChecker)
+  cmGeneratorExpressionDAGChecker* dagChecker,
+  cmGeneratorTarget::LinkInterfaceFor interfaceFor)
 {
   std::string result;
   if (cmLinkImplementationLibraries const* impl =
@@ -2716,8 +2718,7 @@ static std::string getLinkedTargetsContent(
           target, context->EvaluateForBuildsystem, lib.Backtrace,
           context->Language);
         std::string libResult = lib.Target->EvaluateInterfaceProperty(
-          prop, &libContext, dagChecker,
-          cmGeneratorTarget::LinkInterfaceFor::Usage);
+          prop, &libContext, dagChecker, interfaceFor);
         if (!libResult.empty()) {
           if (result.empty()) {
             result = std::move(libResult);
@@ -2875,25 +2876,15 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode
 
     std::string interfacePropertyName;
     bool isInterfaceProperty = false;
+    cmGeneratorTarget::LinkInterfaceFor interfaceFor =
+      cmGeneratorTarget::LinkInterfaceFor::Usage;
 
-#define POPULATE_INTERFACE_PROPERTY_NAME(prop)                                \
-  if (propertyName == #prop) {                                                \
-    interfacePropertyName = "INTERFACE_" #prop;                               \
-  } else if (propertyName == "INTERFACE_" #prop) {                            \
-    interfacePropertyName = "INTERFACE_" #prop;                               \
-    isInterfaceProperty = true;                                               \
-  } else
-
-    CM_FOR_EACH_TRANSITIVE_PROPERTY_NAME(POPULATE_INTERFACE_PROPERTY_NAME)
-    // Note that the above macro terminates with an else
-    /* else */ if (cmHasLiteralPrefix(propertyName, "COMPILE_DEFINITIONS_")) {
-      cmPolicies::PolicyStatus polSt =
-        context->LG->GetPolicyStatus(cmPolicies::CMP0043);
-      if (polSt == cmPolicies::WARN || polSt == cmPolicies::OLD) {
-        interfacePropertyName = "INTERFACE_COMPILE_DEFINITIONS";
-      }
+    if (cm::optional<cmGeneratorTarget::TransitiveProperty> transitiveProp =
+          target->IsTransitiveProperty(propertyName, context->LG)) {
+      interfacePropertyName = std::string(transitiveProp->InterfaceName);
+      isInterfaceProperty = transitiveProp->InterfaceName == propertyName;
+      interfaceFor = transitiveProp->InterfaceFor;
     }
-#undef POPULATE_INTERFACE_PROPERTY_NAME
 
     bool evaluatingLinkLibraries = false;
 
@@ -2916,20 +2907,19 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode
           return std::string();
         }
       } else {
-        assert(dagCheckerParent
-                 ->EvaluatingTransitiveProperty()); // NOLINT(clang-tidy)
+        assert(dagCheckerParent->EvaluatingTransitiveProperty());
       }
     }
 
     if (isInterfaceProperty) {
       return cmGeneratorExpression::StripEmptyListElements(
-        target->EvaluateInterfaceProperty(
-          propertyName, context, dagCheckerParent,
-          cmGeneratorTarget::LinkInterfaceFor::Usage));
+        target->EvaluateInterfaceProperty(propertyName, context,
+                                          dagCheckerParent, interfaceFor));
     }
 
-    cmGeneratorExpressionDAGChecker dagChecker(
-      context->Backtrace, target, propertyName, content, dagCheckerParent);
+    cmGeneratorExpressionDAGChecker dagChecker(context->Backtrace, target,
+                                               propertyName, content,
+                                               dagCheckerParent, context->LG);
 
     switch (dagChecker.Check()) {
       case cmGeneratorExpressionDAGChecker::SELF_REFERENCE:
@@ -3011,7 +3001,7 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode
         this->EvaluateDependentExpression(result, context->LG, context, target,
                                           &dagChecker, target));
       std::string linkedTargetsContent = getLinkedTargetsContent(
-        target, interfacePropertyName, context, &dagChecker);
+        target, interfacePropertyName, context, &dagChecker, interfaceFor);
       if (!linkedTargetsContent.empty()) {
         result += (result.empty() ? "" : ";") + linkedTargetsContent;
       }

+ 92 - 25
Source/cmGeneratorTarget.cxx

@@ -65,6 +65,7 @@
 
 namespace {
 using LinkInterfaceFor = cmGeneratorTarget::LinkInterfaceFor;
+using TransitiveProperty = cmGeneratorTarget::TransitiveProperty;
 
 const std::string kINTERFACE_LINK_LIBRARIES = "INTERFACE_LINK_LIBRARIES";
 const std::string kINTERFACE_LINK_LIBRARIES_DIRECT =
@@ -73,6 +74,33 @@ const std::string kINTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE =
   "INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE";
 }
 
+const std::map<cm::string_view, TransitiveProperty>
+  cmGeneratorTarget::BuiltinTransitiveProperties = {
+    { "AUTOMOC_MACRO_NAMES"_s,
+      { "INTERFACE_AUTOMOC_MACRO_NAMES"_s, LinkInterfaceFor::Usage } },
+    { "AUTOUIC_OPTIONS"_s,
+      { "INTERFACE_AUTOUIC_OPTIONS"_s, LinkInterfaceFor::Usage } },
+    { "COMPILE_DEFINITIONS"_s,
+      { "INTERFACE_COMPILE_DEFINITIONS"_s, LinkInterfaceFor::Usage } },
+    { "COMPILE_FEATURES"_s,
+      { "INTERFACE_COMPILE_FEATURES"_s, LinkInterfaceFor::Usage } },
+    { "COMPILE_OPTIONS"_s,
+      { "INTERFACE_COMPILE_OPTIONS"_s, LinkInterfaceFor::Usage } },
+    { "INCLUDE_DIRECTORIES"_s,
+      { "INTERFACE_INCLUDE_DIRECTORIES"_s, LinkInterfaceFor::Usage } },
+    { "LINK_DEPENDS"_s,
+      { "INTERFACE_LINK_DEPENDS"_s, LinkInterfaceFor::Link } },
+    { "LINK_DIRECTORIES"_s,
+      { "INTERFACE_LINK_DIRECTORIES"_s, LinkInterfaceFor::Link } },
+    { "LINK_OPTIONS"_s,
+      { "INTERFACE_LINK_OPTIONS"_s, LinkInterfaceFor::Link } },
+    { "PRECOMPILE_HEADERS"_s,
+      { "INTERFACE_PRECOMPILE_HEADERS"_s, LinkInterfaceFor::Usage } },
+    { "SOURCES"_s, { "INTERFACE_SOURCES"_s, LinkInterfaceFor::Usage } },
+    { "SYSTEM_INCLUDE_DIRECTORIES"_s,
+      { "INTERFACE_SYSTEM_INCLUDE_DIRECTORIES"_s, LinkInterfaceFor::Usage } },
+  };
+
 template <>
 cmValue cmTargetPropertyComputer::GetSources<cmGeneratorTarget>(
   cmGeneratorTarget const* tgt, cmMakefile const& /* mf */)
@@ -887,7 +915,7 @@ std::string cmGeneratorTarget::GetLinkerTypeProperty(
   auto linkerType = this->GetProperty(propName);
   if (!linkerType.IsEmpty()) {
     cmGeneratorExpressionDAGChecker dagChecker(this, propName, nullptr,
-                                               nullptr);
+                                               nullptr, this->LocalGenerator);
     auto ltype =
       cmGeneratorExpression::Evaluate(*linkerType, this->GetLocalGenerator(),
                                       config, this, &dagChecker, this, lang);
@@ -1352,7 +1380,8 @@ bool cmGeneratorTarget::IsSystemIncludeDirectory(
 
   if (iter == this->SystemIncludesCache.end()) {
     cmGeneratorExpressionDAGChecker dagChecker(
-      this, "SYSTEM_INCLUDE_DIRECTORIES", nullptr, nullptr);
+      this, "SYSTEM_INCLUDE_DIRECTORIES", nullptr, nullptr,
+      this->LocalGenerator);
 
     bool excludeImported = this->GetPropertyAsBool("NO_SYSTEM_FROM_IMPORTED");
 
@@ -1460,7 +1489,8 @@ std::string cmGeneratorTarget::EvaluateInterfaceProperty(
   // a subset of TargetPropertyNode::Evaluate without stringify/parse steps
   // but sufficient for transitive interface properties.
   cmGeneratorExpressionDAGChecker dagChecker(context->Backtrace, this, prop,
-                                             nullptr, dagCheckerParent);
+                                             nullptr, dagCheckerParent,
+                                             this->LocalGenerator);
   switch (dagChecker.Check()) {
     case cmGeneratorExpressionDAGChecker::SELF_REFERENCE:
       dagChecker.ReportError(
@@ -1525,6 +1555,38 @@ std::string cmGeneratorTarget::EvaluateInterfaceProperty(
   return result;
 }
 
+cm::optional<cmGeneratorTarget::TransitiveProperty>
+cmGeneratorTarget::IsTransitiveProperty(cm::string_view prop,
+                                        cmLocalGenerator const* lg) const
+{
+  cm::optional<TransitiveProperty> result;
+  static const cm::string_view kINTERFACE_ = "INTERFACE_"_s;
+  if (cmHasPrefix(prop, kINTERFACE_)) {
+    prop = prop.substr(kINTERFACE_.length());
+  }
+  auto i = BuiltinTransitiveProperties.find(prop);
+  if (i != BuiltinTransitiveProperties.end()) {
+    result = i->second;
+    if (result->InterfaceFor != cmGeneratorTarget::LinkInterfaceFor::Usage) {
+      cmPolicies::PolicyStatus cmp0166 =
+        lg->GetPolicyStatus(cmPolicies::CMP0166);
+      if ((cmp0166 == cmPolicies::WARN || cmp0166 == cmPolicies::OLD) &&
+          (prop == "LINK_DIRECTORIES"_s || prop == "LINK_DEPENDS"_s ||
+           prop == "LINK_OPTIONS"_s)) {
+        result->InterfaceFor = cmGeneratorTarget::LinkInterfaceFor::Usage;
+      }
+    }
+  } else if (cmHasLiteralPrefix(prop, "COMPILE_DEFINITIONS_")) {
+    cmPolicies::PolicyStatus cmp0043 =
+      lg->GetPolicyStatus(cmPolicies::CMP0043);
+    if (cmp0043 == cmPolicies::WARN || cmp0043 == cmPolicies::OLD) {
+      result = TransitiveProperty{ "INTERFACE_COMPILE_DEFINITIONS"_s,
+                                   LinkInterfaceFor::Usage };
+    }
+  }
+  return result;
+}
+
 namespace {
 
 enum class IncludeDirectoryFallBack
@@ -1539,8 +1601,10 @@ std::string AddLangSpecificInterfaceIncludeDirectories(
   const std::string& propertyName, IncludeDirectoryFallBack mode,
   cmGeneratorExpressionDAGChecker* context)
 {
-  cmGeneratorExpressionDAGChecker dag{ target->GetBacktrace(), target,
-                                       propertyName, nullptr, context };
+  cmGeneratorExpressionDAGChecker dag{
+    target->GetBacktrace(),     target, propertyName, nullptr, context,
+    target->GetLocalGenerator()
+  };
   switch (dag.Check()) {
     case cmGeneratorExpressionDAGChecker::SELF_REFERENCE:
       dag.ReportError(
@@ -1590,8 +1654,10 @@ void AddLangSpecificImplicitIncludeDirectories(
 {
   if (const auto* libraries = target->GetLinkImplementationLibraries(
         config, LinkInterfaceFor::Usage)) {
-    cmGeneratorExpressionDAGChecker dag{ target->GetBacktrace(), target,
-                                         propertyName, nullptr, nullptr };
+    cmGeneratorExpressionDAGChecker dag{
+      target->GetBacktrace(),     target, propertyName, nullptr, nullptr,
+      target->GetLocalGenerator()
+    };
 
     for (const cmLinkImplItem& library : libraries->Libraries) {
       if (const cmGeneratorTarget* dependency = library.Target) {
@@ -1843,8 +1909,8 @@ std::vector<BT<std::string>> cmGeneratorTarget::GetSourceFilePaths(
     this->DebugSourcesDone = true;
   }
 
-  cmGeneratorExpressionDAGChecker dagChecker(this, "SOURCES", nullptr,
-                                             nullptr);
+  cmGeneratorExpressionDAGChecker dagChecker(this, "SOURCES", nullptr, nullptr,
+                                             this->LocalGenerator);
 
   EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries(
     this, config, std::string(), &dagChecker, this->SourceEntries);
@@ -3058,7 +3124,7 @@ void cmGeneratorTarget::GetAutoUicOptions(std::vector<std::string>& result,
   }
 
   cmGeneratorExpressionDAGChecker dagChecker(this, "AUTOUIC_OPTIONS", nullptr,
-                                             nullptr);
+                                             nullptr, this->LocalGenerator);
   cmExpandList(cmGeneratorExpression::Evaluate(prop, this->LocalGenerator,
                                                config, this, &dagChecker),
                result);
@@ -3858,8 +3924,8 @@ std::vector<BT<std::string>> cmGeneratorTarget::GetIncludeDirectories(
   std::vector<BT<std::string>> includes;
   std::unordered_set<std::string> uniqueIncludes;
 
-  cmGeneratorExpressionDAGChecker dagChecker(this, "INCLUDE_DIRECTORIES",
-                                             nullptr, nullptr);
+  cmGeneratorExpressionDAGChecker dagChecker(
+    this, "INCLUDE_DIRECTORIES", nullptr, nullptr, this->LocalGenerator);
 
   cmList debugProperties{ this->Makefile->GetDefinition(
     "CMAKE_DEBUG_TARGET_PROPERTIES") };
@@ -4123,7 +4189,7 @@ std::vector<BT<std::string>> cmGeneratorTarget::GetCompileOptions(
   std::unordered_set<std::string> uniqueOptions;
 
   cmGeneratorExpressionDAGChecker dagChecker(this, "COMPILE_OPTIONS", nullptr,
-                                             nullptr);
+                                             nullptr, this->LocalGenerator);
 
   cmList debugProperties{ this->Makefile->GetDefinition(
     "CMAKE_DEBUG_TARGET_PROPERTIES") };
@@ -4164,7 +4230,7 @@ std::vector<BT<std::string>> cmGeneratorTarget::GetCompileFeatures(
   std::unordered_set<std::string> uniqueFeatures;
 
   cmGeneratorExpressionDAGChecker dagChecker(this, "COMPILE_FEATURES", nullptr,
-                                             nullptr);
+                                             nullptr, this->LocalGenerator);
 
   cmList debugProperties{ this->Makefile->GetDefinition(
     "CMAKE_DEBUG_TARGET_PROPERTIES") };
@@ -4213,8 +4279,8 @@ std::vector<BT<std::string>> cmGeneratorTarget::GetCompileDefinitions(
   std::vector<BT<std::string>> list;
   std::unordered_set<std::string> uniqueOptions;
 
-  cmGeneratorExpressionDAGChecker dagChecker(this, "COMPILE_DEFINITIONS",
-                                             nullptr, nullptr);
+  cmGeneratorExpressionDAGChecker dagChecker(
+    this, "COMPILE_DEFINITIONS", nullptr, nullptr, this->LocalGenerator);
 
   cmList debugProperties{ this->Makefile->GetDefinition(
     "CMAKE_DEBUG_TARGET_PROPERTIES") };
@@ -4277,8 +4343,8 @@ std::vector<BT<std::string>> cmGeneratorTarget::GetPrecompileHeaders(
   }
   std::unordered_set<std::string> uniqueOptions;
 
-  cmGeneratorExpressionDAGChecker dagChecker(this, "PRECOMPILE_HEADERS",
-                                             nullptr, nullptr);
+  cmGeneratorExpressionDAGChecker dagChecker(
+    this, "PRECOMPILE_HEADERS", nullptr, nullptr, this->LocalGenerator);
 
   cmList debugProperties{ this->Makefile->GetDefinition(
     "CMAKE_DEBUG_TARGET_PROPERTIES") };
@@ -4667,7 +4733,7 @@ std::vector<BT<std::string>> cmGeneratorTarget::GetLinkOptions(
   std::unordered_set<std::string> uniqueOptions;
 
   cmGeneratorExpressionDAGChecker dagChecker(this, "LINK_OPTIONS", nullptr,
-                                             nullptr);
+                                             nullptr, this->LocalGenerator);
 
   cmList debugProperties{ this->Makefile->GetDefinition(
     "CMAKE_DEBUG_TARGET_PROPERTIES") };
@@ -4835,8 +4901,8 @@ std::vector<BT<std::string>> cmGeneratorTarget::GetStaticLibraryLinkOptions(
   std::vector<BT<std::string>> result;
   std::unordered_set<std::string> uniqueOptions;
 
-  cmGeneratorExpressionDAGChecker dagChecker(this, "STATIC_LIBRARY_OPTIONS",
-                                             nullptr, nullptr);
+  cmGeneratorExpressionDAGChecker dagChecker(
+    this, "STATIC_LIBRARY_OPTIONS", nullptr, nullptr, this->LocalGenerator);
 
   EvaluatedTargetPropertyEntries entries;
   if (cmValue linkOptions = this->GetProperty("STATIC_LIBRARY_OPTIONS")) {
@@ -4949,7 +5015,7 @@ std::vector<BT<std::string>> cmGeneratorTarget::GetLinkDirectories(
   std::unordered_set<std::string> uniqueDirectories;
 
   cmGeneratorExpressionDAGChecker dagChecker(this, "LINK_DIRECTORIES", nullptr,
-                                             nullptr);
+                                             nullptr, this->LocalGenerator);
 
   cmList debugProperties{ this->Makefile->GetDefinition(
     "CMAKE_DEBUG_TARGET_PROPERTIES") };
@@ -4993,7 +5059,7 @@ std::vector<BT<std::string>> cmGeneratorTarget::GetLinkDepends(
   std::vector<BT<std::string>> result;
   std::unordered_set<std::string> uniqueOptions;
   cmGeneratorExpressionDAGChecker dagChecker(this, "LINK_DEPENDS", nullptr,
-                                             nullptr);
+                                             nullptr, this->LocalGenerator);
 
   EvaluatedTargetPropertyEntries entries;
   if (cmValue linkDepends = this->GetProperty("LINK_DEPENDS")) {
@@ -6979,7 +7045,8 @@ void cmGeneratorTarget::ExpandLinkItems(
     return;
   }
   // Keep this logic in sync with ComputeLinkImplementationLibraries.
-  cmGeneratorExpressionDAGChecker dagChecker(this, prop, nullptr, nullptr);
+  cmGeneratorExpressionDAGChecker dagChecker(this, prop, nullptr, nullptr,
+                                             this->LocalGenerator);
   // The $<LINK_ONLY> expression may be in a link interface to specify
   // private link dependencies that are otherwise excluded from usage
   // requirements.
@@ -8654,7 +8721,7 @@ void cmGeneratorTarget::ComputeLinkImplementationLibraries(
   for (auto const& entry : entryRange) {
     // Keep this logic in sync with ExpandLinkItems.
     cmGeneratorExpressionDAGChecker dagChecker(this, "LINK_LIBRARIES", nullptr,
-                                               nullptr);
+                                               nullptr, this->LocalGenerator);
     // The $<LINK_ONLY> expression may be used to specify link dependencies
     // that are otherwise excluded from usage requirements.
     if (implFor == LinkInterfaceFor::Usage) {

+ 13 - 0
Source/cmGeneratorTarget.h

@@ -15,6 +15,7 @@
 #include <vector>
 
 #include <cm/optional>
+#include <cm/string_view>
 
 #include "cmAlgorithms.h"
 #include "cmComputeLinkInformation.h"
@@ -886,6 +887,18 @@ public:
     cmGeneratorExpressionDAGChecker* dagCheckerParent,
     LinkInterfaceFor interfaceFor) const;
 
+  struct TransitiveProperty
+  {
+    cm::string_view InterfaceName;
+    LinkInterfaceFor InterfaceFor;
+  };
+
+  static const std::map<cm::string_view, TransitiveProperty>
+    BuiltinTransitiveProperties;
+
+  cm::optional<TransitiveProperty> IsTransitiveProperty(
+    cm::string_view prop, cmLocalGenerator const* lg) const;
+
   bool HaveInstallTreeRPATH(const std::string& config) const;
 
   bool GetBuildRPATH(const std::string& config, std::string& rpath) const;

+ 7 - 3
Source/cmPolicies.h

@@ -294,8 +294,8 @@ class cmMakefile;
          "FindFLEX runs flex in CMAKE_CURRENT_BINARY_DIR when executing.", 3, \
          17, 0, cmPolicies::WARN)                                             \
   SELECT(POLICY, CMP0099,                                                     \
-         "Link properties are transitive over private dependency on static "  \
-         "libraries.",                                                        \
+         "Link properties are transitive over private dependencies of "       \
+         "static libraries.",                                                 \
          3, 17, 0, cmPolicies::WARN)                                          \
   SELECT(POLICY, CMP0100, "Let AUTOMOC and AUTOUIC process .hh files.", 3,    \
          17, 0, cmPolicies::WARN)                                             \
@@ -508,7 +508,11 @@ class cmMakefile;
          3, 30, 0, cmPolicies::WARN)                                          \
   SELECT(POLICY, CMP0165,                                                     \
          "enable_language() must not be called before project().", 3, 30, 0,  \
-         cmPolicies::WARN)
+         cmPolicies::WARN)                                                    \
+  SELECT(POLICY, CMP0166,                                                     \
+         "TARGET_PROPERTY evaluates link properties transitively over "       \
+         "private dependencies of static libraries.",                         \
+         3, 30, 0, cmPolicies::WARN)
 
 #define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1)
 #define CM_FOR_EACH_POLICY_ID(POLICY)                                         \

+ 3 - 2
Source/cmQtAutoGenInitializer.cxx

@@ -1918,8 +1918,9 @@ bool cmQtAutoGenInitializer::SetupWriteAutogenInfo()
     info.SetBool("MOC_RELAXED_MODE", this->Moc.RelaxedMode);
     info.SetBool("MOC_PATH_PREFIX", this->Moc.PathPrefix);
 
-    cmGeneratorExpressionDAGChecker dagChecker(
-      this->GenTarget, "AUTOMOC_MACRO_NAMES", nullptr, nullptr);
+    cmGeneratorExpressionDAGChecker dagChecker(this->GenTarget,
+                                               "AUTOMOC_MACRO_NAMES", nullptr,
+                                               nullptr, this->LocalGen);
     EvaluatedTargetPropertyEntries InterfaceAutoMocMacroNamesEntries;
 
     if (this->MultiConfig) {

+ 2 - 2
Tests/RunCMake/GenEx-TARGET_PROPERTY/CMakeLists.txt

@@ -1,6 +1,6 @@
-cmake_minimum_required(VERSION 3.10)
+cmake_minimum_required(VERSION 3.17)
 if(RunCMake_TEST STREQUAL "LOCATION")
   cmake_minimum_required(VERSION 2.8.12) # Leave CMP0026 unset.
 endif()
 project(${RunCMake_TEST} NONE)
-include(${RunCMake_TEST}.cmake)
+include(${RunCMake_TEST}.cmake NO_POLICY_SCOPE)

+ 3 - 0
Tests/RunCMake/GenEx-TARGET_PROPERTY/RunCMakeTest.cmake

@@ -13,6 +13,9 @@ run_cmake(LinkImplementationCycle5)
 run_cmake(LinkImplementationCycle6)
 run_cmake(LOCATION)
 run_cmake(SOURCES)
+run_cmake(TransitiveBuild)
+run_cmake(TransitiveLink-CMP0166-OLD)
+run_cmake(TransitiveLink-CMP0166-NEW)
 
 block()
   run_cmake(Scope)

+ 25 - 0
Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveBuild-check.cmake

@@ -0,0 +1,25 @@
+set(expect [[
+# file\(GENERATE\) produced:
+main INCLUDE_DIRECTORIES: '[^';]*/Tests/RunCMake/GenEx-TARGET_PROPERTY/dirM;[^';]*/Tests/RunCMake/GenEx-TARGET_PROPERTY/dir1'
+main SYSTEM_INCLUDE_DIRECTORIES: '[^';]*/Tests/RunCMake/GenEx-TARGET_PROPERTY/sys1'
+main COMPILE_DEFINITIONS: 'DEFM;DEF1'
+main COMPILE_FEATURES: 'cxx_std_20;cxx_std_11'
+main COMPILE_OPTIONS: '-optM;-opt1'
+main PRECOMPILE_HEADERS: '[^';]*/Tests/RunCMake/GenEx-TARGET_PROPERTY/empty.h;[^';]*/Tests/RunCMake/GenEx-TARGET_PROPERTY/empty1.h'
+main SOURCES: 'main.c;[^';]*/Tests/RunCMake/GenEx-TARGET_PROPERTY/empty1.c'
+main AUTOMOC_MACRO_NAMES: 'MOCM;MOC1'
+main AUTOUIC_OPTIONS: '-uicM;-uic1'
+]])
+
+string(REGEX REPLACE "\r\n" "\n" expect "${expect}")
+string(REGEX REPLACE "\n+$" "" expect "${expect}")
+
+file(READ "${RunCMake_TEST_BINARY_DIR}/out.txt" actual)
+string(REGEX REPLACE "\r\n" "\n" actual "${actual}")
+string(REGEX REPLACE "\n+$" "" actual "${actual}")
+
+if(NOT actual MATCHES "^${expect}$")
+  string(REPLACE "\n" "\n expect> " expect " expect> ${expect}")
+  string(REPLACE "\n" "\n actual> " actual " actual> ${actual}")
+  message(FATAL_ERROR "Expected file(GENERATE) output:\n${expect}\ndoes not match actual output:\n${actual}")
+endif()

+ 66 - 0
Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveBuild.cmake

@@ -0,0 +1,66 @@
+enable_language(C)
+set(CMAKE_PCH_EXTENSION "") # suppress cmake_pch from SOURCES
+
+add_library(foo1 STATIC empty.c)
+target_link_libraries(foo1 PRIVATE foo2 foo3)
+target_include_directories(foo1 INTERFACE dir1)
+target_compile_definitions(foo1 INTERFACE DEF1)
+target_compile_features(foo1 INTERFACE cxx_std_11)
+target_compile_options(foo1 INTERFACE -opt1)
+target_precompile_headers(foo1 INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/empty1.h")
+target_sources(foo1 INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/empty1.c")
+set_target_properties(foo1 PROPERTIES
+  INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/sys1"
+  INTERFACE_AUTOMOC_MACRO_NAMES "MOC1"
+  INTERFACE_AUTOUIC_OPTIONS "-uic1"
+  )
+
+add_library(foo2 STATIC empty.c)
+target_include_directories(foo2 INTERFACE dir2)
+target_compile_definitions(foo2 INTERFACE DEF2)
+target_compile_features(foo2 INTERFACE cxx_std_14)
+target_compile_options(foo2 INTERFACE -opt2)
+target_precompile_headers(foo2 INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/empty2.h")
+target_sources(foo2 INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/empty2.c")
+set_target_properties(foo2 PROPERTIES
+  INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/sys2"
+  INTERFACE_AUTOMOC_MACRO_NAMES "MOC2"
+  INTERFACE_AUTOUIC_OPTIONS "-uic2"
+  )
+
+add_library(foo3 STATIC empty.c)
+target_include_directories(foo3 PRIVATE dir3)
+target_compile_definitions(foo3 PRIVATE DEF3)
+target_compile_features(foo3 PRIVATE cxx_std_17)
+target_compile_options(foo3 PRIVATE -opt3)
+target_precompile_headers(foo3 PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/empty3.h")
+target_sources(foo3 PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/empty3.c")
+set_target_properties(foo3 PROPERTIES
+  SYSTEM_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/sys3"
+  AUTOMOC_MACRO_NAMES "MOC3"
+  AUTOUIC_OPTIONS "-uic3"
+  )
+
+add_executable(main main.c)
+target_link_libraries(main PRIVATE foo1)
+target_include_directories(main PRIVATE dirM)
+target_compile_definitions(main PRIVATE DEFM)
+target_compile_features(main PRIVATE cxx_std_20)
+target_compile_options(main PRIVATE -optM)
+target_precompile_headers(main PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/empty.h")
+set_target_properties(main PROPERTIES
+  AUTOMOC_MACRO_NAMES "MOCM"
+  AUTOUIC_OPTIONS "-uicM"
+  )
+
+file(GENERATE OUTPUT out.txt CONTENT "# file(GENERATE) produced:
+main INCLUDE_DIRECTORIES: '$<TARGET_PROPERTY:main,INCLUDE_DIRECTORIES>'
+main SYSTEM_INCLUDE_DIRECTORIES: '$<TARGET_PROPERTY:main,SYSTEM_INCLUDE_DIRECTORIES>'
+main COMPILE_DEFINITIONS: '$<TARGET_PROPERTY:main,COMPILE_DEFINITIONS>'
+main COMPILE_FEATURES: '$<TARGET_PROPERTY:main,COMPILE_FEATURES>'
+main COMPILE_OPTIONS: '$<TARGET_PROPERTY:main,COMPILE_OPTIONS>'
+main PRECOMPILE_HEADERS: '$<TARGET_PROPERTY:main,PRECOMPILE_HEADERS>'
+main SOURCES: '$<TARGET_PROPERTY:main,SOURCES>'
+main AUTOMOC_MACRO_NAMES: '$<TARGET_PROPERTY:main,AUTOMOC_MACRO_NAMES>'
+main AUTOUIC_OPTIONS: '$<TARGET_PROPERTY:main,AUTOUIC_OPTIONS>'
+")

+ 8 - 0
Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-CMP0166-NEW-check.cmake

@@ -0,0 +1,8 @@
+set(expect [[
+# file\(GENERATE\) produced:
+main LINK_LIBRARIES: 'foo1' # not transitive
+main LINK_DIRECTORIES: '[^';]*/Tests/RunCMake/GenEx-TARGET_PROPERTY/dirM;[^';]*/Tests/RunCMake/GenEx-TARGET_PROPERTY/dir1;[^';]*/Tests/RunCMake/GenEx-TARGET_PROPERTY/dir2'
+main LINK_OPTIONS: '-optM;-opt1;-opt2'
+main LINK_DEPENDS: '[^';]*/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-CMP0166-NEW-build/depM;[^';]*/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-CMP0166-NEW-build/dep1;[^';]*/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-CMP0166-NEW-build/dep2'
+]])
+include(${CMAKE_CURRENT_LIST_DIR}/TransitiveLink-check-common.cmake)

+ 2 - 0
Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-CMP0166-NEW.cmake

@@ -0,0 +1,2 @@
+cmake_policy(SET CMP0166 NEW)
+include(TransitiveLink-common.cmake)

+ 8 - 0
Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-CMP0166-OLD-check.cmake

@@ -0,0 +1,8 @@
+set(expect [[
+# file\(GENERATE\) produced:
+main LINK_LIBRARIES: 'foo1' # not transitive
+main LINK_DIRECTORIES: '[^';]*/Tests/RunCMake/GenEx-TARGET_PROPERTY/dirM;[^';]*/Tests/RunCMake/GenEx-TARGET_PROPERTY/dir1'
+main LINK_OPTIONS: '-optM;-opt1'
+main LINK_DEPENDS: '[^';]*/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-CMP0166-OLD-build/depM;[^';]*/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-CMP0166-OLD-build/dep1'
+]])
+include(${CMAKE_CURRENT_LIST_DIR}/TransitiveLink-check-common.cmake)

+ 2 - 0
Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-CMP0166-OLD.cmake

@@ -0,0 +1,2 @@
+cmake_policy(SET CMP0166 OLD)
+include(TransitiveLink-common.cmake)

+ 12 - 0
Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-check-common.cmake

@@ -0,0 +1,12 @@
+string(REGEX REPLACE "\r\n" "\n" expect "${expect}")
+string(REGEX REPLACE "\n+$" "" expect "${expect}")
+
+file(READ "${RunCMake_TEST_BINARY_DIR}/out.txt" actual)
+string(REGEX REPLACE "\r\n" "\n" actual "${actual}")
+string(REGEX REPLACE "\n+$" "" actual "${actual}")
+
+if(NOT actual MATCHES "^${expect}$")
+  string(REPLACE "\n" "\n expect> " expect " expect> ${expect}")
+  string(REPLACE "\n" "\n actual> " actual " actual> ${actual}")
+  message(FATAL_ERROR "Expected file(GENERATE) output:\n${expect}\ndoes not match actual output:\n${actual}")
+endif()

+ 42 - 0
Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-common.cmake

@@ -0,0 +1,42 @@
+enable_language(C)
+
+add_library(foo1 STATIC empty.c)
+target_link_libraries(foo1 PRIVATE foo2 foo3)
+target_link_directories(foo1 INTERFACE dir1)
+target_link_options(foo1 INTERFACE -opt1)
+set_target_properties(foo1 PROPERTIES
+  INTERFACE_LINK_DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/dep1"
+  )
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/dep1" "")
+
+add_library(foo2 STATIC empty.c)
+target_link_directories(foo2 INTERFACE dir2)
+target_link_options(foo2 INTERFACE -opt2)
+set_target_properties(foo2 PROPERTIES
+  INTERFACE_LINK_DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/dep2"
+  )
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/dep2" "")
+
+add_library(foo3 STATIC empty.c)
+target_link_directories(foo3 PRIVATE dir3)
+target_link_options(foo3 PRIVATE -opt3)
+set_target_properties(foo3 PROPERTIES
+  LINK_DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/dep3"
+  )
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/dep3" "")
+
+add_executable(main main.c)
+target_link_libraries(main PRIVATE foo1)
+target_link_directories(main PRIVATE dirM)
+target_link_options(main PRIVATE -optM)
+set_target_properties(main PROPERTIES
+  LINK_DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/depM"
+  )
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/depM" "")
+
+file(GENERATE OUTPUT out.txt CONTENT "# file(GENERATE) produced:
+main LINK_LIBRARIES: '$<TARGET_PROPERTY:main,LINK_LIBRARIES>' # not transitive
+main LINK_DIRECTORIES: '$<TARGET_PROPERTY:main,LINK_DIRECTORIES>'
+main LINK_OPTIONS: '$<TARGET_PROPERTY:main,LINK_OPTIONS>'
+main LINK_DEPENDS: '$<TARGET_PROPERTY:main,LINK_DEPENDS>'
+")

+ 0 - 0
Tests/RunCMake/GenEx-TARGET_PROPERTY/empty.h


+ 0 - 0
Tests/RunCMake/GenEx-TARGET_PROPERTY/empty1.c


+ 0 - 0
Tests/RunCMake/GenEx-TARGET_PROPERTY/empty1.h


+ 0 - 0
Tests/RunCMake/GenEx-TARGET_PROPERTY/empty2.h


+ 0 - 0
Tests/RunCMake/GenEx-TARGET_PROPERTY/empty3.h


+ 4 - 0
Tests/RunCMake/GenEx-TARGET_PROPERTY/main.c

@@ -0,0 +1,4 @@
+int main(void)
+{
+  return 0;
+}