-
Notifications
You must be signed in to change notification settings - Fork 2.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(cmake): improved cross-compilation support #5083
Conversation
I think this is the right direction, but I'm curious, what would a tool like scikit-build-core set here to ensure the correct Python_SOABI is set? Can you just set Python_SOABI if not finding Interpreter? |
If scikit-build-core knows the desired extension suffix and ABI, it should set the Alternatively, to be less pybind11-specific, it could set On macOS and Linux, with sufficiently recent CMake and Python >=3.8, it doesn't need to set anything, simply pointing to the desired Python installation using IIRC, FindPython unconditionally unsets The way I currently approach this in py-build-cmake is as follows:
It would be nice if we could come up a more robust, standardized way, though :) |
I'd like to get this in for the next release, but I'm worried about potential regressions, especially for cross-compilation that currently work, like WebAssembly. So I think I'll make it opt-in with a variable like PYBIND11_CROSSCOMPILE for now, then we can see if we can remove the need for the variable in the future. |
14ad704
to
12159d5
Compare
I just did this too. The main differences: I used Thoughts? diff --git a/CMakeLists.txt b/CMakeLists.txt
index 18814d4d..3526a1a6 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -116,6 +116,7 @@ option(PYBIND11_NUMPY_1_ONLY
set(PYBIND11_INTERNALS_VERSION
""
CACHE STRING "Override the ABI version, may be used to enable the unstable ABI.")
+option(PYBIND11_USE_CROSSCOMPILING "Respect CMAKE_CROSSCOMPILING" OFF)
if(PYBIND11_DISABLE_HANDLE_TYPE_NAME_DEFAULT_IMPLEMENTATION)
add_compile_definitions(PYBIND11_DISABLE_HANDLE_TYPE_NAME_DEFAULT_IMPLEMENTATION)
diff --git a/tools/FindPythonLibsNew.cmake b/tools/FindPythonLibsNew.cmake
index 8275b9d5..0640a191 100644
--- a/tools/FindPythonLibsNew.cmake
+++ b/tools/FindPythonLibsNew.cmake
@@ -205,7 +205,7 @@ endif()
# Make sure the Python has the same pointer-size as the chosen compiler
# Skip if CMAKE_SIZEOF_VOID_P is not defined
# This should be skipped for (non-Apple) cross-compiles (like EMSCRIPTEN)
-if(NOT CMAKE_CROSSCOMPILING
+if(NOT _PYBIND11_CROSSCOMPILING
AND CMAKE_SIZEOF_VOID_P
AND (NOT "${PYTHON_SIZEOF_VOID_P}" STREQUAL "${CMAKE_SIZEOF_VOID_P}"))
if(PythonLibsNew_FIND_REQUIRED)
diff --git a/tools/pybind11Common.cmake b/tools/pybind11Common.cmake
index 06ee9253..d8e18e67 100644
--- a/tools/pybind11Common.cmake
+++ b/tools/pybind11Common.cmake
@@ -42,6 +42,16 @@ set(pybind11_INCLUDE_DIRS
"${pybind11_INCLUDE_DIR}"
CACHE INTERNAL "Include directory for pybind11 (Python not requested)")
+if(CMAKE_CROSSCOMPILING AND PYBIND11_USE_CROSSCOMPILING)
+ set(_PYBIND11_CROSSCOMPILING
+ ON
+ CACHE INTERNAL "")
+else()
+ set(_PYBIND11_CROSSCOMPILING
+ OFF
+ CACHE INTERNAL "")
+endif()
+
# --------------------- Shared targets ----------------------------
# Build an interface library target:
@@ -195,7 +205,7 @@ endif()
# --------------------- pybind11_find_import -------------------------------
-if(NOT _pybind11_nopython AND NOT CMAKE_CROSSCOMPILING)
+if(NOT _pybind11_nopython AND NOT _PYBIND11_CROSSCOMPILING)
# Check to see if modules are importable. Use REQUIRED to force an error if
# one of the modules is not found. <package_name>_FOUND will be set if the
# package was found (underscores replace dashes if present). QUIET will hide
diff --git a/tools/pybind11NewTools.cmake b/tools/pybind11NewTools.cmake
index dc3adfe9..f2ec3475 100644
--- a/tools/pybind11NewTools.cmake
+++ b/tools/pybind11NewTools.cmake
@@ -33,7 +33,7 @@ if(NOT Python_FOUND AND NOT Python3_FOUND)
endif()
# Interpreter should not be found when cross-compiling
- if(CMAKE_CROSSCOMPILING)
+ if(_PYBIND11_CROSSCOMPILING)
set(_pybind11_interp_component "")
else()
set(_pybind11_interp_component Interpreter)
@@ -110,7 +110,7 @@ if(PYBIND11_MASTER_PROJECT)
endif()
endif()
-if(NOT CMAKE_CROSSCOMPILING)
+if(NOT _PYBIND11_CROSSCOMPILING)
# If a user finds Python, they may forget to include the Interpreter component
# and the following two steps require it. It is highly recommended by CMake
# when finding development libraries anyway, so we will require it. |
Sure, looks good to me, feel free to replace my last commit :) |
Signed-off-by: Henry Schreiner <[email protected]>
Guess I'm the only reviewer. Thanks! |
Hello author! I would like to ask if you can provide an example of using toolchains and completing cross-compilation to generate a wheel for the target platform. |
Description
This PR aims to improve cross-compilation support:
find_package(Python)
is now called without theInterpreter
component.execute_process(COMMAND ${Python_EXECUTABLE} ...)
has been guarded byif(NOT CMAKE_CROSSCOMPILING)
.PYTHON_IS_DEBUG
,PYTHON_MODULE_EXTENSION
andPYTHON_MODULE_DEBUG_POSTFIX
, the new CMake code attempts to deduce sensible values from theSETUPTOOLS_EXT_SUFFIX
environment variable (set by e.g. cibuildwheel) and thePython_SOABI
variable provided by FindPython (CMake >= 3.17).Suggested changelog entry:
No longer rely on the Python interpreter when cross-compiling if ``PYBIND11_USE_CROSSCOMPILING`` is set.
Details
(The diffs look larger than they are, mostly because of whitespace changes from wrapping blocks in if statements, see https://github.com/pybind/pybind11/pull/5083/files?diff=unified&w=1 for a diff that ignores whitespace.)
FindPython
To ensure consistency between the Interpreter and Development.Module components, the call to
find_package(Python)
should generally include both components simultaneously. However, this is not true when cross-compiling: finding the Interpreter component will fail because CMake cannot run the binary for the target on the host.SETUPTOOLS_EXT_SUFFIX
This environment variable is set by cibuildwheel when cross-compiling on Windows. I chose to let this have precedence over the
Python_SOABI
variable, under the assumption that cibuildwheel has the necessary information to get this value correct.Python_SOABI
The
Python_SOABI
variable is available in CMake 3.17 and later. If it is unavailable and no other way to determine the extension suffix is found, the user will be asked to provide the necessary information directly (by setting thePYTHON_IS_DEBUG
,PYTHON_MODULE_EXTENSION
andPYTHON_MODULE_DEBUG_POSTFIX
variables).Caching
Since we don't need to query Python for information, the result variables are not added to the CMake cache. If the
SETUPTOOLS_EXT_SUFFIX
environment variable is used, a cache entryPYTHON_MODULE_EXT_SUFFIX
is created to save its value for subsequent runs.Native builds
Native builds are not affected by this PR, all new logic is guarded by
if(CMAKE_CROSSCOMPILING)
.Tests
I've added a file
tools/test-pybind11GuessPythonExtSuffix.cmake
which checks that thePYTHON_MODULE_EXTENSION
/PYTHON_MODULE_DEBUG_POSTFIX
variables are set correctly for different values ofPython_SOABI
on Windows, macOS and Linux.It has not yet been integrated in the rest of the test suite (pointers on how best to do this are welcome).
I've created a repository (tttapa/pybind11-cross-test) with minimal CMake toolchain files for cross-compiling a simple pybind11 extension module for ARM64 on Windows, Linux and macOS. All CI runs are successful, and the correct extension suffixes are used: https://github.com/tttapa/pybind11-cross-test/actions/runs/8511097705/
Quirks
Linux:
CMake's
FindPython
does not correctly find the SOABI for Python 3.6 and Python 3.7 (it is set to the empty string), unlessPython_FIND_ABI="ANY;ANY;ANY"
is specified. Annoyingly, settingPython_FIND_ABI
breaks things for Python >=3.8, but I believe that this is something that needs to be fixed upstream.Windows:
It seems that
FindPython
does not find the SOABI at all. This is whySETUPTOOLS_EXT_SUFFIX
is necessary. Even if this environment variable is not set, the proposed code falls back to.pyd
as the extension suffix whenPython_SOABI
is set to the empty string. This again seems like a limitation ofFindPython
itself and not something that can be fixed inpybind11
.