From 35d3e00e4e9b138dec1d1a646766fc342d04b8f5 Mon Sep 17 00:00:00 2001
From: Christopher Degawa <ccom@randomderp.com>
Date: Fri, 11 Jun 2021 15:17:39 -0500
Subject: [PATCH] FindPkgConfig: split args if loaded from environment

It's common for some people to use the PKG_CONFIG environment variable
to not only load a custom pkg-config/pkgconf but also to load some
default arguments such as `--static` or `--keep-system-libs` which often
worked since shell scripts would call `$PKG_CONFIG --libs pkg` without
quotes, but this breaks FindPkgConfig since it uses the full string as
`argv[0]` and might try looking for a binary called `pkgconf --static`,
instead of looking for `pkgconf` and adding `--static` as the `argv[1]`

Additionally adds RunCMake.FindPkgConfig ARGN test case

Fixes: #22305
Signed-off-by: Christopher Degawa <ccom@randomderp.com>
---
 .../dev/FindPkgConfig-PKG_CONFIG-args.rst     |  5 +++
 Modules/FindPkgConfig.cmake                   | 39 ++++++++++++++++---
 .../FindPkgConfig_GET_MATCHING_ARGN.cmake     | 17 ++++++++
 .../RunCMake/FindPkgConfig/RunCMakeTest.cmake |  1 +
 .../FindPkgConfig/dummy-pkg-config.bat        | 17 ++++++++
 .../FindPkgConfig/dummy-pkg-config.sh         | 19 +++++++++
 6 files changed, 93 insertions(+), 5 deletions(-)
 create mode 100644 Help/release/dev/FindPkgConfig-PKG_CONFIG-args.rst
 create mode 100644 Tests/RunCMake/FindPkgConfig/FindPkgConfig_GET_MATCHING_ARGN.cmake

diff --git a/Help/release/dev/FindPkgConfig-PKG_CONFIG-args.rst b/Help/release/dev/FindPkgConfig-PKG_CONFIG-args.rst
new file mode 100644
index 0000000000..44c26b544f
--- /dev/null
+++ b/Help/release/dev/FindPkgConfig-PKG_CONFIG-args.rst
@@ -0,0 +1,5 @@
+FindPkgConfig-PKG_CONFIG-args
+-----------------------------
+
+* The :module:`FindPkgConfig` module gained a :variable:`PKG_CONFIG_ARGN`
+  variable to specify arguments to ``pkg-config`` calls.
diff --git a/Modules/FindPkgConfig.cmake b/Modules/FindPkgConfig.cmake
index 3bc9dba167..bd54fd9dbc 100644
--- a/Modules/FindPkgConfig.cmake
+++ b/Modules/FindPkgConfig.cmake
@@ -15,6 +15,8 @@ following variables will also be set:
   if pkg-config executable was found
 ``PKG_CONFIG_EXECUTABLE``
   pathname of the pkg-config program
+``PKG_CONFIG_ARGN``
+  list of arguments to pass to pkg-config
 ``PKG_CONFIG_VERSION_STRING``
   version of pkg-config (since CMake 2.8.8)
 
@@ -29,7 +31,15 @@ set(PKG_CONFIG_VERSION 1)
 
 # find pkg-config, use PKG_CONFIG if set
 if((NOT PKG_CONFIG_EXECUTABLE) AND (NOT "$ENV{PKG_CONFIG}" STREQUAL ""))
-  set(PKG_CONFIG_EXECUTABLE "$ENV{PKG_CONFIG}" CACHE FILEPATH "pkg-config executable")
+  separate_arguments(PKG_CONFIG_FROM_ENV_SPLIT NATIVE_COMMAND PROGRAM SEPARATE_ARGS "$ENV{PKG_CONFIG}")
+  list(LENGTH PKG_CONFIG_FROM_ENV_SPLIT PKG_CONFIG_FROM_ENV_SPLIT_ARGC)
+  if(PKG_CONFIG_FROM_ENV_SPLIT_ARGC GREATER 0)
+    list(GET PKG_CONFIG_FROM_ENV_SPLIT 0 PKG_CONFIG_FROM_ENV_ARGV0)
+    if(PKG_CONFIG_FROM_ENV_SPLIT_ARGC GREATER 1)
+      list(SUBLIST PKG_CONFIG_FROM_ENV_SPLIT 1 -1 PKG_CONFIG_ARGN)
+    endif()
+    set(PKG_CONFIG_EXECUTABLE "${PKG_CONFIG_FROM_ENV_ARGV0}" CACHE FILEPATH "pkg-config executable")
+  endif()
 endif()
 
 set(PKG_CONFIG_NAMES "pkg-config")
@@ -43,9 +53,12 @@ find_program(PKG_CONFIG_EXECUTABLE
   DOC "pkg-config executable")
 mark_as_advanced(PKG_CONFIG_EXECUTABLE)
 
+set(PKG_CONFIG_ARGN "${PKG_CONFIG_ARGN}" CACHE STRING "Arguments to supply to pkg-config")
+mark_as_advanced(PKG_CONFIG_ARGN)
+
 set(_PKG_CONFIG_FAILURE_MESSAGE "")
 if (PKG_CONFIG_EXECUTABLE)
-  execute_process(COMMAND ${PKG_CONFIG_EXECUTABLE} --version
+  execute_process(COMMAND ${PKG_CONFIG_EXECUTABLE} ${PKG_CONFIG_ARGN} --version
     OUTPUT_VARIABLE PKG_CONFIG_VERSION_STRING OUTPUT_STRIP_TRAILING_WHITESPACE
     ERROR_VARIABLE _PKG_CONFIG_VERSION_ERROR ERROR_STRIP_TRAILING_WHITESPACE
     RESULT_VARIABLE _PKG_CONFIG_VERSION_RESULT
@@ -53,14 +66,18 @@ if (PKG_CONFIG_EXECUTABLE)
 
   if (NOT _PKG_CONFIG_VERSION_RESULT EQUAL 0)
     string(REPLACE "\n" "\n    " _PKG_CONFIG_VERSION_ERROR "      ${_PKG_CONFIG_VERSION_ERROR}")
+    if(PKG_CONFIG_ARGN)
+      string(REPLACE ";" " " PKG_CONFIG_ARGN " ${PKG_CONFIG_ARGN}")
+    endif()
     string(APPEND _PKG_CONFIG_FAILURE_MESSAGE
       "The command\n"
-      "      \"${PKG_CONFIG_EXECUTABLE}\" --version\n"
+      "      \"${PKG_CONFIG_EXECUTABLE}\"${PKG_CONFIG_ARGN} --version\n"
       "    failed with output:\n${PKG_CONFIG_VERSION_STRING}\n"
       "    stderr: \n${_PKG_CONFIG_VERSION_ERROR}\n"
       "    result: \n${_PKG_CONFIG_VERSION_RESULT}"
       )
     set(PKG_CONFIG_EXECUTABLE "")
+    set(PKG_CONFIG_ARGN "")
     unset(PKG_CONFIG_VERSION_STRING)
   endif ()
   unset(_PKG_CONFIG_VERSION_RESULT)
@@ -91,7 +108,7 @@ macro(_pkgconfig_invoke _pkglist _prefix _varname _regexp)
   set(_pkgconfig_invoke_result)
 
   execute_process(
-    COMMAND ${PKG_CONFIG_EXECUTABLE} ${ARGN} ${_pkglist}
+    COMMAND ${PKG_CONFIG_EXECUTABLE} ${PKG_CONFIG_ARGN} ${ARGN} ${_pkglist}
     OUTPUT_VARIABLE _pkgconfig_invoke_result
     RESULT_VARIABLE _pkgconfig_failed
     OUTPUT_STRIP_TRAILING_WHITESPACE)
@@ -533,7 +550,7 @@ macro(_pkg_check_modules_internal _is_required _is_silent _no_cmake_path _no_cma
 
       # execute the query
       execute_process(
-        COMMAND ${PKG_CONFIG_EXECUTABLE} ${_pkg_check_modules_exist_query}
+        COMMAND ${PKG_CONFIG_EXECUTABLE} ${PKG_CONFIG_ARGN} ${_pkg_check_modules_exist_query}
         RESULT_VARIABLE _pkgconfig_retval
         ERROR_VARIABLE _pkgconfig_error
         ERROR_STRIP_TRAILING_WHITESPACE)
@@ -892,6 +909,18 @@ Variables Affecting Behavior
   .. versionadded:: 3.1
     The ``PKG_CONFIG`` environment variable can be used as a hint.
 
+.. variable:: PKG_CONFIG_ARGN
+
+  .. versionadded:: 3.22
+
+  This can be set to a list of arguments to additionally pass to pkg-config
+  if needed. If not provided, it will be an empty string, however, if the
+  environment variable ``PKG_CONFIG`` is provided, this will be set to the
+  result of splitting the variable.
+
+  The ``PKG_CONFIG`` environment variable can be used to provide both
+  ``PKG_CONFIG_EXECUTABLE`` and ``PKG_CONFIG_ARGN``
+
 .. variable:: PKG_CONFIG_USE_CMAKE_PREFIX_PATH
 
   .. versionadded:: 3.1
diff --git a/Tests/RunCMake/FindPkgConfig/FindPkgConfig_GET_MATCHING_ARGN.cmake b/Tests/RunCMake/FindPkgConfig/FindPkgConfig_GET_MATCHING_ARGN.cmake
new file mode 100644
index 0000000000..e49ff22f6b
--- /dev/null
+++ b/Tests/RunCMake/FindPkgConfig/FindPkgConfig_GET_MATCHING_ARGN.cmake
@@ -0,0 +1,17 @@
+if(WIN32)
+    set(ENV{PKG_CONFIG} "\"${CMAKE_CURRENT_SOURCE_DIR}\\dummy-pkg-config.bat\" --static --print-errors")
+else()
+    set(ENV{PKG_CONFIG} "\"${CMAKE_CURRENT_SOURCE_DIR}/dummy-pkg-config.sh\" --static --print-errors")
+endif()
+
+find_package(PkgConfig REQUIRED)
+
+if(NOT PKG_CONFIG_ARGN STREQUAL "--static;--print-errors")
+  message(SEND_ERROR "PKG_CONFIG_ARGN has wrong value '${PKG_CONFIG_ARGN}'")
+endif()
+
+_pkgconfig_invoke("none" "prefix" "output" "")
+
+if(NOT prefix_output STREQUAL "Received;--static;Received;--print-errors")
+  message(SEND_ERROR "prefix_output has wrong value '${prefix_output}'")
+endif()
diff --git a/Tests/RunCMake/FindPkgConfig/RunCMakeTest.cmake b/Tests/RunCMake/FindPkgConfig/RunCMakeTest.cmake
index e7f008ca53..17e046a2b8 100644
--- a/Tests/RunCMake/FindPkgConfig/RunCMakeTest.cmake
+++ b/Tests/RunCMake/FindPkgConfig/RunCMakeTest.cmake
@@ -12,6 +12,7 @@ run_cmake(FindPkgConfig_PKGCONFIG_PATH)
 run_cmake(FindPkgConfig_PKGCONFIG_PATH_NO_CMAKE_PATH)
 run_cmake(FindPkgConfig_PKGCONFIG_PATH_NO_CMAKE_ENVIRONMENT_PATH)
 run_cmake(FindPkgConfig_extract_frameworks)
+run_cmake(FindPkgConfig_GET_MATCHING_ARGN)
 
 if(APPLE)
   run_cmake(FindPkgConfig_extract_frameworks_target)
diff --git a/Tests/RunCMake/FindPkgConfig/dummy-pkg-config.bat b/Tests/RunCMake/FindPkgConfig/dummy-pkg-config.bat
index b03837030c..c91713b257 100755
--- a/Tests/RunCMake/FindPkgConfig/dummy-pkg-config.bat
+++ b/Tests/RunCMake/FindPkgConfig/dummy-pkg-config.bat
@@ -1,5 +1,10 @@
 @ECHO OFF
 
+rem variables to get around `--static --version` printing the received
+rem message and then version
+set static=false
+set print_errors=false
+
 :LOOP
 
 IF "%1"=="" (
@@ -21,7 +26,19 @@ IF "%1"=="--exists" (
     EXIT /B 0
   )
 )
+IF "%1"=="--static" (
+  set static=true
+)
+IF "%1"=="--print-errors" (
+  set print_errors=true
+)
 SHIFT
 IF NOT "%~1"=="" GOTO LOOP
 
+IF "%static%"=="true" ECHO Received --static
+IF "%print_errors%"=="true" ECHO Received --print-errors
+
+IF "%static%"=="true" GOTO :EOF
+IF "%print_errors%"=="true" GOTO :EOF
+
 EXIT /B 255
diff --git a/Tests/RunCMake/FindPkgConfig/dummy-pkg-config.sh b/Tests/RunCMake/FindPkgConfig/dummy-pkg-config.sh
index 56bba30dd4..4021bf7a6f 100755
--- a/Tests/RunCMake/FindPkgConfig/dummy-pkg-config.sh
+++ b/Tests/RunCMake/FindPkgConfig/dummy-pkg-config.sh
@@ -4,6 +4,11 @@
 # to the --exists argument with the PKG_CONFIG_PATH environment variable
 # and returns 1 if they are different.
 
+# variables to get around `--static --version` printing the received
+# message and then version
+static=false
+print_errors=false
+
 while [ $# -gt 0 ]; do
   case $1 in
     --version)
@@ -17,7 +22,21 @@ while [ $# -gt 0 ]; do
       echo "Found:    ${PKG_CONFIG_PATH}"
       [ "${last}" = "${PKG_CONFIG_PATH}" ] && exit 0 || exit 1
       ;;
+    --static)
+      static=true
+      ;;
+    --print-errors)
+      print_errors=true
+      ;;
   esac
   shift
 done
+
+$static && echo "Received --static"
+$print_errors && echo "Received --print-errors"
+
+if $static || $print_errors; then
+  exit 0
+fi
+
 exit 255
-- 
GitLab

