From fad1ebaecc0c489d38c0a9a155f63fdfd9086907 Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Fri, 6 Mar 2026 15:50:09 +0100 Subject: [PATCH] cmake: resolve imported targets recursively when generating `libcurl.pc` To allow simplifying the binutils ld hack, by chaining the original imported target to curl's local duplicate target. Also to allow linking to dependencies' native imported targets via their CMake Configs, which will always be hooked up to a `CURL::` interface, and may also be chained upstream. Fixing (seen on Linux with simplified binutils hack via #20839): ``` Requires: Requires.private: libzstd openssl zlib Libs: -L${libdir} -lcurl -Libs.private: -lcrypto -lssl -lz -lzstd +Libs.private: -lOpenSSL::Crypto -lZLIB::ZLIB -lcrypto -lssl -lz -lzstd Cflags: -I${includedir} Cflags.private: -DCURL_STATICLIB Error: Process completed with exit code ``` Ref: https://github.com/curl/curl/actions/runs/22768301699/job/66041980258?pr=20839 Note this makes it possible to run into an infinite loop because CMake allows cyclic dependencies. It isn't added by curl's CMake script nor by any dependencies as defined by default, but may happen in theory with custom-created targets. In such case CMake automatically stops with an error at 1000 iterations. I find it overkill to add custom protection for it. Cherry-picked from #20814 Cherry-picked from #20839 Closes #20840 --- CMake/Macros.cmake | 32 ++++++++++++++++++++++++++++++++ CMakeLists.txt | 26 ++++++-------------------- 2 files changed, 38 insertions(+), 20 deletions(-) diff --git a/CMake/Macros.cmake b/CMake/Macros.cmake index e182f21d04..a0c26d483b 100644 --- a/CMake/Macros.cmake +++ b/CMake/Macros.cmake @@ -252,3 +252,35 @@ function(curl_add_clang_tidy_test_target _target_clang_tidy _target) add_dependencies(tests-clang-tidy ${_target_clang_tidy}) endif() endfunction() + +# Internal: Recurse into interface targets and collect their libraries +# and library paths. +macro(curl_collect_target_link_options _target) + get_target_property(_val ${_target} INTERFACE_LINK_DIRECTORIES) + if(_val) + list(APPEND _libdirs ${_val}) + endif() + get_target_property(_val ${_target} IMPORTED) + if(_val) + # LOCATION is empty for interface library targets and safe to ignore. + # Explicitly skip this query to avoid CMake v3.18 and older erroring out. + get_target_property(_val ${_target} TYPE) + if(NOT "${_val}" STREQUAL "INTERFACE_LIBRARY") + get_target_property(_val ${_target} LOCATION) + if(_val) + list(APPEND _libs ${_val}) + endif() + endif() + endif() + get_target_property(_val ${_target} INTERFACE_LINK_LIBRARIES) + if(_val) + foreach(_lib IN LISTS _val) + if(TARGET "${_lib}") + curl_collect_target_link_options(${_lib}) + else() + list(APPEND _libs ${_lib}) + endif() + endforeach() + endif() + unset(_val) +endmacro() diff --git a/CMakeLists.txt b/CMakeLists.txt index 308f04e84c..64c8895eef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2167,26 +2167,12 @@ if(NOT CURL_DISABLE_INSTALL) # Assume the user will not need this information in the .pc file. continue() endif() - if(_lib MATCHES "CURL::") - # This is empty for 'CURL::*' targets and safe to ignore. - # Explicitly skip this query to avoid CMake v3.18 and older erroring out. - set(_libname "") - else() - get_target_property(_libname "${_lib}" LOCATION) - endif() - if(_libname) - list(APPEND _explicit_libs "${_libname}") - else() - get_target_property(_libs "${_lib}" INTERFACE_LINK_LIBRARIES) - if(_libs) - list(APPEND _explicit_libs "${_libs}") - endif() - get_target_property(_libdirs "${_lib}" INTERFACE_LINK_DIRECTORIES) - if(_libdirs) - list(APPEND _explicit_libdirs "${_libdirs}") - endif() - endif() - if(NOT _libname AND NOT _libs AND NOT _libdirs) + set(_libdirs "") + set(_libs "") + curl_collect_target_link_options("${_lib}") # look into the target recursively + list(APPEND _explicit_libdirs ${_libdirs}) + list(APPEND _explicit_libs ${_libs}) + if(NOT _libs AND NOT _libdirs) message(WARNING "Bad lib in library list: ${_lib}") endif() if(_lib STREQUAL OpenSSL::SSL)