XGBoost Python 包打包详解:构建二进制 Wheel 与源码发行版(sdist)
【免费下载链接】xgboostScalable, Portable and Distributed Gradient Boosting (GBDT, GBRT or GBM) Library, for Python, R, Java, Scala, C and more. Runs on single machine, Hadoop, Spark, Dask, Flink and DataFlow项目地址: https://gitcode.com/gh_mirrors/xg/xgboost
本文基于仓库中的 doc/contrib/python_packaging.rst 展开,系统讲解 XGBoost Python 包两种标准分发形态——源码发行版(sdist)与二进制 wheel——的构建方法、产物差异与底层原理。读完后,你将能够自行构建可分发的 XGBoost 包,并理解其中 CMake 构建后端、共享库打包与运行时库查找机制的完整链路。
两种分发机制:sdist 与 wheel
Wheel 和源码发行版(source distribution,简称 sdist)是打包和分发 Python 包的两种主要机制:
- 源码发行版(sdist):一个 tarball(
.tar.gz扩展名),包含源码。 - wheel:一个 ZIP 压缩归档(
.whl扩展名),代表一个已构建的发行版。与 sdist 不同,wheel 可以包含编译产物。编译产物在分发前已完成编译,对终端用户而言安装 wheel 更方便。包含编译产物的 wheel 称为二进制 wheel(binary wheel)。
理解这两种形态的关键差异在于:wheel 是"构建完成"的产物,sdist 是"构建原料"的产物。对 XGBoost 这样的 C++ 核心 + Python 绑定项目,这一差异直接体现在"是否需要在pip install时现场编译"上。
构建源码发行版(sdist)
对 XGBoost 而言,sdist 同时包含 Python 代码和 C++ 代码,因此 XGBoost 的核心部分可以被编译成共享库libxgboost.so(不同操作系统下共享库文件名不同,见结尾脚注)。
你可以用如下命令获得 sdist:
$ python -m build --sdist .(执行前需要先安装build包:pip install build或conda install python-build。)
用 sdist 执行pip install会启动 CMake 和 C++ 编译器,把捆绑的 C++ 代码编译为libxgboost.so:
$ pip install -v xgboost-2.0.0.tar.gz # 加 -v 可查看构建进度仓库源码视角:sdist 如何做到自包含
上面的python -m build --sdist .在当前仓库结构下应在 python-package/ 目录中执行(该目录下的 pyproject.toml 声明了构建系统)。这里有一个值得注意的工程细节:sdist 需要把仓库根目录的整个 C++ 源码树塞进发行版,但构建后端的打包规则只能看到项目根目录之下的文件。仓库用一个独立脚本来解决这一点,即 ops/script/prepare_sdist.py:
- 它把
src、include、dmlc-core、cmake、plugin五个 C++ 子目录,连同根目录的CMakeLists.txt和LICENSE,整体拷贝到python-package/cpp_src/暂存目录; - 脚本是幂等的:每次运行先清空并重建暂存目录;
- 它必须在
python -m build --sdist之前运行。
pyproject.toml 中的相应配置印证了这一流程:
[tool.scikit-build] # Source distributions ship the C++ tree under cpp_src/, staged by # ops/script/prepare_sdist.py before `python -m build --sdist`. sdist.include = ["cpp_src/**"] sdist.exclude = ["build/", "dist/", "wheelhouse/", "*.pyc", "__pycache__"] sdist.reproducible = true其中sdist.reproducible = true开启可复现构建(归档内文件时间戳等元数据归一化),保证同一版本多次打出的 sdist 字节一致,便于校验与审计。
CI 中完整的 sdist 验证流程可见 ops/pipeline/test-python-sdist.sh:先运行prepare_sdist.py暂存 C++ 源码树,再进入python-package/执行python -m build --sdist,随后用pip install -v ./dist/xgboost-*.tar.gz从 sdist 现场编译安装,最后以python -c 'import xgboost'确认导入成功。脚本中还演示了通过--config-settings cmake.define.CMAKE_C_COMPILER_LAUNCHER=sccache给 CMake 注入编译缓存加速器的用法。
构建二进制 wheel
你也可以构建 wheel:
$ pip wheel --no-deps -v .(--no-deps表示不连带构建/下载依赖。)
需要特别指出的是,产物 wheel 中包含一份共享库libxgboost.so的拷贝。由于含有编译二进制,这是一个二进制 wheel。
用二进制 wheel 执行pip install时,pip 只把 wheel 内容解压到当前 Python 环境。由于 wheel 内已经带有预构建的libxgboost.so,安装时无需再构建,因此pip install一个二进制 wheel 能很快完成:
$ pip install xgboost-2.0.0-py3-none-linux_x86_64.whl # 快速完成(文件名中的版本号、Python ABI 标签与平台标签是 wheel 命名规范的一部分:py3-none-linux_x86_64表示纯 Python 3 兼容、无特定 C 扩展 ABI、面向 Linux x86_64。)
仓库源码视角:wheel 中的 libxgboost 从何而来
Python 侧的构建入口是 python-package/CMakeLists.txt,它支持两种构建模式:
- 复用预构建库:如果
${C++ 源码}/lib/目录下已存在libxgboost.{so,dylib,dll}(通常由 CI 先在仓库根用 CMake 构建好),则直接把该库安装进 wheel 的xgboost/lib/目录。源码中还专门处理了 SOVERSION 符号链接(.so -> .so.3 -> .so.3.3.0)的情况——先file(REAL_PATH ...)解析到底层实体文件再安装,避免 wheel 打包器丢弃悬空软链。 - 从源码编译:如果没有预构建库(例如
pip install python-package/或从 sdist 安装的场景),该 CMakeLists 通过add_subdirectory()挂载 C++ 源码树现场编译。sdist 安装时 C++ 树位于本文件旁边的cpp_src/(由prepare_sdist.py暂存),仓库内构建时则指向父目录。
两种路径最终都把共享库以无版本号的文件名安装为xgboost/lib/下的libxgboost.so(或对应平台名),这正是 Python 端加载器期望的位置。
pyproject.toml中与 wheel 构建直接相关的配置:
[build-system] requires = ["scikit-build-core>=0.11.0"] build-backend = "scikit_build_core.build" [tool.scikit-build] build-dir = "build/{wheel_tag}" ninja.make-fallback = true wheel.py-api = "py3" wheel.packages = ["xgboost"] # Only install rules tagged with COMPONENT XGBoostPython make it into the # wheel. C++ subprojects (e.g. dmlc-core) register their own install() rules # for headers / static libs / CMake configs that we don't want inside the # Python wheel; this filter quietly skips all of them. install.components = ["XGBoostPython"] [tool.scikit-build.cmake] version = "CMakeLists.txt" build-type = "Release"几个关键点:
- 构建后端是scikit-build-core,它驱动 CMake 完成 C++ 编译并组装 wheel;
build-type = "Release"指定优化编译; install.components = ["XGBoostPython"]是一个过滤器:只有带该 COMPONENT 标签的安装规则才会进入 wheel,从而把 dmlc-core 等 C++ 子项目面向系统安装的 headers/静态库/CMake 配置规则全部排除在外;ninja.make-fallback = true允许在没有 Ninja 时回退到 Makefile 生成器。
另外,pyproject.toml 文件头注明其由 ops/script/pypi_variants.py 自动生成(当前变体为--use-suffix=na --require-nccl-dep=cu13),用于派生不同发行渠道所需的依赖组合——例如 CUDA 13 变体在 Linux 上额外声明nvidia-nccl-cu13运行时依赖。
CI 中对 wheel 的验证
CI 的 wheel 测试脚本 ops/pipeline/test-python-wheel.sh 演示了官方对 wheel 产物的验收方式:安装./wheelhouse/*.whl后,对于 GPU 变体,会调用from xgboost import build_info读取build_info()["CUDA_VERSION"],断言 wheel 实际是用预期大版本的 CUDA 构建的,再按 CPU/GPU/多 GPU/ARM64 等套件分别运行对应测试集。这为"二进制 wheel 必须与宣传的构建环境一致"提供了可执行的验证手段。
运行时如何找到共享库
为什么 wheel 必须把libxgboost.so打进xgboost/lib/?答案在 python-package/xgboost/libpath.py 的find_lib_path():
- 首先找包内的
xgboost/lib/("normal, after installationlibis copied into Python package tree")——这对应 wheel 安装的场景,库就内置在包目录里; - 其次找仓库布局下的
../../lib/(editable 安装时不做拷贝); - 最后回退到系统前缀
sys.base_prefix/lib,并在 Windows 上追加若干 Conda 目录; - 找不到时抛出
XGBoostLibraryNotFound,错误信息中列出所有候选路径以便排查。
文件名也是按平台硬编码映射的:Linux/FreeBSD/Emscripten 为libxgboost.so,macOS 为libxgboost.dylib,Windows 为xgboost.dll,Cygwin 为cygxgboost.dll。这与 python-package/CMakeLists.txt 中_XGB_LIBNAME的平台分支一一对应。
相关配置速查
| 主题 | 位置 | 说明 |
|---|---|---|
| 构建后端声明 | python-package/pyproject.toml | scikit-build-core>=0.11.0,sdist.include = ["cpp_src/**"] |
| C++ 树暂存 | ops/script/prepare_sdist.py | 打 sdist 前把src/include/dmlc-core/cmake/plugin拷入python-package/cpp_src/ |
| wheel 打包 CMake | python-package/CMakeLists.txt | 预构建库复用 / 源码编译两种模式 |
| sdist CI 验证 | ops/pipeline/test-python-sdist.sh | sdist 构建 + 现场编译安装 +import xgboost冒烟 |
| wheel CI 验证 | ops/pipeline/test-python-wheel.sh | 安装 wheel 并核对build_info()的 CUDA 版本 |
| 运行时库查找 | python-package/xgboost/libpath.py | 按平台解析共享库名与候选路径 |
| 共享库构建文档 | doc/build.rst | CMake 选项(USE_CUDA、USE_NCCL等)与平台库名 |
共享库文件名的平台差异
脚注(继承原文档):共享库文件名会随操作系统变化——
- Linux 及其他类 UNIX 系统:
libxgboost.so- macOS:
libxgboost.dylib- Windows:
xgboost.dll完整的共享库构建说明参见仓库文档 doc/build.rst 中 "Building the Shared Library" 一节(即原文档脚注引用的
build_shared_lib锚点),其中还给出了 GPU(USE_CUDA)、NCCL(USE_NCCL、USE_DLOPEN_NCCL)、HIDE_CXX_SYMBOLS等常用 CMake 选项。
小结
- sdist 是"源码 + 构建配方",
pip install时由 CMake 现场编译libxgboost.so,适合无法提供 wheel 的目标平台,代价是安装慢、要求本机有 C++ 工具链; - 二进制 wheel 内置预编译的
libxgboost.so(安装到xgboost/lib/),pip install秒级完成,是终端用户的首选; - 两条链路的公共基石是 python-package/ 下由 scikit-build-core 驱动的 CMake 构建,配合
install.components = ["XGBoostPython"]过滤确保 wheel 内容精确可控; - 复现 CI 行为时,参考 ops/pipeline/test-python-sdist.sh 与 ops/pipeline/test-python-wheel.sh 中的命令序列即可。
【免费下载链接】xgboostScalable, Portable and Distributed Gradient Boosting (GBDT, GBRT or GBM) Library, for Python, R, Java, Scala, C and more. Runs on single machine, Hadoop, Spark, Dask, Flink and DataFlow项目地址: https://gitcode.com/gh_mirrors/xg/xgboost
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考