构建可再分发二进制文件#

本节的目标受众是任何希望构建 SciPy 并将其部署到其自身机器以外的任何人——从发行版打包者到希望构建 wheel 并部署到其生产环境的用户。

当使用 python -m buildpip wheel 构建 SciPy wheel 时,该 wheel 将依赖于外部共享库(至少包括 BLAS/LAPACK 和 Fortran 编译器运行时库,可能还有其他库)。因此,此类 wheel 只能在其构建系统上运行。有关更多上下文,请参见pypackaging-native 中“构建和安装或上传工件”部分下的内容

因此,这样的 wheel 是生成可分发二进制文件的一个中间阶段。最终的二进制文件可能是一个 wheel——在这种情况下,运行 auditwheel (Linux)、delocate (macOS)、delvewheel (Windows) 或 repairwheel (平台无关) 来将所需的共享库打包到 wheel 中。

最终的二进制文件也可以是其他打包格式(例如,.rpm.deb.conda 包)。在这种情况下,存在针对特定打包生态系统的工具,首先将 wheel 安装到暂存区,然后使该安装位置的扩展模块可重定位(例如,通过重写 RPATHs),然后将其重新打包成最终的包格式。

构建和运行时依赖项#

构建 SciPy 所需的 Python 构建和运行时依赖项可以在 pyproject.toml 元数据中找到。请注意,对于已发布的 SciPy 版本,依赖项可能具有上限。每个上限上方都有注释;在大多数情况下(除了 numpy),打包者可以自由删除或放宽这些上限。例如:

# The upper bound on pybind11 is preemptive only
"pybind11>=2.12.0,<2.13.0",

#   ...
#   3. The <2.3 upper bound is for matching the numpy deprecation policy,
#      it should not be loosened.
"numpy>=2.0.0rc1,<2.3",

非 Python 构建要求包括:

  • C、C++ 和 Fortran 编译器

  • BLAS 和 LAPACK 库

  • ninja

  • pkg-config

常见编译器的最低版本在顶层 meson.build 文件中强制执行。LAPACK 的最低版本目前是 3.7.1。有关这些构建依赖项的更详细信息,请参见工具链路线图

使用系统依赖项而不是打包的源代码#

SciPy 包含大量从 C、C++ 和 Fortran 编写的库中打包的代码。这是出于历史和健壮性原因的混合考虑。发行版打包者有时希望取消打包这些代码,以确保他们只使用某个库的一个版本。我们提供了一个构建选项 -Duse-system-libraries,允许他们以比手动修补 SciPy 更简单的方式实现这一点。

$ python -m build -wnx -Duse-system-libraries=boost.math

此构建选项仅查找与 SciPy 中捆绑的完全相同版本的依赖项。不同的系统版本将不被接受,构建将退回至打包的源代码或出错。这是因为不同版本很容易在构建时或运行时导致故障,并且 SciPy 的 CI 不会测试任何其他版本。这在未来可能会改变,但截至目前,使用不同版本需要修补相关的版本检查,并且强烈不建议这样做。

给打包者的建议

  1. 您应该将此构建选项理解为“如果我已经出于某种令人信服的原因解绑了某个库,此选项将使其变得相当容易”。

  2. 您不应该默认解绑所有内容,这会导致钻石依赖和更多的构建复杂性,而收益甚微。这些库中的大多数维护得不如 SciPy 好,并且/或已知不提供稳定的 API;实际上,您将为每个解绑的库添加一个额外的 == 依赖。只有在有充分理由时才这样做。例如,它允许您放弃补丁,您认为 SciPy 和所涉及的打包库都是安全关键的,或者它在二进制大小方面有很大增益。

此构建选项接受以下值:

  • none: 使用作为 SciPy 一部分提供的源代码,不查找外部依赖项(默认)。

  • all: 对所有由 use-system-libraries 控制的组件使用外部库,如果未找到则报错。检测由 Meson 完成,通常首先通过 pkg-config,然后是 dependency() 函数的 cmake 方法。

  • auto: 尝试检测外部库,如果未找到则退回至打包的源代码。

  • 逗号分隔的依赖名称列表(例如,boost.math,qhull)。如果给定,则对指定依赖项使用 all 行为(其余则为 none)。支持的选项包括:

    • boost.math (自 1.16.0 起)

    • qhull (自 1.16.0 起)

结合通用选项和命名库选项也是有效的,例如 boost.math,auto 将对除 boost.math 之外的所有依赖项应用 auto 行为。

从 wheel 或已安装的包中剥离测试套件#

默认情况下,已安装的 scipy 版本包含完整的测试套件。该测试套件,包括数据文件和仅用于测试的编译扩展模块,在 wheel 中占用约 4.5 MB(对于 x86-64,截至 v1.14.0),在磁盘上占用更多。在二进制大小重要的场景中,打包者可能希望删除测试套件。自 SciPy 1.14.0 起,有一种方便的方法可以实现这一点,利用了 Meson 的安装标签功能。只需一行命令:

$ python -m build -wnx -Cinstall-args=--tags=runtime,python-runtime,devel

注意

请注意,在上述命令中 -wnx 表示 --wheel --no-isolation --skip-dependency-check。它假设打包者已经设置了构建环境,这对于发行版打包通常是这种情况。安装标签功能同样适用于隔离构建(例如 pip install scipy --no-binary -Cinstall-args=--tags=runtime,python-runtime,devel)。

如果您想为测试本身生成一个单独的包,例如命名为 scipy-tests,则编辑 pyproject.toml 以更改项目名称:

[project]
name = "scipy-tests"

然后使用以下命令构建:

$ python -m build -wnx -Cinstall-args=--tags=tests

上述命令将构建整个包两次;为了以缓存方式重新构建,请使用 -Cbuild-dir=build 构建选项:

$     $ # apply patch to change the project name in pyproject.toml
$ python -m build -wnx -Cbuild-dir=build -Cinstall-args=--tags=tests

最终结果将类似于:

$ ls -lh dist/*.whl
...  20M  ...  dist/scipy-1.14.0-cp311-cp311-linux_x86_64.whl
...  4,5M ...  dist/scipy_tests-1.14.0-cp311-cp311-linux_x86_64.whl