PEP 720 – 交叉编译 Python 包
- 作者:
- Filipe Laíns <lains at python.org>
- PEP 代理人:
- 状态:
- 草案
- 类型:
- 信息性
- 创建日期:
- 2023年7月1日
- Python 版本:
- 3.12
摘要
本 PEP 旨在记录下游项目交叉编译的现状。
它应概述分发商(Linux 发行版、WASM 环境提供商等)目前用于交叉编译下游项目(第三方扩展等)的方法。
动机
我们编写此 PEP 是为了阐明交叉编译中的挑战,并作为未来改进提案的辅助文档。
分析
引言
有几种不同的方法用于解决这个问题,对用户的交互要求程度不同,但它们都需要大量的精力。这是由于 Python 打包生态系统中缺乏标准化的交叉编译基础设施,这本身源于交叉构建的复杂性,使其成为一项巨大的任务。
上游支持
一些主要项目,如 CPython、setuptools 等,提供了一些支持来帮助交叉编译,但它们是非官方的,并且是尽力而为的。例如,sysconfig 模块允许通过 _PYTHON_SYSCONFIGDATA_NAME 环境变量覆盖数据模块名称,这是交叉构建所必需的,而 setuptools 接受补丁 [1] 以调整/修复其逻辑,使其与流行的 “环境模拟” 工作流 [2] 兼容。
上游项目缺乏第一方支持导致交叉编译变得脆弱,需要用户付出巨大的努力,但与此同时,缺乏标准化使得上游项目难以改进支持,因为如何提供此功能尚不明确。
具有良好交叉构建支持的项目
值得指出的是,目前有一些现代 Python 包构建后端至少具有不错的交叉编译支持,它们是 scikit-build 和 meson-python。这两个项目都将外部成熟的构建系统集成到 Python 打包中——分别是 CMake 和 Meson——因此交叉构建支持是从它们继承而来的。
下游方法
交叉编译方法范围广泛,从设计上需要大量用户交互到(理想情况下)几乎不需要交互。通常,它们将基于两种主要策略之一:使用 交叉构建环境 或 模拟目标环境。
交叉构建环境
这包括正常运行 Python 解释器并利用项目构建系统提供的交叉构建。然而,正如我们上面所看到的,上游支持不足,因此这种方法只适用于一小部分项目。当它失败时,通常的策略是修补构建系统代码以构建使用正确的工具链、系统详细信息等 [3]。
由于这种方法通常需要针对特定包进行修补,因此需要大量的用户交互。
示例
模拟目标环境
为了减少用户输入的需要,一种流行的方法是尝试模拟目标环境。它通常包括对 Python 解释器进行猴子补丁,使其模仿目标系统上的解释器,这包括更改 sys 模块的许多属性、sysconfig 数据等。使用这种策略,构建后端不需要任何交叉构建支持,并且应该在没有任何代码更改的情况下工作。
然而,不幸的是,不可能真正模拟目标环境。这有很多原因,其中一个主要原因是它会破坏实际需要自省运行中解释器的代码。因此,将 Python 猴子补丁以使其看起来像目标环境非常棘手——为了实现最少的破坏,我们只能修补解释器的某些方面。因此,构建后端可能需要一些代码更改,但这些更改通常比以前的方法小得多。这是该技术的固有局限性,这意味着此策略仍需要一些用户交互。
尽管如此,这种策略仍然比上述方法对更多项目开箱即用,并且在这些情况下所需的工作量要少得多。它成功地减少了所需的用户交互量,尽管它没有成功实现通用性。
示例
环境自省
如上所述,大多数构建系统代码都是在目标系统与构建发生地相同的假设下编写的,因此通常使用自省来指导构建。
在本节中,我们尝试记录实现这一目标的大多数方式。它应该对构建系统所需的环境详细信息有一个不错的概述。
| 代码片段 | 描述 | 差异 |
|---|---|---|
>>> importlib.machinery.EXTENSION_SUFFIXES
[
'.cpython-311-x86_64-linux-gnu.so',
'.abi3.so',
'.so',
]
|
此解释器支持的扩展(原生模块)后缀。 | 这是由实现定义的,但它**通常**因实现、系统架构、构建配置、Python 语言版本和实现版本(如果存在)而异。 |
>>> importlib.machinery.SOURCE_SUFFIXES
['.py']
|
此解释器支持的源(纯 Python)后缀。 | 这是由实现定义的,但它**通常**没有差异(除了奇特实现或系统)。 |
>>> importlib.machinery.all_suffixes()
[
'.py',
'.pyc',
'.cpython-311-x86_64-linux-gnu.so',
'.abi3.so',
'.so',
]
|
此解释器支持的所有模块文件后缀。它*应该*是所有 importlib.machinery.*_SUFFIXES 属性的并集。 |
这是由实现定义的,但它**通常**因实现、系统架构、构建配置、Python 语言版本和实现版本(如果存在)而异。有关更多信息,请参阅上面的条目。 |
>>> sys.abiflags
''
|
ABI 标志,如 PEP 3149 中指定。 | 根据构建配置而异。 |
>>> sys.api_version
1013
|
C API 版本。 | 根据 Python 安装而异。 |
>>> sys.base_prefix
/usr
|
安装范围内的目录前缀,其中安装了平台无关文件。 | 根据平台和安装而异。 |
>>> sys.base_exec_prefix
/usr
|
安装范围内的目录前缀,其中安装了平台相关文件。 | 根据平台和安装而异。 |
>>> sys.byteorder
'little'
|
原生字节序。 | 根据平台而异。 |
>>> sys.builtin_module_names
('_abc', '_ast', '_codecs', ...)
|
编译到 Python 解释器中的所有模块的名称。 | 根据平台、系统架构和构建配置而异。 |
>>> sys.exec_prefix
/usr
|
特定站点的目录前缀,其中安装了平台无关文件。由于它涉及特定站点的目录,在标准虚拟环境实现中,它将是特定于虚拟环境的路径。 | 根据平台、安装和环境而异。 |
>>> sys.executable
'/usr/bin/python'
|
正在使用的 Python 解释器的路径。 | 根据安装而异。 |
>>> with open(sys.executable, 'rb') as f:
... header = f.read(4)
... if is_elf := (header == b'\x7fELF'):
... elf_class = int(f.read(1))
... size = {1: 52, 2: 64}.get(elf_class)
... elf_header = f.read(size - 5)
|
Python 解释器是否是 ELF 文件,以及 ELF 头。这种方法用于识别安装的目标架构(示例)。 | 根据安装而异。 |
>>> sys.float_info
sys.float_info(
max=1.7976931348623157e+308,
max_exp=1024,
max_10_exp=308,
min=2.2250738585072014e-308,
min_exp=-1021,
min_10_exp=-307,
dig=15,
mant_dig=53,
epsilon=2.220446049250313e-16,
radix=2,
rounds=1,
)
|
浮点类型(由 float.h 定义)的低级信息。 |
根据架构和平台而异。 |
>>> sys.getandroidapilevel()
21
|
表示 Android API 级别的整数。 | 根据平台而异。 |
>>> sys.getwindowsversion()
sys.getwindowsversion(
major=10,
minor=0,
build=19045,
platform=2,
service_pack='',
)
|
系统的 Windows 版本。 | 根据平台而异。 |
>>> sys.hexversion
0x30b03f0
|
编码为整数的 Python 版本。 | 根据 Python 语言版本而异。 |
>>> sys.implementation
namespace(
name='cpython',
cache_tag='cpython-311',
version=sys.version_info(
major=3,
minor=11,
micro=3,
releaselevel='final',
serial=0,
),
hexversion=0x30b03f0,
_multiarch='x86_64-linux-gnu',
)
|
解释器实现细节。 | 根据解释器实现、Python 语言版本和实现版本(如果存在)而异。它可能还包括依赖于架构的信息,因此也可能根据系统架构而异。 |
>>> sys.int_info
sys.int_info(
bits_per_digit=30,
sizeof_digit=4,
default_max_str_digits=4300,
str_digits_check_threshold=640,
)
|
Python 内部整数表示的低级信息。 | 根据架构、平台、实现、构建和运行时标志而异。 |
>>> sys.maxsize
0x7fffffffffffffff
|
Py_ssize_t 类型的变量可以接受的最大值。 |
根据架构、平台和实现而异。 |
>>> sys.maxunicode
0x10ffff
|
最大 Unicode 码点的值。 | 根据实现和 Python 3.3 之前的版本,以及构建而异。 |
>>> sys.platform
linux
|
平台标识符。 | 根据平台而异。 |
>>> sys.prefix
/usr
|
特定站点的目录前缀,其中安装了平台相关文件。由于它涉及特定站点的目录,在标准虚拟环境实现中,它将是特定于虚拟环境的路径。 | 根据平台、安装和环境而异。 |
>>> sys.platlibdir
lib
|
平台特定的库目录。 | 根据平台和供应商而异。 |
>>> sys.version_info
sys.version_info(
major=3,
minor=11,
micro=3,
releaselevel='final',
serial=0,
)
|
解释器实现的 Python 语言版本。 | 如果目标 Python 版本不同,则不同 [4]。 |
>>> sys.thread_info
sys.thread_info(
name='pthread',
lock='semaphore',
version='NPTL 2.37',
)
|
有关线程实现的信息。 | 根据平台和实现而异。 |
>>> sys.winver
3.8-32
|
用于形成 Windows 注册表项的版本号。 | 根据平台和实现而异。 |
>>> sysconfig.get_config_vars()
{ ... }
>>> sysconfig.get_config_var(...)
...
|
Python 分发配置变量。它包含一组变量 [5]——例如 prefix、exec_prefix 等——基于运行上下文 [6],并且可能包含一些基于 Python 实现和系统的额外变量。在 CPython 和大多数使用相同构建系统的其他实现中,上面提到的“额外”变量是:在 POSIX 上,用于构建解释器的 |
这是由实现定义的,但它**通常**在非相同构建之间有所不同。请参阅 sysconfig 配置变量 表以概述通常存在的不同配置变量。 |
sys.prefix 和其他属性。Makefile,而是 使用 Visual Studio 构建系统。提供了一小部分最相关的 Makefile 变量,以简化使用它们的用户代码。CPython(及类似项目)
| 名称 | 示例值 | 描述 | 差异 |
|---|---|---|---|
SOABI |
cpython-311-x86_64-linux-gnu |
ABI 字符串 — 由 PEP 3149 定义。 | 根据实现、系统架构、Python 语言版本和实现版本(如果存在)而异。 |
SHLIB_SUFFIX |
.so |
共享库后缀。 | 根据平台而异。 |
EXT_SUFFIX |
.cpython-311-x86_64-linux-gnu.so |
特定于解释器的 Python 扩展(原生模块)后缀——通常定义为 .{SOABI}.{SHLIB_SUFFIX}。 |
根据实现、系统架构、Python 语言版本和实现版本(如果存在)而异。 |
LDLIBRARY |
libpython3.11.so |
共享 libpython 库名称——如果可用。如果不可用 [8],变量将为空;如果可用,库应位于 LIBDIR 中。 |
根据实现、系统架构、构建配置、Python 语言版本和实现版本(如果存在)而异。 |
PY3LIBRARY |
libpython3.so |
仅限 Python 3(仅限主版本绑定)[9] 共享 libpython 库名称——如果可用。如果不可用 [8],变量将为空;如果可用,库应位于 LIBDIR 中。 |
根据实现、系统架构、构建配置、Python 语言版本和实现版本(如果存在)而异。 |
LIBRARY |
libpython3.11.a |
静态 libpython 库名称——如果可用。如果不可用 [8],变量将为空;如果可用,库应位于 LIBDIR 中。 |
根据实现、系统架构、构建配置、Python 语言版本和实现版本(如果存在)而异。 |
Py_DEBUG |
0 |
是否为 调试构建。 | 根据构建配置而异。 |
WITH_PYMALLOC |
1 |
此构建是否支持 pymalloc。 | 根据构建配置而异。 |
Py_TRACE_REFS |
0 |
是否启用了引用跟踪(仅限调试构建)。 | 根据构建配置而异。 |
Py_UNICODE_SIZE |
Py_UNICODE 对象的大小,以字节为单位。此变量仅存在于 3.3 之前的 CPython 版本中,通常用于检测构建是使用 UCS2 还是 UCS4 进行 Unicode 对象——在 PEP 393 之前。 |
根据构建配置而异。 | |
Py_ENABLE_SHARED |
1 |
共享 libpython 是否可用。 |
根据构建配置而异。 |
PY_ENABLE_SHARED |
1 |
共享 libpython 是否可用。 |
根据构建配置而异。 |
CC |
gcc |
用于构建 Python 分发版的 C 编译器。 | 根据构建配置而异。 |
CXX |
g++ |
用于构建 Python 分发版的 C 编译器。 | 根据构建配置而异。 |
CFLAGS |
-DNDEBUG -g -fwrapv ... |
用于构建 Python 分发版的 C 编译器标志。 | 根据构建配置而异。 |
py_version |
3.11.3 |
Python 版本的完整形式。 | 根据 Python 语言版本而异。 |
py_version_short |
3.11 |
Python 版本的自定义形式,仅包含主要和次要版本号。 | 根据 Python 语言版本而异。 |
py_version_nodot |
311 |
Python 版本的自定义形式,仅包含主要和次要版本号,不含点。 | 根据 Python 语言版本而异。 |
prefix |
/usr |
与 sys.prefix 相同,请参阅上表中的条目。 |
根据平台、安装和环境而异。 |
base |
/usr |
与 sys.prefix 相同,请参阅上表中的条目。 |
根据平台、安装和环境而异。 |
exec_prefix |
/usr |
与 sys.exec_prefix 相同,请参阅上表中的条目。 |
根据平台、安装和环境而异。 |
platbase |
/usr |
与 sys.exec_prefix 相同,请参阅上表中的条目。 |
根据平台、安装和环境而异。 |
installed_base |
/usr |
与 sys.base_prefix 相同,请参阅上表中的条目。 |
根据平台和安装而异。 |
installed_platbase |
/usr |
与 sys.base_exec_prefix 相同,请参阅上表中的条目。 |
根据平台和安装而异。 |
platlibdir |
lib |
与 sys.platlibdir 相同,请参阅上表中的条目。 |
根据平台和供应商而异。 |
SIZEOF_* |
4 |
某个 C 类型(double、float 等)的大小。 |
根据系统架构和构建细节而异。 |
libpython 支持。libpython 时应链接到的 libpython 库。相关信息
构建系统所需的一些信息——例如平台特性——散布在许多地方,并且通常难以识别基于它们假设的代码。在本节中,我们尝试记录最相关的案例。
扩展何时应链接到 libpython?
- 简短回答
- 在 Windows 上是。在 POSIX 平台上不是,除了 Android、Cygwin 和其他基于 Windows 的类 POSIX 平台。
当为动态加载构建扩展时,根据目标平台,它们可能需要链接到 libpython。
在 Windows 上,扩展需要链接到 libpython,因为所有符号必须在链接时可解析。基于 Windows 的类 POSIX 平台——例如 Cygwin、MinGW 或 MSYS——也将需要链接到 libpython。
在大多数 POSIX 平台上,不需要链接到 libpython,因为符号由于解释器(或在嵌入时,相关的可执行文件/库)已经链接到 libpython 而已可用。不将扩展模块链接到 libpython 将允许它由静态 Python 构建加载,因此在可能的情况下,这是可取的(参见 GH-65735)。
这在所有 POSIX 平台上可能并非如此,因此请务必检查。一个例子是 Android,其中只有主可执行文件和 LD_PRELOAD 条目被认为是 RTLD_GLOBAL(意味着依赖项是 RTLD_LOCAL)[10],这导致在加载扩展时 libpython 符号不可用。
prefix、exec_prefix、base_prefix 和 base_exec_prefix 是什么?
这些是 sys 属性 在 Python 初始化中设置 的,描述了运行环境。它们指的是安装/环境文件目录的前缀,根据下表。
| 名称 | 目标文件 | 环境范围 |
|---|---|---|
prefix |
平台无关(例如纯 Python) | 特定于站点的 |
exec_prefix |
平台相关(例如原生代码) | 特定于站点的 |
base_prefix |
平台无关(例如纯 Python) | 安装范围内的 |
base_exec_prefix |
平台相关(例如原生代码) | 安装范围内的 |
由于特定站点的路径在虚拟环境中会有所不同,因此通常使用检查 sys.prexix != sys.base_prefix 来检查我们是否在虚拟环境中。
案例研究
crossenv
- 描述:
- 用于交叉编译 Python 扩展模块的虚拟环境。
- 网址:
- https://github.com/benfogle/crossenv
crossenv 是一个工具,用于创建一个带猴子补丁的 Python 安装的虚拟环境,该环境尝试在某些场景中模拟目标机器。有关此方法的更多信息,请参见 模拟目标环境 一节。
conda-forge
- 描述:
- conda 包管理器的社区主导的配方、构建基础设施和分发集合。
- 网址:
- https://forge.conda.org.cn/
XXX:PEP 草稿公开后,Jaime 将撰写一份简短摘要。
XXX 使用修改后的 crossenv。
Yocto 项目
- 描述:
- Yocto Project 是一个开源协作项目,帮助开发者创建定制的基于 Linux 的系统,无论硬件架构如何。
- 网址:
- https://www.yoctoproject.org/
XXX:已发送邮件到邮件列表。
待办事项
Buildroot
- 描述:
- Buildroot 是一个简单、高效且易于使用的工具,通过交叉编译生成嵌入式 Linux 系统。
- 网址:
- https://buildroot.org/
待办事项
Pyodide
- 描述:
- Pyodide 是一个基于 WebAssembly 的浏览器和 Node.js 的 Python 发行版。
- 网址:
- https://pyodide.org/en/stable/
XXX:Hood 应该审查/扩展此部分。
Pyodide 提供了一个使用 Emscripten 工具链编译到 WebAssembly 的 Python 发行版。
它修补了 CPython 安装和一些外部组件的几个方面。还提供了自定义包管理器——micropip——支持纯 Python 和 wasm32/Emscripten 轮子,作为发行版的一部分。除此之外,还提供了一个包含 一组选定的第三方包 的仓库,并默认启用。
Beeware
- 描述:
- BeeWare 允许您使用 Python 编写应用程序并将其发布到多个平台。
- 网址:
- https://beeware.org/
待办事项
python-for-android
- 描述:
- 将您的 Python 应用程序转换为 Android APK。
- 网址:
- https://github.com/kivy/python-for-android
资源 https://github.com/Android-for-Python/Android-for-Python-Users
python-for-android 是一个在 Android 上打包 Python 应用程序的工具。它创建了一个包含您的应用程序及其依赖项的 Python 发行版。
纯 Python 依赖项以通用方式自动处理,但原生依赖项需要 配方。提供了一组 流行依赖项 的配方,但用户需要为任何其他原生依赖项提供自己的配方。
kivy-ios
- 描述:
- 用于编译 Python / Kivy / 其他库的 iOS 工具链。
- 网址:
- https://github.com/kivy/kivy-ios
kivy-ios 是一个在 iOS 上打包 Python 应用程序的工具。它提供了一个工具链来构建包含您的应用程序及其依赖项的 Python 发行版,以及一个用于创建和管理与工具链集成的 Xcode 项目的 CLI。
它使用与 python-for-android(也由 Kivy 项目 维护)相同的方法来处理应用程序依赖项——纯 Python 依赖项自动处理,但原生依赖项需要 配方,并且该项目提供了 流行依赖项 的配方。
AidLearning
- 描述:
- 人工智能、Android、Linux、ARM:基于 Android+Linux 集成生态的人工智能应用开发平台。
- 网址:
- https://github.com/aidlearning/AidLearning-FrameWork
待办事项
QPython
- 描述:
- QPython 是 Android 的 Python 引擎。
- 网址:
- https://github.com/qpython-android/qpython
待办事项
pyqtdeploy
- 描述:
- pyqtdeploy 是一个用于部署 PyQt 应用程序的工具。
- 网址:
- https://www.riverbankcomputing.com/software/pyqtdeploy/
联系 https://www.riverbankcomputing.com/pipermail/pyqt/2023-May/thread.html 已联系维护者 Phil
待办事项
Chaquopy
- 描述:
- Chaquopy 提供您在 Android 应用中包含 Python 组件所需的一切。
- 网址:
- https://chaquo.com/chaquopy/
待办事项
EDK II
- 描述:
- EDK II 是一个现代、功能丰富、跨平台的固件开发环境,适用于 UEFI 和 PI 规范。
- 网址:
- https://github.com/tianocore/edk2-libc/tree/master/AppPkg/Applications/Python
待办事项
ActivePython
- 描述:
- 商业级、质量保证的 Python 发行版,专注于在 Windows、Linux、Mac OS X、Solaris、HP-UX 和 AIX 上轻松安装和跨平台兼容性。
- 网址:
- https://www.activestate.com/products/python/
待办事项
Termux
- 描述:
- Termux 是一个 Android 终端模拟器和 Linux 环境应用程序,无需 root 或设置即可直接使用。
- 网址:
- https://termux.dev/en/
待办事项
来源: https://github.com/python/peps/blob/main/peps/pep-0720.rst
最后修改时间: 2025-05-06 20:54:51 GMT