Browse Source

FindPkgConfig: Populate _STATIC_LINK_LIBRARIES. Add STATIC_TARGET.

Add LINK_LIBRARIES test to demonstrate static linking of transitive
dependencies.

Add STATIC_TARGET argument to pkg_check_modules() and pkg_search_module().
Influences the properties of target produced by IMPORTED_TARGET.
When enabled: target's properties will be populated from <XXX>_STATIC_*
variables instead of from <XXX>_* variables.

Update existing tests concerning properties of targets produced via
IMPORTED_TARGET, to test STATIC_TARGET variant too.
Update existing tests concerning <XXX>_* variables to test <XXX>_STATIC_*
variables too.

Breaking changes to pkg_check_modules() and pkg_search_module():
- Variables CMAKE_FIND_LIBRARY_PREFIXES and CMAKE_FIND_LIBRARY_SUFFIXES can no
longer be used to influence library lookup (i.e. the internal call to
find_library()), because FindPkgConfig now internally relies on these
variables to differentiate between shared and static library lookup.
Prefer CMAKE_SHARED_LIBRARY_PREFIX + CMAKE_SHARED_LIBRARY_SUFFIX, or
CMAKE_STATIC_LIBRARY_PREFIX + CMAKE_STATIC_LIBRARY_SUFFIX, depending on
whether you wish to impact static or shared lookup.
- <XXX>_LINK_LIBRARIES will now be populated only with libraries
located via CMAKE_SHARED_LIBRARY_PREFIX + CMAKE_SHARED_LIBRARY_SUFFIX match
- <XXX>_STATIC_LIBRARIES now processes -framework options
- <XXX>_STATIC_LDFLAGS_OTHER now processes -framework options
- <XXX>_STATIC_CFLAGS_OTHER now processes -isystem options
- <XXX>_STATIC_INCLUDE_DIRS now processes -isystem options

Fixes: #21714
Alex Birch 3 years ago
parent
commit
020976d637

+ 7 - 0
Help/release/dev/pkgconfig-static-libs.rst

@@ -0,0 +1,7 @@
+pkgconfig-static-libs
+---------------------
+
+* The :module:`FindPkgConfig` module learned to find static libraries
+  in addition to the default search for shared libraries.
+  :command:`pkg_check_modules` gained a ``STATIC_TARGET`` option
+  to make the imported target reference static libraries.

+ 92 - 44
Modules/FindPkgConfig.cmake

@@ -169,13 +169,14 @@ macro(_pkgconfig_invoke_dyn _pkglist _prefix _varname cleanup_regexp)
 endmacro()
 
 # Splits given arguments into options and a package list
-macro(_pkgconfig_parse_options _result _is_req _is_silent _no_cmake_path _no_cmake_environment_path _imp_target _imp_target_global)
+macro(_pkgconfig_parse_options _result _is_req _is_silent _no_cmake_path _no_cmake_environment_path _imp_target _imp_target_global _static_target)
   set(${_is_req} 0)
   set(${_is_silent} 0)
   set(${_no_cmake_path} 0)
   set(${_no_cmake_environment_path} 0)
   set(${_imp_target} 0)
   set(${_imp_target_global} 0)
+  set(${_static_target} 0)
   if(DEFINED PKG_CONFIG_USE_CMAKE_PREFIX_PATH)
     if(NOT PKG_CONFIG_USE_CMAKE_PREFIX_PATH)
       set(${_no_cmake_path} 1)
@@ -205,6 +206,9 @@ macro(_pkgconfig_parse_options _result _is_req _is_silent _no_cmake_path _no_cma
     if (_pkg STREQUAL "GLOBAL")
       set(${_imp_target_global} 1)
     endif()
+    if (_pkg STREQUAL "STATIC_TARGET")
+      set(${_static_target} 1)
+    endif()
   endforeach()
 
   if (${_imp_target_global} AND NOT ${_imp_target})
@@ -217,6 +221,7 @@ macro(_pkgconfig_parse_options _result _is_req _is_silent _no_cmake_path _no_cma
   list(REMOVE_ITEM ${_result} "NO_CMAKE_PATH")
   list(REMOVE_ITEM ${_result} "NO_CMAKE_ENVIRONMENT_PATH")
   list(REMOVE_ITEM ${_result} "IMPORTED_TARGET")
+  list(REMOVE_ITEM ${_result} "STATIC_TARGET")
   list(REMOVE_ITEM ${_result} "GLOBAL")
 endmacro()
 
@@ -248,7 +253,7 @@ endfunction()
 # scan the LDFLAGS returned by pkg-config for library directories and
 # libraries, figure out the absolute paths of that libraries in the
 # given directories
-function(_pkg_find_libs _prefix _no_cmake_path _no_cmake_environment_path)
+function(_pkg_find_libs _prefix _no_cmake_path _no_cmake_environment_path _static)
   unset(_libs)
   unset(_find_opts)
 
@@ -261,9 +266,23 @@ function(_pkg_find_libs _prefix _no_cmake_path _no_cmake_environment_path)
     list(APPEND _find_opts "NO_CMAKE_ENVIRONMENT_PATH")
   endif()
 
+  if(_static)
+    set(var_prefix ${_prefix}_STATIC)
+    set(pkgcfg_lib_prefix pkgcfg_static_lib_${_prefix})
+    # reconfigure library prefixes/suffixes so that only static libraries can be found
+    set(CMAKE_FIND_LIBRARY_PREFIXES "${CMAKE_STATIC_LIBRARY_PREFIX}")
+    set(CMAKE_FIND_LIBRARY_SUFFIXES "${CMAKE_STATIC_LIBRARY_SUFFIX}")
+  else()
+    set(var_prefix ${_prefix})
+    set(pkgcfg_lib_prefix pkgcfg_lib_${_prefix})
+    # reconfigure library prefixes/suffixes so that only dynamic libraries can be found
+    set(CMAKE_FIND_LIBRARY_PREFIXES "${CMAKE_SHARED_LIBRARY_PREFIX}")
+    set(CMAKE_FIND_LIBRARY_SUFFIXES "${CMAKE_SHARED_LIBRARY_SUFFIX}")
+  endif()
+
   unset(_search_paths)
   unset(_next_is_framework)
-  foreach (flag IN LISTS ${_prefix}_LDFLAGS)
+  foreach (flag IN LISTS ${var_prefix}_LDFLAGS)
     if (_next_is_framework)
       list(APPEND _libs "-framework ${flag}")
       unset(_next_is_framework)
@@ -282,61 +301,78 @@ function(_pkg_find_libs _prefix _no_cmake_path _no_cmake_environment_path)
       continue()
     endif()
 
+    set(lib_var_name ${pkgcfg_lib_prefix}_${_pkg_search})
+
     if(_search_paths)
         # Firstly search in -L paths
-        find_library(pkgcfg_lib_${_prefix}_${_pkg_search}
+        find_library(${lib_var_name}
                      NAMES ${_pkg_search}
                      HINTS ${_search_paths} NO_DEFAULT_PATH)
     endif()
-    find_library(pkgcfg_lib_${_prefix}_${_pkg_search}
+    find_library(${lib_var_name}
                  NAMES ${_pkg_search}
                  ${_find_opts})
-    mark_as_advanced(pkgcfg_lib_${_prefix}_${_pkg_search})
-    if(pkgcfg_lib_${_prefix}_${_pkg_search})
-      list(APPEND _libs "${pkgcfg_lib_${_prefix}_${_pkg_search}}")
+
+    mark_as_advanced(${lib_var_name})
+    if(${lib_var_name})
+      list(APPEND _libs "${${lib_var_name}}")
     else()
       list(APPEND _libs ${_pkg_search})
     endif()
   endforeach()
 
-  set(${_prefix}_LINK_LIBRARIES "${_libs}" PARENT_SCOPE)
+  set(${var_prefix}_LINK_LIBRARIES "${_libs}" PARENT_SCOPE)
 endfunction()
 
 # create an imported target from all the information returned by pkg-config
-function(_pkg_create_imp_target _prefix _imp_target_global)
-  if (NOT TARGET PkgConfig::${_prefix})
+function(_pkg_create_imp_target _prefix _imp_target_global _var_qualifier)
+  set(tgt PkgConfig::${_prefix})
+  set(var_prefix ${_prefix}${_var_qualifier})
+
+  if (NOT TARGET ${tgt})
     if(${_imp_target_global})
       set(_global_opt "GLOBAL")
     else()
       unset(_global_opt)
     endif()
-    add_library(PkgConfig::${_prefix} INTERFACE IMPORTED ${_global_opt})
+    add_library(${tgt} INTERFACE IMPORTED ${_global_opt})
 
-    if(${_prefix}_INCLUDE_DIRS)
-      set_property(TARGET PkgConfig::${_prefix} PROPERTY
-                   INTERFACE_INCLUDE_DIRECTORIES "${${_prefix}_INCLUDE_DIRS}")
+    if(${var_prefix}_INCLUDE_DIRS)
+      set_property(TARGET ${tgt} PROPERTY
+                   INTERFACE_INCLUDE_DIRECTORIES "${${var_prefix}_INCLUDE_DIRS}")
     endif()
-    if(${_prefix}_LINK_LIBRARIES)
-      set_property(TARGET PkgConfig::${_prefix} PROPERTY
-                   INTERFACE_LINK_LIBRARIES "${${_prefix}_LINK_LIBRARIES}")
+    if(${var_prefix}_LINK_LIBRARIES)
+      set_property(TARGET ${tgt} PROPERTY
+                   INTERFACE_LINK_LIBRARIES "${${var_prefix}_LINK_LIBRARIES}")
     endif()
-    if(${_prefix}_LDFLAGS_OTHER)
-      set_property(TARGET PkgConfig::${_prefix} PROPERTY
-                   INTERFACE_LINK_OPTIONS "${${_prefix}_LDFLAGS_OTHER}")
+    if(${var_prefix}_LDFLAGS_OTHER)
+      set_property(TARGET ${tgt} PROPERTY
+                   INTERFACE_LINK_OPTIONS "${${var_prefix}_LDFLAGS_OTHER}")
     endif()
-    if(${_prefix}_CFLAGS_OTHER)
-      set_property(TARGET PkgConfig::${_prefix} PROPERTY
-                   INTERFACE_COMPILE_OPTIONS "${${_prefix}_CFLAGS_OTHER}")
+    if(${var_prefix}_CFLAGS_OTHER)
+      set_property(TARGET ${tgt} PROPERTY
+                   INTERFACE_COMPILE_OPTIONS "${${var_prefix}_CFLAGS_OTHER}")
     endif()
   endif()
 endfunction()
 
 # recalculate the dynamic output
 # this is a macro and not a function so the result of _pkg_find_libs is automatically propagated
-macro(_pkg_recalculate _prefix _no_cmake_path _no_cmake_environment_path _imp_target _imp_target_global)
-  _pkg_find_libs(${_prefix} ${_no_cmake_path} ${_no_cmake_environment_path})
+macro(_pkg_recalculate _prefix _no_cmake_path _no_cmake_environment_path _imp_target _imp_target_global _static_target)
+  # populate unqualified (shared) series of variables
+  _pkg_find_libs("${_prefix}" ${_no_cmake_path} ${_no_cmake_environment_path} FALSE)
+  # populate STATIC_ series of variables
+  _pkg_find_libs("${_prefix}" ${_no_cmake_path} ${_no_cmake_environment_path} TRUE)
+
   if(${_imp_target})
-    _pkg_create_imp_target(${_prefix} ${_imp_target_global})
+    if (${_static_target})
+      set(var_qualifier "_STATIC")
+    else()
+      set(var_qualifier "")
+    endif()
+    # create unqualified target, sourced from whichever series of variables is preferred.
+    # default: source target from unqualified (shared) variables. otherwise STATIC_ variables
+    _pkg_create_imp_target("${_prefix}" ${_imp_target_global} "${var_qualifier}")
   endif()
 endmacro()
 
@@ -503,7 +539,7 @@ function(_pkgconfig_extract_isystem _prefix)
 endfunction()
 
 ###
-macro(_pkg_check_modules_internal _is_required _is_silent _no_cmake_path _no_cmake_environment_path _imp_target _imp_target_global _prefix)
+macro(_pkg_check_modules_internal _is_required _is_silent _no_cmake_path _no_cmake_environment_path _imp_target _imp_target_global _static_target _prefix)
   _pkgconfig_unset(${_prefix}_FOUND)
   _pkgconfig_unset(${_prefix}_VERSION)
   _pkgconfig_unset(${_prefix}_PREFIX)
@@ -639,20 +675,21 @@ macro(_pkg_check_modules_internal _is_required _is_silent _no_cmake_path _no_cma
       _pkgconfig_invoke_dyn("${_pkg_check_modules_packages}" "${_prefix}" LIBRARY_DIRS  "(^| )-L"             --libs-only-L )
       _pkgconfig_invoke_dyn("${_pkg_check_modules_packages}" "${_prefix}" LDFLAGS       ""                    --libs )
       _pkgconfig_invoke_dyn("${_pkg_check_modules_packages}" "${_prefix}" LDFLAGS_OTHER ""                    --libs-only-other )
-
-      if (APPLE AND "-framework" IN_LIST ${_prefix}_LDFLAGS_OTHER)
-        _pkgconfig_extract_frameworks("${_prefix}")
-      endif()
-
       _pkgconfig_invoke_dyn("${_pkg_check_modules_packages}" "${_prefix}" INCLUDE_DIRS  "(^| )(-I|-isystem ?)" --cflags-only-I )
       _pkgconfig_invoke_dyn("${_pkg_check_modules_packages}" "${_prefix}" CFLAGS        ""                    --cflags )
       _pkgconfig_invoke_dyn("${_pkg_check_modules_packages}" "${_prefix}" CFLAGS_OTHER  ""                    --cflags-only-other )
 
-      if (${_prefix}_CFLAGS_OTHER MATCHES "-isystem")
-        _pkgconfig_extract_isystem("${_prefix}")
-      endif ()
+      foreach (qualifier IN ITEMS "" "_STATIC")
+        set(qual_prefix ${_prefix}${qualifier})
+        if (APPLE AND "-framework" IN_LIST ${qual_prefix}_LDFLAGS_OTHER)
+          _pkgconfig_extract_frameworks("${qual_prefix}")
+        endif()
+        if (${qual_prefix}_CFLAGS_OTHER MATCHES "-isystem")
+          _pkgconfig_extract_isystem("${qual_prefix}")
+        endif ()
+      endforeach()
 
-      _pkg_recalculate("${_prefix}" ${_no_cmake_path} ${_no_cmake_environment_path} ${_imp_target} ${_imp_target_global})
+      _pkg_recalculate("${_prefix}" ${_no_cmake_path} ${_no_cmake_environment_path} ${_imp_target} ${_imp_target_global} ${_static_target})
     endif()
 
     _pkg_restore_path_internal()
@@ -676,7 +713,7 @@ endmacro()
                       [REQUIRED] [QUIET]
                       [NO_CMAKE_PATH]
                       [NO_CMAKE_ENVIRONMENT_PATH]
-                      [IMPORTED_TARGET [GLOBAL]]
+                      [IMPORTED_TARGET [GLOBAL] [STATIC_TARGET]]
                       <moduleSpec> [<moduleSpec>...])
 
   When the ``REQUIRED`` argument is given, the command will fail with an error
@@ -707,6 +744,13 @@ endmacro()
     The ``GLOBAL`` argument will make the
     imported target available in global scope.
 
+  .. versionadded:: 3.24
+    The ``STATIC_TARGET`` argument will make the
+    imported target reference the static libraries
+    reported in ``<XXX>_STATIC_LINK_LIBRARIES``.
+    Without this option, the imported target
+    references the ``<XXX>_LINK_LIBRARIES``.
+
   .. versionadded:: 3.15
     Non-library linker options reported by ``pkg-config`` are stored in the
     :prop_tgt:`INTERFACE_LINK_OPTIONS` target property.
@@ -738,6 +782,10 @@ endmacro()
     only the libraries (without the '-l')
   ``<XXX>_LINK_LIBRARIES``
     the libraries and their absolute paths
+  ``<XXX>_STATIC_LINK_LIBRARIES``
+    .. versionadded:: 3.24
+
+    static libraries and their absolute paths
   ``<XXX>_LIBRARY_DIRS``
     the paths of the libraries (without the '-L')
   ``<XXX>_LDFLAGS``
@@ -819,12 +867,12 @@ endmacro()
     XRENDER_STATIC_LIBRARIES=Xrender;X11;pthread;Xau;Xdmcp
 #]========================================]
 macro(pkg_check_modules _prefix _module0)
-  _pkgconfig_parse_options(_pkg_modules _pkg_is_required _pkg_is_silent _no_cmake_path _no_cmake_environment_path _imp_target _imp_target_global "${_module0}" ${ARGN})
+  _pkgconfig_parse_options(_pkg_modules _pkg_is_required _pkg_is_silent _no_cmake_path _no_cmake_environment_path _imp_target _imp_target_global _static_target "${_module0}" ${ARGN})
   # check cached value
   if (NOT DEFINED __pkg_config_checked_${_prefix} OR __pkg_config_checked_${_prefix} LESS ${PKG_CONFIG_VERSION} OR NOT ${_prefix}_FOUND OR
       (NOT "${ARGN}" STREQUAL "" AND NOT "${__pkg_config_arguments_${_prefix}}" STREQUAL "${_module0};${ARGN}") OR
       (    "${ARGN}" STREQUAL "" AND NOT "${__pkg_config_arguments_${_prefix}}" STREQUAL "${_module0}"))
-    _pkg_check_modules_internal("${_pkg_is_required}" "${_pkg_is_silent}" ${_no_cmake_path} ${_no_cmake_environment_path} ${_imp_target} ${_imp_target_global} "${_prefix}" ${_pkg_modules})
+    _pkg_check_modules_internal("${_pkg_is_required}" "${_pkg_is_silent}" ${_no_cmake_path} ${_no_cmake_environment_path} ${_imp_target} ${_imp_target_global} ${_static_target} "${_prefix}" ${_pkg_modules})
 
     _pkgconfig_set(__pkg_config_checked_${_prefix} ${PKG_CONFIG_VERSION})
     if (${_prefix}_FOUND)
@@ -832,7 +880,7 @@ macro(pkg_check_modules _prefix _module0)
     endif()
   else()
     if (${_prefix}_FOUND)
-      _pkg_recalculate("${_prefix}" ${_no_cmake_path} ${_no_cmake_environment_path} ${_imp_target} ${_imp_target_global})
+      _pkg_recalculate("${_prefix}" ${_no_cmake_path} ${_no_cmake_environment_path} ${_imp_target} ${_imp_target_global} ${_static_target})
     endif()
   endif()
 endmacro()
@@ -866,7 +914,7 @@ endmacro()
     pkg_search_module (BAR libxml-2.0 libxml2 libxml>=2)
 #]========================================]
 macro(pkg_search_module _prefix _module0)
-  _pkgconfig_parse_options(_pkg_modules_alt _pkg_is_required _pkg_is_silent _no_cmake_path _no_cmake_environment_path _imp_target _imp_target_global "${_module0}" ${ARGN})
+  _pkgconfig_parse_options(_pkg_modules_alt _pkg_is_required _pkg_is_silent _no_cmake_path _no_cmake_environment_path _imp_target _imp_target_global _static_target "${_module0}" ${ARGN})
   # check cached value
   if (NOT DEFINED __pkg_config_checked_${_prefix} OR __pkg_config_checked_${_prefix} LESS ${PKG_CONFIG_VERSION} OR NOT ${_prefix}_FOUND)
     set(_pkg_modules_found 0)
@@ -878,7 +926,7 @@ macro(pkg_search_module _prefix _module0)
     # iterate through all modules and stop at the first working one.
     foreach(_pkg_alt ${_pkg_modules_alt})
       if(NOT _pkg_modules_found)
-        _pkg_check_modules_internal(0 1 ${_no_cmake_path} ${_no_cmake_environment_path} ${_imp_target} ${_imp_target_global} "${_prefix}" "${_pkg_alt}")
+        _pkg_check_modules_internal(0 1 ${_no_cmake_path} ${_no_cmake_environment_path} ${_imp_target} ${_imp_target_global} ${_static_target} "${_prefix}" "${_pkg_alt}")
       endif()
 
       if (${_prefix}_FOUND)
@@ -895,7 +943,7 @@ macro(pkg_search_module _prefix _module0)
 
     _pkgconfig_set(__pkg_config_checked_${_prefix} ${PKG_CONFIG_VERSION})
   elseif (${_prefix}_FOUND)
-    _pkg_recalculate("${_prefix}" ${_no_cmake_path} ${_no_cmake_environment_path} ${_imp_target} ${_imp_target_global})
+    _pkg_recalculate("${_prefix}" ${_no_cmake_path} ${_no_cmake_environment_path} ${_imp_target} ${_imp_target_global} ${_static_target})
   endif()
 endmacro()
 

+ 87 - 51
Tests/RunCMake/FindPkgConfig/FindPkgConfig_IMPORTED_TARGET.cmake

@@ -1,40 +1,59 @@
-cmake_minimum_required(VERSION 3.12)
+cmake_minimum_required(VERSION 3.17)
 
 project(FindPkgConfig_IMPORTED_TARGET C)
 
-find_package(PkgConfig REQUIRED)
-pkg_check_modules(NCURSES IMPORTED_TARGET QUIET ncurses)
+set(shared_lib_prefix "")
+set(shared_lib_suffix ".lib")
+set(static_lib_prefix "lib")
+set(static_lib_suffix ".a")
 
-message(STATUS "source: ${CMAKE_CURRENT_SOURCE_DIR} bin ${CMAKE_CURRENT_BINARY_DIR}")
+set(CMAKE_SHARED_LIBRARY_PREFIX ${shared_lib_prefix})
+set(CMAKE_SHARED_LIBRARY_SUFFIX ${shared_lib_suffix})
+set(CMAKE_STATIC_LIBRARY_PREFIX ${static_lib_prefix})
+set(CMAKE_STATIC_LIBRARY_SUFFIX ${static_lib_suffix})
 
-if (NCURSES_FOUND)
-  set(tgt PkgConfig::NCURSES)
-  if (NOT TARGET ${tgt})
-    message(FATAL_ERROR "FindPkgConfig found ncurses, but did not create an imported target for it")
-  endif ()
-  set(prop_found FALSE)
-  foreach (prop IN ITEMS INTERFACE_INCLUDE_DIRECTORIES INTERFACE_LINK_LIBRARIES INTERFACE_COMPILE_OPTIONS)
-    get_target_property(value ${tgt} ${prop})
-    if (value)
-      message(STATUS "Found property ${prop} on target: ${value}")
-      set(prop_found TRUE)
+find_package(PkgConfig REQUIRED)
+
+# to test multiple variations, we must pick unique prefix names (same-named targets are cached for re-use)
+set(prefix_uniquifiers 0 1)
+# whether to apply STATIC_TARGET argument
+set(static_target_args "" STATIC_TARGET)
+foreach (prefix_uniquifier static_target_arg IN ZIP_LISTS prefix_uniquifiers static_target_args)
+  set(prefix "NCURSES${prefix_uniquifier}")
+  message(STATUS "static_target_arg: ${static_target_arg}")
+  pkg_check_modules(${prefix} IMPORTED_TARGET QUIET ${static_target_arg} ncurses)
+
+  message(STATUS "source: ${CMAKE_CURRENT_SOURCE_DIR} bin ${CMAKE_CURRENT_BINARY_DIR}")
+
+  if (${prefix}_FOUND)
+    set(tgt PkgConfig::${prefix})
+    message(STATUS "Verifying target \"${tgt}\"")
+    if (NOT TARGET ${tgt})
+      message(FATAL_ERROR "FindPkgConfig found ncurses, but did not create an imported target for it")
     endif ()
-  endforeach ()
-  if (NOT prop_found)
-    message(FATAL_ERROR "target ${tgt} found, but it has no properties")
+    set(prop_found FALSE)
+    foreach (prop IN ITEMS INTERFACE_INCLUDE_DIRECTORIES INTERFACE_LINK_LIBRARIES INTERFACE_COMPILE_OPTIONS)
+      get_target_property(value ${tgt} ${prop})
+      if (value)
+        message(STATUS "Found property ${prop} on target: ${value}")
+        set(prop_found TRUE)
+      endif ()
+    endforeach ()
+    if (NOT prop_found)
+      message(FATAL_ERROR "target ${tgt} found, but it has no properties")
+    endif ()
+  else ()
+    message(STATUS "skipping test; ncurses not found")
   endif ()
-else ()
-  message(STATUS "skipping test; ncurses not found")
-endif ()
-
+endforeach ()
 
 # Setup for the remaining package tests below
 set(PKG_CONFIG_USE_CMAKE_PREFIX_PATH)
 set(fakePkgDir ${CMAKE_CURRENT_BINARY_DIR}/pc-fakepackage)
 foreach(i 1 2)
   set(pname cmakeinternalfakepackage${i})
-  file(WRITE ${fakePkgDir}/lib/lib${pname}.a "")
-  file(WRITE ${fakePkgDir}/lib/${pname}.lib  "")
+  file(WRITE ${fakePkgDir}/lib/${static_lib_prefix}${pname}${static_lib_suffix} "")
+  file(WRITE ${fakePkgDir}/lib/${shared_lib_prefix}${pname}${shared_lib_suffix} "")
   file(WRITE ${fakePkgDir}/lib/pkgconfig/${pname}.pc
 "Name: CMakeInternalFakePackage${i}
 Description: Dummy package (${i}) for FindPkgConfig IMPORTED_TARGET test
@@ -66,35 +85,52 @@ unset(CMAKE_PREFIX_PATH)
 unset(ENV{CMAKE_PREFIX_PATH})
 set(ENV{CMAKE_PREFIX_PATH} ${fakePkgDir})
 
-pkg_check_modules(FakePackage2 REQUIRED QUIET IMPORTED_TARGET cmakeinternalfakepackage2)
-if (NOT TARGET PkgConfig::FakePackage2)
-  message(FATAL_ERROR "No import target for fake package 2 with prefix path")
-endif()
 
-# check that 2 library entries exist
-list(LENGTH FakePackage2_LINK_LIBRARIES fp2_nlibs)
-if (NOT fp2_nlibs EQUAL 2)
-  message(FATAL_ERROR "FakePackage2_LINK_LIBRARIES has ${fp2_nlibs} entries but should have exactly 2")
-endif()
-
-# check that the full library path is also returned
-list(GET FakePackage2_LINK_LIBRARIES 0 fp2_lib0)
-if (NOT fp2_lib0 STREQUAL "${fakePkgDir}/lib/libcmakeinternalfakepackage2.a")
-  message(FATAL_ERROR "FakePackage2_LINK_LIBRARIES has bad content on first run: ${FakePackage2_LINK_LIBRARIES}")
-endif()
-
-# check that the library that couldn't be found still shows up
-list(GET FakePackage2_LINK_LIBRARIES 1 fp2_lib1)
-if (NOT fp2_lib1 STREQUAL "cmakeinternalfakepackage2-doesnotexist")
-  message(FATAL_ERROR "FakePackage2_LINK_LIBRARIES has bad content on first run: ${FakePackage2_LINK_LIBRARIES}")
-endif()
+# to test multiple variations, we must pick unique prefix names (same-named targets are cached for re-use)
+set(prefix_uniquifiers 0 1)
+# whether to apply STATIC_TARGET argument
+set(static_target_args "" STATIC_TARGET)
+# whether target properties are populated from the unqualified (i.e. shared library) series of vars, or the STATIC_ series of vars
+set(target_var_qualifiers "" STATIC_)
+set(lib_types shared static)
+foreach (prefix_uniquifier static_target_arg target_var_qualifier lib_type IN ZIP_LISTS prefix_uniquifiers static_target_args target_var_qualifiers lib_types)
+  set(prefix "FakePackage2${prefix_uniquifier}")
+  set(tgt "PkgConfig::${prefix}")
+  pkg_check_modules(${prefix} REQUIRED QUIET IMPORTED_TARGET ${static_target_arg} cmakeinternalfakepackage2)
 
-# the information in *_LINK_LIBRARIES is not cached, so ensure is also is present on second run
-unset(FakePackage2_LINK_LIBRARIES)
-pkg_check_modules(FakePackage2 REQUIRED QUIET IMPORTED_TARGET cmakeinternalfakepackage2)
-if (NOT FakePackage2_LINK_LIBRARIES STREQUAL "${fakePkgDir}/lib/libcmakeinternalfakepackage2.a;cmakeinternalfakepackage2-doesnotexist")
-  message(FATAL_ERROR "FakePackage2_LINK_LIBRARIES has bad content on second run: ${FakePackage2_LINK_LIBRARIES}")
-endif()
+  message(STATUS "Verifying library path resolution for lib type \"${lib_type}\"")
+  if (NOT TARGET ${tgt})
+    message(FATAL_ERROR "No import target for fake package 2 with prefix path")
+  endif()
+
+  set(link_libraries_var ${prefix}_${target_var_qualifier}LINK_LIBRARIES)
+  # check that 2 library entries exist
+  list(LENGTH ${link_libraries_var} fp2_nlibs)
+  if (NOT fp2_nlibs EQUAL 2)
+    message(FATAL_ERROR "${link_libraries_var} has ${fp2_nlibs} entries but should have exactly 2")
+  endif()
+
+  set(lib_leafname ${${lib_type}_lib_prefix}cmakeinternalfakepackage2${${lib_type}_lib_suffix})
+  message(STATUS "Expecting library leafname \"${lib_leafname}\"")
+  # check that the full library path is also returned
+  list(GET ${link_libraries_var} 0 fp2_lib0)
+  if (NOT fp2_lib0 STREQUAL "${fakePkgDir}/lib/${lib_leafname}")
+    message(FATAL_ERROR "${link_libraries_var} has bad content on first run: ${${link_libraries_var}}")
+  endif()
+
+  # check that the library that couldn't be found still shows up
+  list(GET ${link_libraries_var} 1 fp2_lib1)
+  if (NOT fp2_lib1 STREQUAL "cmakeinternalfakepackage2-doesnotexist")
+    message(FATAL_ERROR "${link_libraries_var} has bad content on first run: ${${link_libraries_var}}")
+  endif()
+
+  # the information in *_LINK_LIBRARIES is not cached, so ensure is also is present on second run
+  unset(${link_libraries_var})
+  pkg_check_modules(${prefix} REQUIRED QUIET IMPORTED_TARGET ${static_target_arg} cmakeinternalfakepackage2)
+  if (NOT ${link_libraries_var} STREQUAL "${fakePkgDir}/lib/${lib_leafname};cmakeinternalfakepackage2-doesnotexist")
+    message(FATAL_ERROR "${link_libraries_var} has bad content on second run: ${${link_libraries_var}}")
+  endif()
+endforeach()
 
 set(pname fakelinkoptionspackage)
 file(WRITE ${fakePkgDir}/lib/pkgconfig/${pname}.pc

+ 4 - 1
Tests/RunCMake/FindPkgConfig/FindPkgConfig_LIBRARY_PATH-stdout.txt

@@ -1,3 +1,6 @@
 -- ZOT_LIBRARIES='zot'
--- ZOT_LINK_LIBRARIES='[^']*/Tests/RunCMake/FindPkgConfig/FindPkgConfig_LIBRARY_PATH-build/root/lib/prefix-zot-suffix'
+-- ZOT_LINK_LIBRARIES='[^']*/Tests/RunCMake/FindPkgConfig/FindPkgConfig_LIBRARY_PATH-build/root/lib/dyprefix-zot-dysuffix'
 -- ZOT_LDFLAGS='-L[^']*/Tests/RunCMake/FindPkgConfig/FindPkgConfig_LIBRARY_PATH-build/root/lib;-lzot'
+-- ZOT_STATIC_LIBRARIES='zot'
+-- ZOT_STATIC_LINK_LIBRARIES='[^']*/Tests/RunCMake/FindPkgConfig/FindPkgConfig_LIBRARY_PATH-build/root/lib/stprefix-zot-stsuffix'
+-- ZOT_STATIC_LDFLAGS='-L[^']*/Tests/RunCMake/FindPkgConfig/FindPkgConfig_LIBRARY_PATH-build/root/lib;-lzot'

+ 16 - 3
Tests/RunCMake/FindPkgConfig/FindPkgConfig_LIBRARY_PATH.cmake

@@ -15,10 +15,20 @@ Version: 1.0
 Libs: -L\${libdir} -lzot
 ")
 
+set(shared_lib_prefix "dyprefix-")
+set(shared_lib_suffix "-dysuffix")
+set(static_lib_prefix "stprefix-")
+set(static_lib_suffix "-stsuffix")
+
+set(CMAKE_SHARED_LIBRARY_PREFIX ${shared_lib_prefix})
+set(CMAKE_SHARED_LIBRARY_SUFFIX ${shared_lib_suffix})
+set(CMAKE_STATIC_LIBRARY_PREFIX ${static_lib_prefix})
+set(CMAKE_STATIC_LIBRARY_SUFFIX ${static_lib_suffix})
+
 # Create a "library" file to find in libdir.
-set(CMAKE_FIND_LIBRARY_PREFIXES "prefix-")
-set(CMAKE_FIND_LIBRARY_SUFFIXES "-suffix")
-file(WRITE "${LIB_DIR}/prefix-zot-suffix")
+foreach(variant shared static)
+  file(WRITE "${LIB_DIR}/${${variant}_lib_prefix}zot${${variant}_lib_suffix}")
+endforeach()
 
 # 'pkg-config --libs' drops -L flags in PKG_CONFIG_SYSTEM_LIBRARY_PATH by default.
 set(ENV{PKG_CONFIG_SYSTEM_LIBRARY_PATH} "${LIB_DIR}")
@@ -32,3 +42,6 @@ pkg_check_modules(ZOT REQUIRED zot)
 message(STATUS "ZOT_LIBRARIES='${ZOT_LIBRARIES}'")
 message(STATUS "ZOT_LINK_LIBRARIES='${ZOT_LINK_LIBRARIES}'")
 message(STATUS "ZOT_LDFLAGS='${ZOT_LDFLAGS}'")
+message(STATUS "ZOT_STATIC_LIBRARIES='${ZOT_STATIC_LIBRARIES}'")
+message(STATUS "ZOT_STATIC_LINK_LIBRARIES='${ZOT_STATIC_LINK_LIBRARIES}'")
+message(STATUS "ZOT_STATIC_LDFLAGS='${ZOT_STATIC_LDFLAGS}'")

+ 6 - 0
Tests/RunCMake/FindPkgConfig/FindPkgConfig_LINK_LIBRARIES-stdout.txt

@@ -0,0 +1,6 @@
+-- IMM_LIBRARIES='imm'
+-- IMM_LINK_LIBRARIES='[^']*/Tests/RunCMake/FindPkgConfig/FindPkgConfig_LINK_LIBRARIES-build/root/lib/dyprefix-imm-dysuffix'
+-- IMM_LDFLAGS='-L[^']*/Tests/RunCMake/FindPkgConfig/FindPkgConfig_LINK_LIBRARIES-build/root/lib;-limm'
+-- IMM_STATIC_LIBRARIES='imm;trns'
+-- IMM_STATIC_LINK_LIBRARIES='[^']*/Tests/RunCMake/FindPkgConfig/FindPkgConfig_LINK_LIBRARIES-build/root/lib/stprefix-imm-stsuffix;[^']*/Tests/RunCMake/FindPkgConfig/FindPkgConfig_LINK_LIBRARIES-build/root/lib/stprefix-trns-stsuffix'
+-- IMM_STATIC_LDFLAGS='-L[^']*/Tests/RunCMake/FindPkgConfig/FindPkgConfig_LINK_LIBRARIES-build/root/lib;-limm;-ltrns'

+ 53 - 0
Tests/RunCMake/FindPkgConfig/FindPkgConfig_LINK_LIBRARIES.cmake

@@ -0,0 +1,53 @@
+find_package(PkgConfig REQUIRED)
+
+set(ROOT "${CMAKE_CURRENT_BINARY_DIR}/root")
+string(REPLACE " " "\\ " ESCAPED_ROOT "${ROOT}")
+set(LIB_DIR "${ROOT}/lib")
+set(PKGCONFIG_DIR "${LIB_DIR}/pkgconfig")
+
+file(WRITE "${PKGCONFIG_DIR}/imm.pc" "
+prefix=${ESCAPED_ROOT}
+libdir=\${prefix}/lib
+
+Name: Immediate
+Description: Dummy package to test *LINK_LIBRARIES support
+Version: 1.0
+Libs: -L\${libdir} -limm
+Libs.private: -ltrns
+")
+file(WRITE "${PKGCONFIG_DIR}/trns.pc" "
+prefix=${ESCAPED_ROOT}
+libdir=\${prefix}/lib
+
+Name: Transitive
+Description: Dummy package to test *LINK_LIBRARIES support
+Version: 1.0
+Libs: -L\${libdir} -ltrns
+")
+
+set(shared_lib_prefix "dyprefix-")
+set(shared_lib_suffix "-dysuffix")
+set(static_lib_prefix "stprefix-")
+set(static_lib_suffix "-stsuffix")
+
+set(CMAKE_SHARED_LIBRARY_PREFIX ${shared_lib_prefix})
+set(CMAKE_SHARED_LIBRARY_SUFFIX ${shared_lib_suffix})
+set(CMAKE_STATIC_LIBRARY_PREFIX ${static_lib_prefix})
+set(CMAKE_STATIC_LIBRARY_SUFFIX ${static_lib_suffix})
+
+# Create "library" files to find in libdir.
+foreach(lib imm trns)
+  foreach(variant shared static)
+    file(WRITE "${LIB_DIR}/${${variant}_lib_prefix}${lib}${${variant}_lib_suffix}")
+  endforeach()
+endforeach()
+
+set(ENV{PKG_CONFIG_PATH}                "${PKGCONFIG_DIR}")
+pkg_check_modules(IMM REQUIRED imm)
+
+message(STATUS "IMM_LIBRARIES='${IMM_LIBRARIES}'")
+message(STATUS "IMM_LINK_LIBRARIES='${IMM_LINK_LIBRARIES}'")
+message(STATUS "IMM_LDFLAGS='${IMM_LDFLAGS}'")
+message(STATUS "IMM_STATIC_LIBRARIES='${IMM_STATIC_LIBRARIES}'")
+message(STATUS "IMM_STATIC_LINK_LIBRARIES='${IMM_STATIC_LINK_LIBRARIES}'")
+message(STATUS "IMM_STATIC_LDFLAGS='${IMM_STATIC_LDFLAGS}'")

+ 36 - 17
Tests/RunCMake/FindPkgConfig/FindPkgConfig_extract_frameworks_target.cmake

@@ -1,3 +1,5 @@
+cmake_minimum_required(VERSION 3.17)
+
 # Prepare environment to reuse bletch.pc
 file(TO_NATIVE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/pc-bletch/lib/pkgconfig" PC_PATH)
 if(UNIX)
@@ -6,24 +8,41 @@ endif()
 set(ENV{PKG_CONFIG_PATH} "${PC_PATH}")
 
 find_package(PkgConfig REQUIRED)
-pkg_check_modules(Bletch IMPORTED_TARGET REQUIRED bletch-framework)
-
-if (Bletch_LDFLAGS_OTHER)
-  message(SEND_ERROR "Bletch_LDFLAGS_OTHER should be empty, but is '${Bletch_LDFLAGS_OTHER}'")
-endif ()
 
-if (NOT Bletch_LINK_LIBRARIES STREQUAL "-framework foo;-framework bar;bletch;-framework baz")
-  message(SEND_ERROR "Bletch_LINK_LIBRARIES has wrong value '${Bletch_LINK_LIBRARIES}'")
-endif ()
+# to test multiple variations, we must pick unique prefix names (same-named targets are cached for re-use)
+set(prefix_uniquifiers 0 1)
+# whether to apply STATIC_TARGET argument
+set(static_target_args "" STATIC_TARGET)
+# whether target properties are populated from the unqualified (i.e. shared library) series of vars, or the STATIC_ series of vars
+set(target_var_qualifiers "" STATIC_)
+foreach (prefix_uniquifier static_target_arg target_var_qualifier IN ZIP_LISTS prefix_uniquifiers static_target_args target_var_qualifiers)
+  set(prefix "Bletch${prefix_uniquifier}")
+  set(tgt "PkgConfig::${prefix}")
+  pkg_check_modules(${prefix} IMPORTED_TARGET REQUIRED ${static_target_arg} bletch-framework)
+  foreach (prop IN ITEMS INTERFACE_INCLUDE_DIRECTORIES INTERFACE_LINK_OPTIONS INTERFACE_COMPILE_OPTIONS)
+    get_target_property(prop_value ${tgt} ${prop})
+    if (prop_value)
+      message(SEND_ERROR "target property ${prop} should not be set, but is '${prop_value}'")
+    endif ()
+  endforeach ()
 
-foreach (prop IN ITEMS INTERFACE_INCLUDE_DIRECTORIES INTERFACE_LINK_OPTIONS INTERFACE_COMPILE_OPTIONS)
-  get_target_property(prop_value PkgConfig::Bletch ${prop})
-  if (prop_value)
-    message(SEND_ERROR "target property ${prop} should not be set, but is '${prop_value}'")
+  # there is 1 target yet 2 series of variables.
+  # if STATIC_TARGET is set, then the target will follow the STATIC_ qualified series of variables
+  # (otherwise will follow the unqualified series of variables).
+  get_target_property(prop_value ${tgt} INTERFACE_LINK_LIBRARIES)
+  if (NOT prop_value STREQUAL ${prefix}_${target_var_qualifier}LINK_LIBRARIES)
+    message(SEND_ERROR "target property INTERFACE_LINK_LIBRARIES has wrong value '${prop_value}'")
   endif ()
-endforeach ()
 
-get_target_property(prop_value PkgConfig::Bletch INTERFACE_LINK_LIBRARIES)
-if (NOT prop_value STREQUAL Bletch_LINK_LIBRARIES)
-  message(SEND_ERROR "target property INTERFACE_LINK_LIBRARIES has wrong value '${prop_value}'")
-endif ()
+  foreach (var_qualifier IN ITEMS "" STATIC_)
+    set (ldflags_var ${prefix}_${var_qualifier}LDFLAGS_OTHER)
+    if (${ldflags_var})
+      message(SEND_ERROR "${ldflags_var} should be empty, but is '${${ldflags_var}}'")
+    endif ()
+
+    set (linklibs_var ${prefix}_${var_qualifier}LINK_LIBRARIES)
+    if (NOT ${linklibs_var} STREQUAL "-framework foo;-framework bar;bletch;-framework baz")
+      message(SEND_ERROR "${linklibs_var} has wrong value '${${linklibs_var}}'")
+    endif ()
+  endforeach()
+endforeach()

+ 1 - 0
Tests/RunCMake/FindPkgConfig/RunCMakeTest.cmake

@@ -29,6 +29,7 @@ if (PKG_CONFIG_FOUND)
   run_cmake(FindPkgConfig_GET_VARIABLE_PKGCONFIG_PATH)
   run_cmake(FindPkgConfig_cache_variables)
   run_cmake(FindPkgConfig_IMPORTED_TARGET)
+  run_cmake(FindPkgConfig_LINK_LIBRARIES)
   run_cmake(FindPkgConfig_VERSION_OPERATORS)
   run_cmake(FindPkgConfig_GET_MATCHING_MODULE_NAME)
   run_cmake(FindPkgConfig_empty_target)