Browse Source

GenEx: Evaluate LINK_LIBRARIES target properties transitively

The `LINK_LIBRARIES` and `INTERFACE_LINK_LIBRARIES` target properties
establish the graph of link dependencies used to propagate usage
requirements transitively.  Therefore the `$<TARGET_PROPERTY:...>`
generator expression should evaluate them transitively as it does for
other transitive properties.  Add policy CMP0189 for compatibility.

Fixes: #26709
Issue: #12435
Brad King 7 months ago
parent
commit
b3da9c6d60

+ 8 - 2
Help/manual/cmake-generator-expressions.7.rst

@@ -1919,7 +1919,10 @@ The expressions have special evaluation rules for some properties:
     :prop_tgt:`INTERFACE_LINK_LIBRARIES` *including* entries guarded by the
     :genex:`LINK_ONLY` generator expression.  See policy :policy:`CMP0166`.
 
-  Evaluation of :prop_tgt:`LINK_LIBRARIES` itself is not transitive.
+  .. versionchanged:: 4.1
+
+    Evaluation of :prop_tgt:`LINK_LIBRARIES` itself is now transitive.
+    See policy :policy:`CMP0189`.
 
 :ref:`Target Usage Requirement Properties <Target Usage Requirements>`
   These evaluate as a :ref:`semicolon-separated list <CMake Language Lists>`
@@ -1936,7 +1939,10 @@ The expressions have special evaluation rules for some properties:
     *including* entries guarded by the :genex:`LINK_ONLY` generator expression.
     See policy :policy:`CMP0166`.
 
-  Evaluation of :prop_tgt:`INTERFACE_LINK_LIBRARIES` itself is not transitive.
+  .. versionchanged:: 4.1
+
+    Evaluation of :prop_tgt:`INTERFACE_LINK_LIBRARIES` itself is now
+    transitive.  See policy :policy:`CMP0189`.
 
 :ref:`Custom Transitive Properties`
   .. versionadded:: 3.30

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

@@ -98,6 +98,7 @@ Policies Introduced by CMake 4.1
 .. toctree::
    :maxdepth: 1
 
+   CMP0189: TARGET_PROPERTY evaluates LINK_LIBRARIES properties transitively. </policy/CMP0189>
    CMP0188: The FindGCCXML module is removed. </policy/CMP0188>
    CMP0187: Include source file without an extension after the same name with an extension. </policy/CMP0187>
    CMP0186: Regular expressions match ^ at most once in repeated searches. </policy/CMP0186>

+ 30 - 0
Help/policy/CMP0189.rst

@@ -0,0 +1,30 @@
+CMP0189
+-------
+
+.. versionadded:: 4.1
+
+:genex:`TARGET_PROPERTY` evaluates ``LINK_LIBRARIES`` properties transitively.
+
+The :prop_tgt:`LINK_LIBRARIES` and :prop_tgt:`INTERFACE_LINK_LIBRARIES`
+target properties record link dependencies through which the
+:genex:`TARGET_PROPERTY` generator expression evaluates transitive properties.
+However, in CMake 4.0 and below, the properties themselves were not evaluated
+transitively.  CMake 4.1 and above prefer to evaluate the
+:prop_tgt:`LINK_LIBRARIES` and :prop_tgt:`INTERFACE_LINK_LIBRARIES`
+target properties transitively because they are among the
+:ref:`build specification <Target Build Specification>` and
+:ref:`usage requirement <Target Usage Requirements>` properties, respectively.
+This policy provides compatibility for projects that have not been updated to
+expect the new behavior.
+
+The ``OLD`` behavior of this policy is for :genex:`TARGET_PROPERTY` to not
+evaluate :prop_tgt:`LINK_LIBRARIES` and :prop_tgt:`INTERFACE_LINK_LIBRARIES`
+transitively.  The ``NEW`` behavior is for :genex:`TARGET_PROPERTY` to
+evaluate :prop_tgt:`LINK_LIBRARIES` and :prop_tgt:`INTERFACE_LINK_LIBRARIES`
+transitively.
+
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 4.1
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
+.. include:: STANDARD_ADVICE.txt
+
+.. include:: DEPRECATED.txt

+ 6 - 0
Help/release/dev/genex-transitive-link-libraries.rst

@@ -0,0 +1,6 @@
+genex-transitive-link-libraries
+-------------------------------
+
+* The :genex:`TARGET_PROPERTY` generator expression now evaluates the
+  :prop_tgt:`LINK_LIBRARIES` and :prop_tgt:`INTERFACE_LINK_LIBRARIES`
+  target properties transitively.  See policy :policy:`CMP0189`.

+ 8 - 0
Source/cmGeneratorTarget_TransitiveProperty.cxx

@@ -45,6 +45,7 @@ std::map<cm::string_view, TransitiveProperty> const
       { "INTERFACE_INCLUDE_DIRECTORIES"_s, UseTo::Compile } },
     { "LINK_DEPENDS"_s, { "INTERFACE_LINK_DEPENDS"_s, UseTo::Link } },
     { "LINK_DIRECTORIES"_s, { "INTERFACE_LINK_DIRECTORIES"_s, UseTo::Link } },
+    { "LINK_LIBRARIES"_s, { "INTERFACE_LINK_LIBRARIES"_s, UseTo::Link } },
     { "LINK_OPTIONS"_s, { "INTERFACE_LINK_OPTIONS"_s, UseTo::Link } },
     { "PRECOMPILE_HEADERS"_s,
       { "INTERFACE_PRECOMPILE_HEADERS"_s, UseTo::Compile } },
@@ -196,6 +197,13 @@ cmGeneratorTarget::IsTransitiveProperty(
     prop = prop.substr(kINTERFACE_.length());
   }
   auto i = BuiltinTransitiveProperties.find(prop);
+  if (i != BuiltinTransitiveProperties.end() &&
+      // Look up CMP0189 in the context where evaluation occurs,
+      // not where the target was created.
+      lg->GetPolicyStatus(cmPolicies::CMP0189) != cmPolicies::NEW &&
+      prop == "LINK_LIBRARIES"_s) {
+    i = BuiltinTransitiveProperties.end();
+  }
   if (i != BuiltinTransitiveProperties.end()) {
     result = i->second;
     if (result->Usage != cmGeneratorTarget::UseTo::Compile) {

+ 4 - 1
Source/cmPolicies.h

@@ -563,7 +563,10 @@ class cmMakefile;
          "Include source file without an extension after the same name with " \
          "an extension.",                                                     \
          4, 1, 0, WARN)                                                       \
-  SELECT(POLICY, CMP0188, "The FindGCCXML module is removed.", 4, 1, 0, WARN)
+  SELECT(POLICY, CMP0188, "The FindGCCXML module is removed.", 4, 1, 0, WARN) \
+  SELECT(POLICY, CMP0189,                                                     \
+         "TARGET_PROPERTY evaluates LINK_LIBRARIES properties transitively.", \
+         4, 1, 0, WARN)
 
 #define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1)
 #define CM_FOR_EACH_POLICY_ID(POLICY)                                         \

+ 57 - 0
Tests/CustomTransitiveProperties/CMP0189/CMakeLists.txt

@@ -0,0 +1,57 @@
+cmake_policy(SET CMP0189 NEW)
+set(out "${CMAKE_CURRENT_BINARY_DIR}/out-$<CONFIG>.txt")
+file(GENERATE OUTPUT "${out}" CONTENT "# file(GENERATE) produced:
+${in_LINK_LIBRARIES}
+")
+add_custom_target(check-CMP0189-NEW ALL VERBATIM
+  COMMAND ${CMAKE_COMMAND} -Dconfig=$<CONFIG> -Dout=${out} -P${CMAKE_CURRENT_SOURCE_DIR}/check.cmake
+  COMMAND check-args
+  "$<TARGET_PROPERTY:iface1,LINK_LIBRARIES>" ""
+  "$<TARGET_PROPERTY:iface1,INTERFACE_LINK_LIBRARIES>" ""
+  "$<TARGET_PROPERTY:iface2,LINK_LIBRARIES>" ""
+  "$<TARGET_PROPERTY:iface2,INTERFACE_LINK_LIBRARIES>" "iface1"
+  "$<TARGET_PROPERTY:static1,LINK_LIBRARIES>" "iface2;iface1"
+  "$<TARGET_PROPERTY:static1,INTERFACE_LINK_LIBRARIES>" "iface2;iface1"
+  "$<TARGET_PROPERTY:CustomTransitiveProperties,LINK_LIBRARIES>" "static1;object1;iface2;iface1;iface2"
+  "$<TARGET_PROPERTY:CustomTransitiveProperties,INTERFACE_LINK_LIBRARIES>" ""
+  COMMAND check-args
+  "$<TARGET_PROPERTY:iface10,LINK_LIBRARIES>" ""
+  "$<TARGET_PROPERTY:iface10,INTERFACE_LINK_LIBRARIES>" ""
+  "$<TARGET_PROPERTY:iface11,LINK_LIBRARIES>" ""
+  "$<TARGET_PROPERTY:iface11,INTERFACE_LINK_LIBRARIES>" "iface10"
+  "$<TARGET_PROPERTY:static10,LINK_LIBRARIES>" "iface11;iface10"
+  #                                              _/         \__
+  #                                             /              \
+  #                               "static10[iface11];iface11[iface10]"
+  "$<TARGET_PROPERTY:static10,INTERFACE_LINK_LIBRARIES>" "iface11;iface10"
+  "$<TARGET_PROPERTY:static11,LINK_LIBRARIES>" "static10;iface11;iface11;iface10"
+  #                                             __/      __/         \__     \__________
+  #                                            /        /               \               \
+  #                              "static11[static10;iface11];static10[iface11;iface11[iface10]]"
+  "$<TARGET_PROPERTY:static11,INTERFACE_LINK_LIBRARIES>" "static10;iface11;iface11;iface10"
+  "$<TARGET_PROPERTY:main10,LINK_LIBRARIES>" "static11;static10;static10;iface11;iface11;iface10"
+  #                                      _______/ _______/         |        |        \______ \______________
+  #                                     /        /                 |        |               \               \
+  #                         "main10[static11;static10];static11[static10;iface11;static10[iface11;iface11[iface10]]]"
+  "$<TARGET_PROPERTY:main10,INTERFACE_LINK_LIBRARIES>" ""
+  COMMAND check-args
+  "$<TARGET_PROPERTY:iface20,LINK_LIBRARIES>" ""
+  "$<TARGET_PROPERTY:iface20,INTERFACE_LINK_LIBRARIES>" ""
+  "$<TARGET_PROPERTY:iface21,LINK_LIBRARIES>" ""
+  "$<TARGET_PROPERTY:iface21,INTERFACE_LINK_LIBRARIES>" "iface20"
+  "$<TARGET_PROPERTY:static20,LINK_LIBRARIES>" "iface21;iface20"
+  #                                              _/         \__
+  #                                             /              \
+  #                               "static20[iface21];iface21[iface20]"
+  "$<TARGET_PROPERTY:static20,INTERFACE_LINK_LIBRARIES>" "iface21;iface20"
+  "$<TARGET_PROPERTY:static21,LINK_LIBRARIES>" "static20;iface21;iface21;iface20"
+  #                                             __/      __/         \__     \__________
+  #                                            /        /               \               \
+  #                              "static21[static20;iface21];static20[iface21;iface21[iface20]]"
+  "$<TARGET_PROPERTY:static21,INTERFACE_LINK_LIBRARIES>" "static20;iface21;iface21;iface20"
+  "$<TARGET_PROPERTY:main20,LINK_LIBRARIES>" "static21;static20;static20;iface21;iface21;iface20"
+  #                                      _______/ _______/         |        |        \______ \______________
+  #                                     /        /                 |        |               \               \
+  #                         "main20[static21;static20];static21[static20;iface21;static20[iface21;iface21[iface20]]]"
+  "$<TARGET_PROPERTY:main20,INTERFACE_LINK_LIBRARIES>" ""
+  )

+ 32 - 0
Tests/CustomTransitiveProperties/CMP0189/check.cmake

@@ -0,0 +1,32 @@
+set(expect [[
+# file\(GENERATE\) produced:
+iface1 LINK_LIBRARIES: ''
+iface1 INTERFACE_LINK_LIBRARIES: ''
+iface2 LINK_LIBRARIES: ''
+iface2 INTERFACE_LINK_LIBRARIES: 'iface1'
+static1 LINK_LIBRARIES: 'iface2;iface1'
+static1 INTERFACE_LINK_LIBRARIES: 'iface2;iface1'
+main LINK_LIBRARIES: 'static1;object1;iface2;iface1;iface2'
+main INTERFACE_LINK_LIBRARIES: ''
+iface10 LINK_LIBRARIES: ''
+iface10 INTERFACE_LINK_LIBRARIES: ''
+iface11 LINK_LIBRARIES: ''
+iface11 INTERFACE_LINK_LIBRARIES: 'iface10'
+static10 LINK_LIBRARIES: 'iface11;iface10'
+static10 INTERFACE_LINK_LIBRARIES: 'iface11;iface10'
+static11 LINK_LIBRARIES: 'static10;iface11;iface11;iface10'
+static11 INTERFACE_LINK_LIBRARIES: 'static10;iface11;iface11;iface10'
+main10 LINK_LIBRARIES: 'static11;static10;static10;iface11;iface11;iface10'
+main10 INTERFACE_LINK_LIBRARIES: ''
+iface20 LINK_LIBRARIES: ''
+iface20 INTERFACE_LINK_LIBRARIES: ''
+iface21 LINK_LIBRARIES: ''
+iface21 INTERFACE_LINK_LIBRARIES: 'iface20'
+static20 LINK_LIBRARIES: 'iface21;iface20'
+static20 INTERFACE_LINK_LIBRARIES: 'iface21;iface20'
+static21 LINK_LIBRARIES: 'static20;iface21;iface21;iface20'
+static21 INTERFACE_LINK_LIBRARIES: 'static20;iface21;iface21;iface20'
+main20 LINK_LIBRARIES: 'static21;static20;static20;iface21;iface21;iface20'
+main20 INTERFACE_LINK_LIBRARIES: ''
+]])
+include(${CMAKE_CURRENT_LIST_DIR}/../check-common.cmake)

+ 34 - 0
Tests/CustomTransitiveProperties/CMakeLists.txt

@@ -102,6 +102,17 @@ target_link_libraries(static11 PRIVATE static10 iface11)
 add_executable(main10 main10.c)
 target_link_libraries(main10 PRIVATE static11 static10)
 
+# Test CMP0189 OLD and NEW behavior.
+add_library(iface20 INTERFACE)
+add_library(iface21 INTERFACE)
+target_link_libraries(iface21 INTERFACE iface20)
+add_library(static20 STATIC static20.c)
+target_link_libraries(static20 PRIVATE iface21)
+add_library(static21 STATIC static21.c)
+target_link_libraries(static21 PRIVATE static20 iface21)
+add_executable(main20 main20.c)
+target_link_libraries(main20 PRIVATE static21 static20)
+
 # Test TRANSITIVE_*_PROPERTY evaluation outside of usage requirements.
 add_executable(check-args check-args.c)
 set(out "${CMAKE_CURRENT_BINARY_DIR}/out-$<CONFIG>.txt")
@@ -158,6 +169,16 @@ static11 LINK_LIBRARIES: '$<TARGET_PROPERTY:static11,LINK_LIBRARIES>'
 static11 INTERFACE_LINK_LIBRARIES: '$<TARGET_PROPERTY:static11,INTERFACE_LINK_LIBRARIES>'
 main10 LINK_LIBRARIES: '$<TARGET_PROPERTY:main10,LINK_LIBRARIES>'
 main10 INTERFACE_LINK_LIBRARIES: '$<TARGET_PROPERTY:main10,INTERFACE_LINK_LIBRARIES>'
+iface20 LINK_LIBRARIES: '$<TARGET_PROPERTY:iface20,LINK_LIBRARIES>'
+iface20 INTERFACE_LINK_LIBRARIES: '$<TARGET_PROPERTY:iface20,INTERFACE_LINK_LIBRARIES>'
+iface21 LINK_LIBRARIES: '$<TARGET_PROPERTY:iface21,LINK_LIBRARIES>'
+iface21 INTERFACE_LINK_LIBRARIES: '$<TARGET_PROPERTY:iface21,INTERFACE_LINK_LIBRARIES>'
+static20 LINK_LIBRARIES: '$<TARGET_PROPERTY:static20,LINK_LIBRARIES>'
+static20 INTERFACE_LINK_LIBRARIES: '$<TARGET_PROPERTY:static20,INTERFACE_LINK_LIBRARIES>'
+static21 LINK_LIBRARIES: '$<TARGET_PROPERTY:static21,LINK_LIBRARIES>'
+static21 INTERFACE_LINK_LIBRARIES: '$<TARGET_PROPERTY:static21,INTERFACE_LINK_LIBRARIES>'
+main20 LINK_LIBRARIES: '$<TARGET_PROPERTY:main20,LINK_LIBRARIES>'
+main20 INTERFACE_LINK_LIBRARIES: '$<TARGET_PROPERTY:main20,INTERFACE_LINK_LIBRARIES>'
 ]====])
 file(GENERATE OUTPUT "${out}" CONTENT "# file(GENERATE) produced:
 ${in_CUSTOM}
@@ -210,4 +231,17 @@ add_custom_target(check ALL VERBATIM
   #                                     /        /                 |        |               \               \
   #                         "main10[static11;static10];static11[static10;iface11;static10[iface11;iface11[iface10]]]"
   "$<TARGET_PROPERTY:main10,INTERFACE_LINK_LIBRARIES>" ""
+  COMMAND check-args
+  "$<TARGET_PROPERTY:iface20,LINK_LIBRARIES>" ""
+  "$<TARGET_PROPERTY:iface20,INTERFACE_LINK_LIBRARIES>" ""
+  "$<TARGET_PROPERTY:iface21,LINK_LIBRARIES>" ""
+  "$<TARGET_PROPERTY:iface21,INTERFACE_LINK_LIBRARIES>" "iface20"
+  "$<TARGET_PROPERTY:static20,LINK_LIBRARIES>" "iface21"
+  "$<TARGET_PROPERTY:static20,INTERFACE_LINK_LIBRARIES>" "$<LINK_ONLY:iface21$<ANGLE-R>"
+  "$<TARGET_PROPERTY:static21,LINK_LIBRARIES>" "static20;iface21"
+  "$<TARGET_PROPERTY:static21,INTERFACE_LINK_LIBRARIES>" "$<LINK_ONLY:static20$<ANGLE-R>;$<LINK_ONLY:iface21$<ANGLE-R>"
+  "$<TARGET_PROPERTY:main20,LINK_LIBRARIES>" "static21;static20"
+  "$<TARGET_PROPERTY:main20,INTERFACE_LINK_LIBRARIES>" ""
   )
+
+add_subdirectory(CMP0189)

+ 12 - 0
Tests/CustomTransitiveProperties/check-common.cmake

@@ -0,0 +1,12 @@
+string(REGEX REPLACE "\r\n" "\n" expect "${expect}")
+string(REGEX REPLACE "\n+$" "" expect "${expect}")
+
+file(READ "${out}" 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()

+ 11 - 12
Tests/CustomTransitiveProperties/check.cmake

@@ -51,16 +51,15 @@ static11 LINK_LIBRARIES: 'static10;iface11;iface11;iface10'
 static11 INTERFACE_LINK_LIBRARIES: 'static10;iface11;iface11;iface10'
 main10 LINK_LIBRARIES: 'static11;static10;static10;iface11;iface11;iface10'
 main10 INTERFACE_LINK_LIBRARIES: ''
+iface20 LINK_LIBRARIES: ''
+iface20 INTERFACE_LINK_LIBRARIES: ''
+iface21 LINK_LIBRARIES: ''
+iface21 INTERFACE_LINK_LIBRARIES: 'iface20'
+static20 LINK_LIBRARIES: 'iface21'
+static20 INTERFACE_LINK_LIBRARIES: '\$<LINK_ONLY:iface21>'
+static21 LINK_LIBRARIES: 'static20;iface21'
+static21 INTERFACE_LINK_LIBRARIES: '\$<LINK_ONLY:static20>;\$<LINK_ONLY:iface21>'
+main20 LINK_LIBRARIES: 'static21;static20'
+main20 INTERFACE_LINK_LIBRARIES: ''
 ]])
-string(REGEX REPLACE "\r\n" "\n" expect "${expect}")
-string(REGEX REPLACE "\n+$" "" expect "${expect}")
-
-file(READ "${out}" 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()
+include(${CMAKE_CURRENT_LIST_DIR}/check-common.cmake)

+ 7 - 0
Tests/CustomTransitiveProperties/main20.c

@@ -0,0 +1,7 @@
+extern int static20(void);
+extern int static21(void);
+
+int main(void)
+{
+  return static20() + static21();
+}

+ 4 - 0
Tests/CustomTransitiveProperties/static20.c

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

+ 4 - 0
Tests/CustomTransitiveProperties/static21.c

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