Cross-compiling Python extension modules is supported out of the box through CMake toolchain files.
In cross-compilation, the build and host systems are often different systems with different architectures.
If your CMake project supports cross-compilation, cross-compiling a Python
package can be achieved by simply adding a py-build-cmake.cross.toml
file
to the same directory as pyproject.toml
. This file should contain the
necessary information about the host system, such as the Python version,
implementation and ABI, the host operating system and architecture, and the
relative path of the CMake toolchain file to use.
For example:
implementation = 'cp' # CPython
version = '39' # 3.9
abi = 'cp39' # (default ABI for CPython 3.9)
arch = 'linux_aarch64'
toolchain_file = 'aarch64-linux-gnu.cmake'
This will generate a package for CPython 3.9 on Linux for 64-bit ARM, using the
compilers defined by the toolchain file aarch64-linux-gnu.cmake
(which you
should provide as well).
The format for the values in this file is the same as the format used for the
tags in wheel filenames, for example package-1.2.3-cp39-cp39-linux_aarch64.whl
.
For more information about cross-compilation, ready-to-use toolchains, and example toolchain files, see https://tttapa.github.io/Pages/Raspberry-Pi/C++-Development-Ubuntu.
Note that py-build-cmake
does not check the Python version when
cross-compiling, so make sure that your CMakeLists.txt scripts find the correct
Python installation (on that matches the version, implementation, ABI, operating
system and architecture specified in the py-build-cmake.cross.toml
file),
e.g. by setting the appropriate hints and artifacts variables:
You can either specify these in your toolchain file, or in the
py-build-cmake.cross.toml
configuration, for example:
[cmake.options]
Python3_LIBRARY = "/path-to-sysroot/usr/lib/aarch64-linux-gnu/libpython3.9.so"
Python3_INCLUDE_DIR = "/path-to-sysroot/usr/include/python3.9"
You can find a full example in examples/pybind11-project/py-build-cmake.cross.example.toml
Some projects are more difficult to cross-compile, often for one of two reasons:
Since you cannot assume that the build system can execute binaries for the host system, or that build-Python can load modules compiled for host-Python, the only solution is to build the project twice: once for the build system, and once for the host system.
To get py-build-cmake
to perform an initial native build for the build
system before cross-compilation for th host system, set the
copy_from_native_build
option in the py-build-cmake.cross.toml
file.
The build process will then be as follows:
-D PY_BUILD_CMAKE_NATIVE_INSTALL_DIR:PATH="..."
that points to a temporary
folder where the build system's version of the project will be installed
in step (3).PY_BUILD_CMAKE_NATIVE_INSTALL_DIR
.PY_BUILD_CMAKE_NATIVE_INSTALL_DIR
CMake option is included again, and
points to the same folder where step (3) installed the project for the build
system. If you need executables for building, add this to the appropriate
CMake search paths in your CMakeLists.txt file. Cross-compilation is selected
by setting the -D CMAKE_TOOLCHAIN_FILE:FILEPATH="..."
CMake option.PY_BUILD_CMAKE_NATIVE_INSTALL_DIR
).copy_from_native_build
option,
match these against the files in PY_BUILD_CMAKE_NATIVE_INSTALL_DIR
, and
move the matching files from PY_BUILD_CMAKE_NATIVE_INSTALL_DIR
to the
staging directory, to be included in the package.The last step provides a convenient way to install stubs when cross-compiling: generating stubs requires loading the compiled extension modules, so cannot be done for host modules on the build system. Instead, the stubs are generated from the build modules in step (2), and then copied into the host package in step (7).
Be careful not to copy any incompatible binaries from the native build into the host package.