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
This commit is contained in:
Viktor Szakats 2026-03-06 15:50:09 +01:00
parent e76968e20d
commit fad1ebaecc
No known key found for this signature in database
2 changed files with 38 additions and 20 deletions

View file

@ -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()

View file

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