From b3da9c6d6054711c5075cc3405fb95aa065ea014 Mon Sep 17 00:00:00 2001 From: Brad King Date: Mon, 24 Feb 2025 11:43:47 -0500 Subject: [PATCH] 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 `$` generator expression should evaluate them transitively as it does for other transitive properties. Add policy CMP0189 for compatibility. Fixes: #26709 Issue: #12435 --- Help/manual/cmake-generator-expressions.7.rst | 10 +++- Help/manual/cmake-policies.7.rst | 1 + Help/policy/CMP0189.rst | 30 ++++++++++ .../dev/genex-transitive-link-libraries.rst | 6 ++ .../cmGeneratorTarget_TransitiveProperty.cxx | 8 +++ Source/cmPolicies.h | 5 +- .../CMP0189/CMakeLists.txt | 57 +++++++++++++++++++ .../CMP0189/check.cmake | 32 +++++++++++ .../CustomTransitiveProperties/CMakeLists.txt | 34 +++++++++++ .../check-common.cmake | 12 ++++ Tests/CustomTransitiveProperties/check.cmake | 23 ++++---- Tests/CustomTransitiveProperties/main20.c | 7 +++ Tests/CustomTransitiveProperties/static20.c | 4 ++ Tests/CustomTransitiveProperties/static21.c | 4 ++ 14 files changed, 218 insertions(+), 15 deletions(-) create mode 100644 Help/policy/CMP0189.rst create mode 100644 Help/release/dev/genex-transitive-link-libraries.rst create mode 100644 Tests/CustomTransitiveProperties/CMP0189/CMakeLists.txt create mode 100644 Tests/CustomTransitiveProperties/CMP0189/check.cmake create mode 100644 Tests/CustomTransitiveProperties/check-common.cmake create mode 100644 Tests/CustomTransitiveProperties/main20.c create mode 100644 Tests/CustomTransitiveProperties/static20.c create mode 100644 Tests/CustomTransitiveProperties/static21.c diff --git a/Help/manual/cmake-generator-expressions.7.rst b/Help/manual/cmake-generator-expressions.7.rst index f434e55424..2e576c1197 100644 --- a/Help/manual/cmake-generator-expressions.7.rst +++ b/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 ` These evaluate as a :ref:`semicolon-separated list ` @@ -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 diff --git a/Help/manual/cmake-policies.7.rst b/Help/manual/cmake-policies.7.rst index 7c46a2aa39..e546ee112c 100644 --- a/Help/manual/cmake-policies.7.rst +++ b/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. CMP0188: The FindGCCXML module is removed. CMP0187: Include source file without an extension after the same name with an extension. CMP0186: Regular expressions match ^ at most once in repeated searches. diff --git a/Help/policy/CMP0189.rst b/Help/policy/CMP0189.rst new file mode 100644 index 0000000000..c15217c1b2 --- /dev/null +++ b/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 ` and +:ref:`usage requirement ` 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 diff --git a/Help/release/dev/genex-transitive-link-libraries.rst b/Help/release/dev/genex-transitive-link-libraries.rst new file mode 100644 index 0000000000..d5cbb901d8 --- /dev/null +++ b/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`. diff --git a/Source/cmGeneratorTarget_TransitiveProperty.cxx b/Source/cmGeneratorTarget_TransitiveProperty.cxx index 855ddea2a6..2de75180de 100644 --- a/Source/cmGeneratorTarget_TransitiveProperty.cxx +++ b/Source/cmGeneratorTarget_TransitiveProperty.cxx @@ -45,6 +45,7 @@ std::map 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) { diff --git a/Source/cmPolicies.h b/Source/cmPolicies.h index c2449755b3..8549c0ac58 100644 --- a/Source/cmPolicies.h +++ b/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) \ diff --git a/Tests/CustomTransitiveProperties/CMP0189/CMakeLists.txt b/Tests/CustomTransitiveProperties/CMP0189/CMakeLists.txt new file mode 100644 index 0000000000..04fb1b4f63 --- /dev/null +++ b/Tests/CustomTransitiveProperties/CMP0189/CMakeLists.txt @@ -0,0 +1,57 @@ +cmake_policy(SET CMP0189 NEW) +set(out "${CMAKE_CURRENT_BINARY_DIR}/out-$.txt") +file(GENERATE OUTPUT "${out}" CONTENT "# file(GENERATE) produced: +${in_LINK_LIBRARIES} +") +add_custom_target(check-CMP0189-NEW ALL VERBATIM + COMMAND ${CMAKE_COMMAND} -Dconfig=$ -Dout=${out} -P${CMAKE_CURRENT_SOURCE_DIR}/check.cmake + COMMAND check-args + "$" "" + "$" "" + "$" "" + "$" "iface1" + "$" "iface2;iface1" + "$" "iface2;iface1" + "$" "static1;object1;iface2;iface1;iface2" + "$" "" + COMMAND check-args + "$" "" + "$" "" + "$" "" + "$" "iface10" + "$" "iface11;iface10" + # _/ \__ + # / \ + # "static10[iface11];iface11[iface10]" + "$" "iface11;iface10" + "$" "static10;iface11;iface11;iface10" + # __/ __/ \__ \__________ + # / / \ \ + # "static11[static10;iface11];static10[iface11;iface11[iface10]]" + "$" "static10;iface11;iface11;iface10" + "$" "static11;static10;static10;iface11;iface11;iface10" + # _______/ _______/ | | \______ \______________ + # / / | | \ \ + # "main10[static11;static10];static11[static10;iface11;static10[iface11;iface11[iface10]]]" + "$" "" + COMMAND check-args + "$" "" + "$" "" + "$" "" + "$" "iface20" + "$" "iface21;iface20" + # _/ \__ + # / \ + # "static20[iface21];iface21[iface20]" + "$" "iface21;iface20" + "$" "static20;iface21;iface21;iface20" + # __/ __/ \__ \__________ + # / / \ \ + # "static21[static20;iface21];static20[iface21;iface21[iface20]]" + "$" "static20;iface21;iface21;iface20" + "$" "static21;static20;static20;iface21;iface21;iface20" + # _______/ _______/ | | \______ \______________ + # / / | | \ \ + # "main20[static21;static20];static21[static20;iface21;static20[iface21;iface21[iface20]]]" + "$" "" + ) diff --git a/Tests/CustomTransitiveProperties/CMP0189/check.cmake b/Tests/CustomTransitiveProperties/CMP0189/check.cmake new file mode 100644 index 0000000000..f8e7125a72 --- /dev/null +++ b/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) diff --git a/Tests/CustomTransitiveProperties/CMakeLists.txt b/Tests/CustomTransitiveProperties/CMakeLists.txt index a9ac2b87bd..8d800ea636 100644 --- a/Tests/CustomTransitiveProperties/CMakeLists.txt +++ b/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-$.txt") @@ -158,6 +169,16 @@ static11 LINK_LIBRARIES: '$' static11 INTERFACE_LINK_LIBRARIES: '$' main10 LINK_LIBRARIES: '$' main10 INTERFACE_LINK_LIBRARIES: '$' +iface20 LINK_LIBRARIES: '$' +iface20 INTERFACE_LINK_LIBRARIES: '$' +iface21 LINK_LIBRARIES: '$' +iface21 INTERFACE_LINK_LIBRARIES: '$' +static20 LINK_LIBRARIES: '$' +static20 INTERFACE_LINK_LIBRARIES: '$' +static21 LINK_LIBRARIES: '$' +static21 INTERFACE_LINK_LIBRARIES: '$' +main20 LINK_LIBRARIES: '$' +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]]]" "$" "" + COMMAND check-args + "$" "" + "$" "" + "$" "" + "$" "iface20" + "$" "iface21" + "$" "$" + "$" "static20;iface21" + "$" "$;$" + "$" "static21;static20" + "$" "" ) + +add_subdirectory(CMP0189) diff --git a/Tests/CustomTransitiveProperties/check-common.cmake b/Tests/CustomTransitiveProperties/check-common.cmake new file mode 100644 index 0000000000..28ea94fbc3 --- /dev/null +++ b/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() diff --git a/Tests/CustomTransitiveProperties/check.cmake b/Tests/CustomTransitiveProperties/check.cmake index 079854292c..ffebca1a77 100644 --- a/Tests/CustomTransitiveProperties/check.cmake +++ b/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: '\$' +static21 LINK_LIBRARIES: 'static20;iface21' +static21 INTERFACE_LINK_LIBRARIES: '\$;\$' +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) diff --git a/Tests/CustomTransitiveProperties/main20.c b/Tests/CustomTransitiveProperties/main20.c new file mode 100644 index 0000000000..4b7dab5910 --- /dev/null +++ b/Tests/CustomTransitiveProperties/main20.c @@ -0,0 +1,7 @@ +extern int static20(void); +extern int static21(void); + +int main(void) +{ + return static20() + static21(); +} diff --git a/Tests/CustomTransitiveProperties/static20.c b/Tests/CustomTransitiveProperties/static20.c new file mode 100644 index 0000000000..1718d2f094 --- /dev/null +++ b/Tests/CustomTransitiveProperties/static20.c @@ -0,0 +1,4 @@ +int static20(void) +{ + return 0; +} diff --git a/Tests/CustomTransitiveProperties/static21.c b/Tests/CustomTransitiveProperties/static21.c new file mode 100644 index 0000000000..669cdb4626 --- /dev/null +++ b/Tests/CustomTransitiveProperties/static21.c @@ -0,0 +1,4 @@ +int static21(void) +{ + return 0; +}