فهرست منبع

FindPython: enforce artifacts consistency when cross-compiling

Fixes: #26696
Marc Chevrier 8 ماه پیش
والد
کامیت
5c1521c54d

+ 1 - 0
Help/manual/cmake-policies.7.rst

@@ -98,6 +98,7 @@ Policies Introduced by CMake 4.1
 .. toctree::
 .. toctree::
    :maxdepth: 1
    :maxdepth: 1
 
 
+   CMP0190: FindPython enforce consistency in cross-compiling mode. </policy/CMP0190>
    CMP0189: TARGET_PROPERTY evaluates LINK_LIBRARIES properties transitively. </policy/CMP0189>
    CMP0189: TARGET_PROPERTY evaluates LINK_LIBRARIES properties transitively. </policy/CMP0189>
    CMP0188: The FindGCCXML module is removed. </policy/CMP0188>
    CMP0188: The FindGCCXML module is removed. </policy/CMP0188>
    CMP0187: Include source file without an extension after the same name with an extension. </policy/CMP0187>
    CMP0187: Include source file without an extension after the same name with an extension. </policy/CMP0187>

+ 31 - 0
Help/policy/CMP0190.rst

@@ -0,0 +1,31 @@
+CMP0190
+-------
+
+.. versionadded:: 4.1
+
+Modules :module:`FindPython3`, :module:`FindPython2` and :module:`FindPython`
+enforce consistency of artifacts in cross-compiling mode.
+
+Starting with CMake 4.1, Modules :module:`FindPython3`, :module:`FindPython2`
+and :module:`FindPython` apply, in cross-compiling mode (i.e. the
+:variable:`CMAKE_CROSSCOMPILING` variable is set to true), the following
+constraints to the requested components:
+
+* ``Interpreter`` or ``Compiler`` alone: the host artifacts will be searched.
+* ``Interpreter`` or ``Compiler`` with ``Development`` or any sub-component:
+  The target artifacts will be searched. In this case, the
+  :variable:`CMAKE_CROSSCOMPILING_EMULATOR` variable must be defined and will
+  be used to execute the interpreter or the compiler.
+
+This policy provides compatibility with projects that expect the legacy
+behavior.
+
+The ``OLD`` behavior for this policy does not enforce consistency in
+cross-compiling mode and will, potentially, returns a mix of artifacts
+(i.e. host and target artifacts).
+
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 4.1
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
+.. include:: STANDARD_ADVICE.txt
+
+.. include:: DEPRECATED.txt

+ 6 - 0
Help/release/dev/FindPython-crosscompiling-consistency.rst

@@ -0,0 +1,6 @@
+FindPython-crosscompiling-consistency
+-------------------------------------
+
+* Modules :module:`FindPython3`, :module:`FindPython2` and :module:`FindPython`
+  enforce consistency of artifacts in cross-compiling mode. This prevent mixing
+  host and target artifacts. See policy :policy:`CMP0190` for more information.

+ 15 - 0
Modules/FindPython.cmake

@@ -48,6 +48,21 @@ If no ``COMPONENTS`` are specified, ``Interpreter`` is assumed.
 If component ``Development`` is specified, it implies sub-components
 If component ``Development`` is specified, it implies sub-components
 ``Development.Module`` and ``Development.Embed``.
 ``Development.Module`` and ``Development.Embed``.
 
 
+.. versionchanged:: 4.1
+  In a cross-compiling mode (i.e. the :variable:`CMAKE_CROSSCOMPILING` variable
+  is defined to true), the following constraints, when the policy
+  :policy:`CMP0190` is set to ``NEW``, now apply to the requested components:
+
+  * ``Interpreter`` or ``Compiler`` alone: the host artifacts will be searched.
+  * ``Interpreter`` or ``Compiler`` with ``Development`` or any sub-component:
+    The target artifacts will be searched. In this case, the
+    :variable:`CMAKE_CROSSCOMPILING_EMULATOR` variable must be defined and will
+    be used to execute the interpreter or the compiler.
+
+  When both host and target artifacts are needed, two different calls to the
+  :command:`find_package` command should be done. The
+  ``Python_ARTIFACTS_PREFIX`` variable can be helpful in this situation.
+
 To ensure consistent versions between components ``Interpreter``, ``Compiler``,
 To ensure consistent versions between components ``Interpreter``, ``Compiler``,
 ``Development`` (or one of its sub-components) and ``NumPy``, specify all
 ``Development`` (or one of its sub-components) and ``NumPy``, specify all
 components at the same time:
 components at the same time:

+ 39 - 7
Modules/FindPython/Support.cmake

@@ -39,13 +39,17 @@ include(FindPackageHandleStandardArgs)
 # helper commands
 # helper commands
 #
 #
 macro (_PYTHON_DISPLAY_FAILURE _PYTHON_MSG)
 macro (_PYTHON_DISPLAY_FAILURE _PYTHON_MSG)
-  if (${_PYTHON_BASE}_FIND_REQUIRED)
+  if (${ARGC} GREATER 1 AND "${ARGV1}" STREQUAL "FATAL")
+    set (_${_PYTHON_PREFIX}_FATAL TRUE)
+  endif()
+  if (${_PYTHON_BASE}_FIND_REQUIRED OR _${_PYTHON_PREFIX}_FATAL)
     message (FATAL_ERROR "${_PYTHON_MSG}")
     message (FATAL_ERROR "${_PYTHON_MSG}")
   else()
   else()
     if (NOT ${_PYTHON_BASE}_FIND_QUIETLY)
     if (NOT ${_PYTHON_BASE}_FIND_QUIETLY)
       message(STATUS "${_PYTHON_MSG}")
       message(STATUS "${_PYTHON_MSG}")
     endif ()
     endif ()
   endif()
   endif()
+  unset(_${_PYTHON_PREFIX}_FATAL)
 
 
   set (${_PYTHON_BASE}_FOUND FALSE)
   set (${_PYTHON_BASE}_FOUND FALSE)
   set (${_PYTHON_PREFIX}_FOUND FALSE)
   set (${_PYTHON_PREFIX}_FOUND FALSE)
@@ -528,7 +532,7 @@ function (_PYTHON_GET_CONFIG_VAR _PYTHON_PGCV_VALUE NAME)
   endif()
   endif()
 
 
   if ("Interpreter" IN_LIST ${_PYTHON_BASE}_FIND_COMPONENTS AND _${_PYTHON_PREFIX}_EXECUTABLE
   if ("Interpreter" IN_LIST ${_PYTHON_BASE}_FIND_COMPONENTS AND _${_PYTHON_PREFIX}_EXECUTABLE
-      AND NOT CMAKE_CROSSCOMPILING)
+      AND (_${_PYTHON_PREFIX}_CROSSCOMPILING OR NOT CMAKE_CROSSCOMPILING))
     if (NAME STREQUAL "PREFIX")
     if (NAME STREQUAL "PREFIX")
       execute_process (COMMAND ${_${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys\ntry:\n   import sysconfig\n   sys.stdout.write(';'.join([sysconfig.get_config_var('base') or '', sysconfig.get_config_var('installed_base') or '']))\nexcept Exception:\n   from distutils import sysconfig\n   sys.stdout.write(';'.join([sysconfig.PREFIX,sysconfig.EXEC_PREFIX,sysconfig.BASE_EXEC_PREFIX]))"
       execute_process (COMMAND ${_${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys\ntry:\n   import sysconfig\n   sys.stdout.write(';'.join([sysconfig.get_config_var('base') or '', sysconfig.get_config_var('installed_base') or '']))\nexcept Exception:\n   from distutils import sysconfig\n   sys.stdout.write(';'.join([sysconfig.PREFIX,sysconfig.EXEC_PREFIX,sysconfig.BASE_EXEC_PREFIX]))"
                        RESULT_VARIABLE _result
                        RESULT_VARIABLE _result
@@ -846,6 +850,11 @@ function (_PYTHON_GET_LAUNCHER _PYTHON_PGL_NAME)
     return()
     return()
   endif()
   endif()
 
 
+  if (_${_PYTHON_PREFIX}_CROSSCOMPILING)
+    set (${_PYTHON_PGL_NAME} "${CMAKE_CROSSCOMPILING_EMULATOR}" PARENT_SCOPE)
+    return()
+  endif()
+
   if ("IronPython" IN_LIST _${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS
   if ("IronPython" IN_LIST _${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS
       AND NOT SYSTEM_NAME MATCHES "Windows|Linux")
       AND NOT SYSTEM_NAME MATCHES "Windows|Linux")
     if (_PGL_INTERPRETER)
     if (_PGL_INTERPRETER)
@@ -1001,7 +1010,7 @@ function (_PYTHON_VALIDATE_INTERPRETER)
   if (CMAKE_SIZEOF_VOID_P AND ("Development.Module" IN_LIST ${_PYTHON_BASE}_FIND_COMPONENTS
   if (CMAKE_SIZEOF_VOID_P AND ("Development.Module" IN_LIST ${_PYTHON_BASE}_FIND_COMPONENTS
         OR "Development.SABIModule" IN_LIST ${_PYTHON_BASE}_FIND_COMPONENTS
         OR "Development.SABIModule" IN_LIST ${_PYTHON_BASE}_FIND_COMPONENTS
         OR "Development.Embed" IN_LIST ${_PYTHON_BASE}_FIND_COMPONENTS)
         OR "Development.Embed" IN_LIST ${_PYTHON_BASE}_FIND_COMPONENTS)
-      AND NOT CMAKE_CROSSCOMPILING)
+      AND (_${_PYTHON_PREFIX}_CROSSCOMPILING OR NOT CMAKE_CROSSCOMPILING))
     # In this case, interpreter must have same architecture as environment
     # In this case, interpreter must have same architecture as environment
     execute_process (COMMAND ${_${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c
     execute_process (COMMAND ${_${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c
                              "import sys, struct; sys.stdout.write(str(struct.calcsize(\"P\")))"
                              "import sys, struct; sys.stdout.write(str(struct.calcsize(\"P\")))"
@@ -1440,6 +1449,22 @@ if (${_PYTHON_BASE}_FIND_REQUIRED_Development)
   set (${_PYTHON_BASE}_FIND_REQUIRED_Development.Embed TRUE)
   set (${_PYTHON_BASE}_FIND_REQUIRED_Development.Embed TRUE)
 endif()
 endif()
 
 
+## handle cross-compiling constraints for components:
+##  If Interpreter and/or Compiler are specified with Development components
+##  the CMAKE_CROSSCOMPILING_EMULATOR variable should be defined
+cmake_policy (GET CMP0190 _${_PYTHON_PREFIX}_CROSSCOMPILING_POLICY)
+unset (_${_PYTHON_PREFIX}_CROSSCOMPILING)
+if (CMAKE_CROSSCOMPILING AND _${_PYTHON_PREFIX}_CROSSCOMPILING_POLICY STREQUAL "NEW")
+  if (${_PYTHON_BASE}_FIND_COMPONENTS MATCHES "Interpreter|Compiler"
+      AND ${_PYTHON_BASE}_FIND_COMPONENTS MATCHES "Development")
+    if (CMAKE_CROSSCOMPILING_EMULATOR)
+      set (_${_PYTHON_PREFIX}_CROSSCOMPILING TRUE)
+    else()
+      _python_display_failure ("${_PYTHON_PREFIX}: When cross-compiling, Interpreter and/or Compiler components cannot be searched when CMAKE_CROSSCOMPILING_EMULATOR variable is not specified (see policy CMP0190)." FATAL)
+    endif()
+  endif()
+endif()
+
 unset (_${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS)
 unset (_${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS)
 unset (_${_PYTHON_PREFIX}_FIND_DEVELOPMENT_MODULE_ARTIFACTS)
 unset (_${_PYTHON_PREFIX}_FIND_DEVELOPMENT_MODULE_ARTIFACTS)
 unset (_${_PYTHON_PREFIX}_FIND_DEVELOPMENT_SABIMODULE_ARTIFACTS)
 unset (_${_PYTHON_PREFIX}_FIND_DEVELOPMENT_SABIMODULE_ARTIFACTS)
@@ -2827,7 +2852,8 @@ if (("Development.Module" IN_LIST ${_PYTHON_BASE}_FIND_COMPONENTS
     # if python interpreter is found, use it to look-up for artifacts
     # if python interpreter is found, use it to look-up for artifacts
     # to ensure consistency between interpreter and development environments.
     # to ensure consistency between interpreter and development environments.
     # If not, try to locate a compatible config tool
     # If not, try to locate a compatible config tool
-    if ((NOT ${_PYTHON_PREFIX}_Interpreter_FOUND OR CMAKE_CROSSCOMPILING)
+    if ((NOT ${_PYTHON_PREFIX}_Interpreter_FOUND
+          OR (NOT _${_PYTHON_PREFIX}_CROSSCOMPILING AND CMAKE_CROSSCOMPILING))
         AND "CPython" IN_LIST _${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS)
         AND "CPython" IN_LIST _${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS)
       set (_${_PYTHON_PREFIX}_HINTS "${${_PYTHON_PREFIX}_ROOT_DIR}" ENV ${_PYTHON_PREFIX}_ROOT_DIR)
       set (_${_PYTHON_PREFIX}_HINTS "${${_PYTHON_PREFIX}_ROOT_DIR}" ENV ${_PYTHON_PREFIX}_ROOT_DIR)
       unset (_${_PYTHON_PREFIX}_VIRTUALENV_PATHS)
       unset (_${_PYTHON_PREFIX}_VIRTUALENV_PATHS)
@@ -3093,7 +3119,9 @@ if (("Development.Module" IN_LIST ${_PYTHON_BASE}_FIND_COMPONENTS
 
 
   if ("LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS)
   if ("LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS)
     if (NOT _${_PYTHON_PREFIX}_LIBRARY_RELEASE)
     if (NOT _${_PYTHON_PREFIX}_LIBRARY_RELEASE)
-      if ((${_PYTHON_PREFIX}_Interpreter_FOUND AND NOT CMAKE_CROSSCOMPILING) OR _${_PYTHON_PREFIX}_CONFIG)
+      if ((${_PYTHON_PREFIX}_Interpreter_FOUND
+            AND (_${_PYTHON_PREFIX}_CROSSCOMPILING OR NOT CMAKE_CROSSCOMPILING))
+          OR _${_PYTHON_PREFIX}_CONFIG)
         # retrieve root install directory
         # retrieve root install directory
         _python_get_config_var (_${_PYTHON_PREFIX}_PREFIX PREFIX)
         _python_get_config_var (_${_PYTHON_PREFIX}_PREFIX PREFIX)
 
 
@@ -3361,7 +3389,9 @@ if (("Development.Module" IN_LIST ${_PYTHON_BASE}_FIND_COMPONENTS
           HINTS "${_${_PYTHON_PREFIX}_PATH}" ${_${_PYTHON_PREFIX}_HINTS}
           HINTS "${_${_PYTHON_PREFIX}_PATH}" ${_${_PYTHON_PREFIX}_HINTS}
           NO_DEFAULT_PATH)
           NO_DEFAULT_PATH)
       else()
       else()
-        if ((${_PYTHON_PREFIX}_Interpreter_FOUND AND NOT CMAKE_CROSSCOMPILING) OR _${_PYTHON_PREFIX}_CONFIG)
+        if ((${_PYTHON_PREFIX}_Interpreter_FOUND
+              AND (_${_PYTHON_PREFIX}_CROSSCOMPILING OR NOT CMAKE_CROSSCOMPILING))
+            OR _${_PYTHON_PREFIX}_CONFIG)
           # retrieve root install directory
           # retrieve root install directory
           _python_get_config_var (_${_PYTHON_PREFIX}_PREFIX PREFIX)
           _python_get_config_var (_${_PYTHON_PREFIX}_PREFIX PREFIX)
 
 
@@ -3622,7 +3652,9 @@ if (("Development.Module" IN_LIST ${_PYTHON_BASE}_FIND_COMPONENTS
         break()
         break()
       endif()
       endif()
 
 
-      if ((${_PYTHON_PREFIX}_Interpreter_FOUND AND NOT CMAKE_CROSSCOMPILING) OR _${_PYTHON_PREFIX}_CONFIG)
+      if ((${_PYTHON_PREFIX}_Interpreter_FOUND
+            AND (_${_PYTHON_PREFIX}_CROSSCOMPILING OR NOT CMAKE_CROSSCOMPILING))
+          OR _${_PYTHON_PREFIX}_CONFIG)
         _python_get_config_var (_${_PYTHON_PREFIX}_INCLUDE_DIRS INCLUDES)
         _python_get_config_var (_${_PYTHON_PREFIX}_INCLUDE_DIRS INCLUDES)
 
 
         find_path (_${_PYTHON_PREFIX}_INCLUDE_DIR
         find_path (_${_PYTHON_PREFIX}_INCLUDE_DIR

+ 15 - 0
Modules/FindPython2.cmake

@@ -41,6 +41,21 @@ If no ``COMPONENTS`` are specified, ``Interpreter`` is assumed.
 If component ``Development`` is specified, it implies sub-components
 If component ``Development`` is specified, it implies sub-components
 ``Development.Module`` and ``Development.Embed``.
 ``Development.Module`` and ``Development.Embed``.
 
 
+.. versionchanged:: 4.1
+  In a cross-compiling mode (i.e. the :variable:`CMAKE_CROSSCOMPILING` variable
+  is defined to true), the following constraints, when the policy
+  :policy:`CMP0190` is set to ``NEW``, now apply to the requested components:
+
+  * ``Interpreter`` or ``Compiler`` alone: the host artifacts will be searched.
+  * ``Interpreter`` or ``Compiler`` with ``Development`` or any sub-component:
+    The target artifacts will be searched. In this case, the
+    :variable:`CMAKE_CROSSCOMPILING_EMULATOR` variable must be defined and will
+    be used to execute the interpreter or the compiler.
+
+  When both host and target artifacts are needed, two different calls to the
+  :command:`find_package` command should be done. The
+  ``Python_ARTIFACTS_PREFIX`` variable can be helpful in this situation.
+
 To ensure consistent versions between components ``Interpreter``, ``Compiler``,
 To ensure consistent versions between components ``Interpreter``, ``Compiler``,
 ``Development`` (or one of its sub-components) and ``NumPy``, specify all
 ``Development`` (or one of its sub-components) and ``NumPy``, specify all
 components at the same time:
 components at the same time:

+ 16 - 0
Modules/FindPython3.cmake

@@ -48,6 +48,22 @@ If no ``COMPONENTS`` are specified, ``Interpreter`` is assumed.
 If component ``Development`` is specified, it implies sub-components
 If component ``Development`` is specified, it implies sub-components
 ``Development.Module`` and ``Development.Embed``.
 ``Development.Module`` and ``Development.Embed``.
 
 
+.. versionchanged:: 4.1
+
+  In a cross-compiling mode (i.e. the :variable:`CMAKE_CROSSCOMPILING` variable
+  is defined to true), the following constraints, when the policy
+  :policy:`CMP0190` is set to ``NEW``, now apply to the requested components:
+
+  * ``Interpreter`` or ``Compiler`` alone: the host artifacts will be searched.
+  * ``Interpreter`` or ``Compiler`` with ``Development`` or any sub-component:
+    The target artifacts will be searched. In this case, the
+    :variable:`CMAKE_CROSSCOMPILING_EMULATOR` variable must be defined and will
+    be used to execute the interpreter or the compiler.
+
+  When both host and target artifacts are needed, two different calls to the
+  :command:`find_package` command should be done. The
+  ``Python_ARTIFACTS_PREFIX`` variable can be helpful in this situation.
+
 To ensure consistent versions between components ``Interpreter``, ``Compiler``,
 To ensure consistent versions between components ``Interpreter``, ``Compiler``,
 ``Development`` (or one of its sub-components) and ``NumPy``, specify all
 ``Development`` (or one of its sub-components) and ``NumPy``, specify all
 components at the same time:
 components at the same time:

+ 4 - 1
Source/cmPolicies.h

@@ -566,7 +566,10 @@ class cmMakefile;
   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,                                                     \
   SELECT(POLICY, CMP0189,                                                     \
          "TARGET_PROPERTY evaluates LINK_LIBRARIES properties transitively.", \
          "TARGET_PROPERTY evaluates LINK_LIBRARIES properties transitively.", \
-         4, 1, 0, WARN)
+         4, 1, 0, WARN)                                                       \
+  SELECT(POLICY, CMP0190,                                                     \
+         "FindPython enforce consistency in cross-compiling mode.", 4, 1, 0,  \
+         WARN)
 
 
 #define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1)
 #define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1)
 #define CM_FOR_EACH_POLICY_ID(POLICY)                                         \
 #define CM_FOR_EACH_POLICY_ID(POLICY)                                         \

+ 4 - 4
Tests/RunCMake/CMakeLists.txt

@@ -423,10 +423,10 @@ endif()
 add_RunCMake_test(ConfigDir)
 add_RunCMake_test(ConfigDir)
 if(CMake_TEST_FindPython2)
 if(CMake_TEST_FindPython2)
   add_RunCMake_test(FindPython2.CPython TEST_DIR FindPython
   add_RunCMake_test(FindPython2.CPython TEST_DIR FindPython
-                                -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME}
-                                "-DRunCMake_BUILD_TYPE=${CMAKE_BUILD_TYPE}"
-                                -DCMake_TEST_FindPython2_CPython=TRUE
-                                -DCMake_TEST_FindPython2=TRUE)
+                                        -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME}
+                                        "-DRunCMake_BUILD_TYPE=${CMAKE_BUILD_TYPE}"
+                                        -DCMake_TEST_FindPython2_CPython=TRUE
+                                        -DCMake_TEST_FindPython2=TRUE)
   set_property(TEST RunCMake.FindPython2.CPython PROPERTY LABELS Python2)
   set_property(TEST RunCMake.FindPython2.CPython PROPERTY LABELS Python2)
 endif()
 endif()
 if(CMake_TEST_FindPython3)
 if(CMake_TEST_FindPython3)

+ 1 - 1
Tests/RunCMake/FindPython/CMakeLists.txt

@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.12...4.0)
+cmake_minimum_required(VERSION 3.20...4.1)
 project(${RunCMake_TEST} NONE)
 project(${RunCMake_TEST} NONE)
 include(${RunCMake_TEST}.cmake)
 include(${RunCMake_TEST}.cmake)

+ 33 - 0
Tests/RunCMake/FindPython/CrossCompiling-BOTH.cmake

@@ -0,0 +1,33 @@
+cmake_policy(SET CMP0190 NEW)
+
+enable_language(C)
+
+
+# Search for host Interpreter
+set(${PYTHON}_ARTIFACTS_PREFIX "_HOST")
+
+find_package(${PYTHON} ${Python_REQUESTED_VERSION} REQUIRED COMPONENTS Interpreter)
+
+unset(${PYTHON}_ARTIFACTS_PREFIX)
+
+
+# Search cross-compilation artifacts
+## First, built an pseudo-emulator
+set(PSEUDO_EMULATOR_DIR "${CMAKE_CURRENT_BINARY_DIR}/pseudo_emulator")
+
+file(MAKE_DIRECTORY "${PSEUDO_EMULATOR_DIR}")
+
+execute_process(COMMAND "${CMAKE_COMMAND}" -G "${CMAKE_GENERATOR}" -DCMAKE_BUILD_TYPE=Release
+                                           -S "${CMAKE_CURRENT_SOURCE_DIR}/pseudo_emulator"
+                                           -B "${PSEUDO_EMULATOR_DIR}"
+                COMMAND_ERROR_IS_FATAL ANY)
+
+execute_process(COMMAND "${CMAKE_COMMAND}" --build "${PSEUDO_EMULATOR_DIR}"
+                COMMAND_ERROR_IS_FATAL ANY)
+
+## Now, configure this pseudo-emulator
+set(CMAKE_CROSSCOMPILING TRUE)
+set(CMAKE_CROSSCOMPILING_EMULATOR "${PSEUDO_EMULATOR_DIR}/pseudo_emulator")
+
+
+find_package(${PYTHON} ${Python_REQUESTED_VERSION} REQUIRED COMPONENTS Interpreter Development)

+ 4 - 0
Tests/RunCMake/FindPython/CrossCompiling-CMP0190-NEW.Python.V2-stderr.txt

@@ -0,0 +1,4 @@
+CMake Error at .+/Support.cmake:[0-9]+ \(message\):
+  Python: When cross-compiling, Interpreter and/or Compiler components cannot
+  be searched when CMAKE_CROSSCOMPILING_EMULATOR variable is not specified
+  \(see policy CMP0190\).

+ 4 - 0
Tests/RunCMake/FindPython/CrossCompiling-CMP0190-NEW.Python.V3-stderr.txt

@@ -0,0 +1,4 @@
+CMake Error at .+/Support.cmake:[0-9]+ \(message\):
+  Python: When cross-compiling, Interpreter and/or Compiler components cannot
+  be searched when CMAKE_CROSSCOMPILING_EMULATOR variable is not specified
+  \(see policy CMP0190\).

+ 4 - 0
Tests/RunCMake/FindPython/CrossCompiling-CMP0190-NEW.Python2-stderr.txt

@@ -0,0 +1,4 @@
+CMake Error at .+/Support.cmake:[0-9]+ \(message\):
+  Python2: When cross-compiling, Interpreter and/or Compiler components
+  cannot be searched when CMAKE_CROSSCOMPILING_EMULATOR variable is not
+  specified \(see policy CMP0190\).

+ 4 - 0
Tests/RunCMake/FindPython/CrossCompiling-CMP0190-NEW.Python3-stderr.txt

@@ -0,0 +1,4 @@
+CMake Error at .+/Support.cmake:[0-9]+ \(message\):
+  Python3: When cross-compiling, Interpreter and/or Compiler components
+  cannot be searched when CMAKE_CROSSCOMPILING_EMULATOR variable is not
+  specified \(see policy CMP0190\).

+ 4 - 0
Tests/RunCMake/FindPython/CrossCompiling-CMP0190-NEW.cmake

@@ -0,0 +1,4 @@
+
+cmake_policy(SET CMP0190 NEW)
+
+include(CrossCompiling-CMP0190.cmake)

+ 4 - 0
Tests/RunCMake/FindPython/CrossCompiling-CMP0190-OLD.cmake

@@ -0,0 +1,4 @@
+
+cmake_policy(SET CMP0190 OLD)
+
+include(CrossCompiling-CMP0190.cmake)

+ 6 - 0
Tests/RunCMake/FindPython/CrossCompiling-CMP0190.cmake

@@ -0,0 +1,6 @@
+set(CMAKE_CROSSCOMPILING TRUE)
+unset(CMAKE_CROSSCOMPILING_EMULATOR)
+
+enable_language(C)
+
+find_package(${PYTHON} ${Python_REQUESTED_VERSION} REQUIRED COMPONENTS Interpreter Development)

+ 8 - 0
Tests/RunCMake/FindPython/CrossCompiling-HOST.cmake

@@ -0,0 +1,8 @@
+cmake_policy(SET CMP0190 NEW)
+
+set(CMAKE_CROSSCOMPILING TRUE)
+set(CMAKE_CROSSCOMPILING_EMULATOR "${CMAKE_COMMAND}" -P raise-error.cmake)
+
+enable_language(C)
+
+find_package(${PYTHON} ${Python_REQUESTED_VERSION} REQUIRED COMPONENTS Interpreter)

+ 23 - 0
Tests/RunCMake/FindPython/CrossCompiling-TARGET.cmake

@@ -0,0 +1,23 @@
+cmake_policy(SET CMP0190 NEW)
+
+enable_language(C)
+
+## First, built an pseudo-emulator
+set(PSEUDO_EMULATOR_DIR "${CMAKE_CURRENT_BINARY_DIR}/pseudo_emulator")
+
+file(MAKE_DIRECTORY "${PSEUDO_EMULATOR_DIR}")
+
+execute_process(COMMAND "${CMAKE_COMMAND}" -G "${CMAKE_GENERATOR}" -DCMAKE_BUILD_TYPE=Release
+                                           -S "${CMAKE_CURRENT_SOURCE_DIR}/pseudo_emulator"
+                                           -B "${PSEUDO_EMULATOR_DIR}"
+                COMMAND_ERROR_IS_FATAL ANY)
+
+execute_process(COMMAND "${CMAKE_COMMAND}" --build "${PSEUDO_EMULATOR_DIR}"
+                COMMAND_ERROR_IS_FATAL ANY)
+
+## Now, configure this pseudo-emulator
+set(CMAKE_CROSSCOMPILING TRUE)
+set(CMAKE_CROSSCOMPILING_EMULATOR "${PSEUDO_EMULATOR_DIR}/pseudo_emulator")
+
+
+find_package(${PYTHON} ${Python_REQUESTED_VERSION} REQUIRED COMPONENTS Interpreter Development)

+ 44 - 6
Tests/RunCMake/FindPython/RunCMakeTest.cmake

@@ -7,9 +7,10 @@ else()
 endif()
 endif()
 
 
 function(run_python test)
 function(run_python test)
+  set(options_args CHECK_RESULT)
   set(one_value_args TYPE ACTION VARIANT STRATEGY)
   set(one_value_args TYPE ACTION VARIANT STRATEGY)
   set(multi_value_args OPTIONS)
   set(multi_value_args OPTIONS)
-  cmake_parse_arguments(PARSE_ARGV 1 RP "" "${one_value_args}" "${multi_value_args}")
+  cmake_parse_arguments(PARSE_ARGV 1 RP "${options_args}" "${one_value_args}" "${multi_value_args}")
 
 
   if(RP_UNPARSED_ARGUMENTS)
   if(RP_UNPARSED_ARGUMENTS)
     message(FATAL_ERROR "run_python: unparsed arguments: ${RP_UNPARSED_ARGUMENTS}")
     message(FATAL_ERROR "run_python: unparsed arguments: ${RP_UNPARSED_ARGUMENTS}")
@@ -37,6 +38,11 @@ function(run_python test)
     list(APPEND options -DCMAKE_BUILD_TYPE=${Python_BUILD_TYPE})
     list(APPEND options -DCMAKE_BUILD_TYPE=${Python_BUILD_TYPE})
   endif()
   endif()
 
 
+  if(RP_CHECK_RESULT)
+    set(RunCMake_TEST_EXPECT_RESULT 1)
+    file(READ "${RunCMake_SOURCE_DIR}/${test_name}-stderr.txt" RunCMake_TEST_EXPECT_stderr)
+  endif()
+
   set(RunCMake_TEST_BINARY_DIR "${RunCMake_BINARY_DIR}/${test_name}-build")
   set(RunCMake_TEST_BINARY_DIR "${RunCMake_BINARY_DIR}/${test_name}-build")
   if(options)
   if(options)
     run_cmake_with_options(${test} ${options})
     run_cmake_with_options(${test} ${options})
@@ -121,11 +127,9 @@ function(required_artifacts)
   endif()
   endif()
 endfunction()
 endfunction()
 
 
-function(custom_failure_message_check name components)
-  set(RunCMake_TEST_EXPECT_RESULT 1)
-  file(READ "${RunCMake_SOURCE_DIR}/CustomFailureMessage.${name}-stderr.txt" RunCMake_TEST_EXPECT_stderr)
-  run_python(CustomFailureMessage VARIANT "${name}" OPTIONS "-DCHECK_COMPONENTS=${components}" ${ARGN})
-endfunction()
+macro(custom_failure_message_check name components)
+  run_python(CustomFailureMessage VARIANT "${name}" CHECK_RESULT OPTIONS "-DCHECK_COMPONENTS=${components}" ${ARGN})
+endmacro()
 
 
 
 
 if(CMake_TEST_FindPython2_CPython)
 if(CMake_TEST_FindPython2_CPython)
@@ -152,6 +156,23 @@ if(CMake_TEST_FindPython2_CPython)
                           OPTIONS -DPython_REQUESTED_VERSION=2)
                           OPTIONS -DPython_REQUESTED_VERSION=2)
   run_python(VersionRange TYPE Python STRATEGY VERSION VARIANT Python.V2
   run_python(VersionRange TYPE Python STRATEGY VERSION VARIANT Python.V2
                           OPTIONS -DPython_REQUESTED_VERSION=2)
                           OPTIONS -DPython_REQUESTED_VERSION=2)
+  run_python(CrossCompiling-CMP0190-OLD TYPE Python2 VARIANT Python2)
+  run_python(CrossCompiling-CMP0190-NEW TYPE Python2 VARIANT Python2 CHECK_RESULT)
+  run_python(CrossCompiling-CMP0190-OLD TYPE Python VARIANT Python.V2
+                                        OPTIONS -DPython_REQUESTED_VERSION=2)
+  run_python(CrossCompiling-CMP0190-NEW TYPE Python VARIANT Python.V2 CHECK_RESULT
+                                        OPTIONS -DPython_REQUESTED_VERSION=2)
+  run_python(CrossCompiling-HOST TYPE Python2 VARIANT Python2)
+  run_python(CrossCompiling-HOST TYPE Python VARIANT Python.V2
+                                 OPTIONS -DPython_REQUESTED_VERSION=2)
+  if(CMAKE_SYSTEM_NAME MATCHES "Linux|Darwin" AND NOT RunCMake_GENERATOR_IS_MULTI_CONFIG)
+    run_python(CrossCompiling-TARGET TYPE Python2 VARIANT Python2)
+    run_python(CrossCompiling-TARGET TYPE Python VARIANT Python.V2
+                                     OPTIONS -DPython_REQUESTED_VERSION=2)
+    run_python(CrossCompiling-BOTH TYPE Python2 VARIANT Python2)
+    run_python(CrossCompiling-BOTH TYPE Python VARIANT Python.V2
+                                   OPTIONS -DPython_REQUESTED_VERSION=2)
+  endif()
 endif()
 endif()
 
 
 if(CMake_TEST_FindPython3_CPython)
 if(CMake_TEST_FindPython3_CPython)
@@ -189,6 +210,23 @@ if(CMake_TEST_FindPython3_CPython)
   custom_failure_message_check("Include" "Development" -DPython3_INCLUDE_DIR=/not/found/include)
   custom_failure_message_check("Include" "Development" -DPython3_INCLUDE_DIR=/not/found/include)
   custom_failure_message_check("Multiple" "Interpreter:Development" -DPython3_EXECUTABLE=/not/found/interpreter
   custom_failure_message_check("Multiple" "Interpreter:Development" -DPython3_EXECUTABLE=/not/found/interpreter
                                                                     -DPython3_LIBRARY=/not/found/library)
                                                                     -DPython3_LIBRARY=/not/found/library)
+  run_python(CrossCompiling-CMP0190-OLD TYPE Python3 VARIANT Python3)
+  run_python(CrossCompiling-CMP0190-NEW TYPE Python3 VARIANT Python3 CHECK_RESULT)
+  run_python(CrossCompiling-CMP0190-OLD TYPE Python VARIANT Python.V3
+                                        OPTIONS -DPython_REQUESTED_VERSION=3)
+  run_python(CrossCompiling-CMP0190-NEW TYPE Python VARIANT Python.V3 CHECK_RESULT
+                                        OPTIONS -DPython_REQUESTED_VERSION=3)
+  run_python(CrossCompiling-HOST TYPE Python3 VARIANT Python3)
+  run_python(CrossCompiling-HOST TYPE Python VARIANT Python.V3
+                                 OPTIONS -DPython_REQUESTED_VERSION=3)
+  if(CMAKE_SYSTEM_NAME MATCHES "Linux|Darwin" AND NOT RunCMake_GENERATOR_IS_MULTI_CONFIG)
+    run_python(CrossCompiling-TARGET TYPE Python3 VARIANT Python3)
+    run_python(CrossCompiling-TARGET TYPE Python VARIANT Python.V3
+                                     OPTIONS -DPython_REQUESTED_VERSION=3)
+    run_python(CrossCompiling-BOTH TYPE Python3 VARIANT Python3)
+    run_python(CrossCompiling-BOTH TYPE Python VARIANT Python.V3
+                                        OPTIONS -DPython_REQUESTED_VERSION=3)
+  endif()
 endif()
 endif()
 
 
 if(CMake_TEST_FindPython2_CPython OR CMake_TEST_FindPython3_CPython)
 if(CMake_TEST_FindPython2_CPython OR CMake_TEST_FindPython3_CPython)

+ 5 - 0
Tests/RunCMake/FindPython/pseudo_emulator/CMakeLists.txt

@@ -0,0 +1,5 @@
+cmake_minimum_required(VERSION 3.22...4.1)
+
+project(PseudoEmulator LANGUAGES C)
+
+add_executable(pseudo_emulator pseudo_emulator.c)

+ 48 - 0
Tests/RunCMake/FindPython/pseudo_emulator/pseudo_emulator.c

@@ -0,0 +1,48 @@
+
+#include <stdio.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/wait.h>
+
+static int execute(char* argv[])
+{
+  pid_t my_pid;
+  int status, timeout;
+  struct timespec duration;
+  duration.tv_sec = 0;
+  duration.tv_nsec = 100000000;
+
+  if (0 == (my_pid = fork())) {
+    if (-1 == execve(argv[0], (char**)argv, NULL)) {
+      perror("child process execve failed");
+      return -1;
+    }
+  }
+
+  timeout = 100;
+  while (0 == waitpid(my_pid, &status, WNOHANG)) {
+    if (--timeout < 0) {
+      perror(argv[0]);
+      return -1;
+    }
+    nanosleep(&duration, NULL);
+  }
+
+  if (1 != WIFEXITED(status) || 0 != WEXITSTATUS(status)) {
+    return -1;
+  }
+
+  return 0;
+}
+
+int main(int argc, char* argv[])
+{
+  if (argc < 2) {
+    fprintf(stderr, "%s: require at least one argument.\n", argv[0]);
+    return -1;
+  }
+
+  return execute(&argv[1]);
+}

+ 2 - 0
Tests/RunCMake/FindPython/raise-error.cmake

@@ -0,0 +1,2 @@
+
+message(FATAL_ERROR "CROSSCOMPILING_EMULATOR erroneously used.")