pip install -e无法构建CMake扩展,如何实现带二进制扩展的可编辑安装?
问题背景
我有一个包含CMake扩展的setup.py文件:
from setuptools.command.build_ext import build_ext class CmakeExtension(build_ext): ... if __name__ == "__main__": setup( ... cmdclass={ "build_ext": CmakeExtension, # Build the C++ extension using cmake }, ext_modules= [ Extension('a', sources=[]), Extension('b', sources=[]), ] )
使用python3 setup.py bdist_wheel或python3 -m build . --wheel构建时一切正常,生成的wheel包包含对应的CMake目标文件。但执行pip install -e .时,无法触发CMake构建流程,仅生成Python文件,缺少关键的共享对象。
我可以先构建wheel再安装来规避问题,但存在两个问题:
- 个人偏好:希望用单条命令(
pip install而非setup.py bdist_wheel && pip install) - 核心需求:需要可编辑安装,而wheel包不支持该特性
根据setuptools文档说明:
"可编辑"仅针对包目录内的Python模块。非Python文件、外部(数据)文件、可执行脚本文件、二进制扩展、头文件和元数据可能仅以安装时的快照形式提供。
请问是否可以将可编辑安装与二进制扩展结合使用?
可行方案
当然可以实现,核心是让pip install -e .触发build_ext命令,并确保构建后的共享对象能被Python正确找到。以下是具体实现方式:
1. 自定义develop命令强制执行build_ext
在setup.py中自定义develop命令(对应可编辑安装流程),让它先执行扩展构建再完成可编辑安装:
from setuptools.command.develop import develop from setuptools.command.build_ext import build_ext class DevelopWithBuildExt(develop): def run(self): # 先触发CMake扩展构建 self.run_command("build_ext") # 执行默认的可编辑安装逻辑 super().run() class CmakeExtension(build_ext): ... if __name__ == "__main__": setup( ... cmdclass={ "build_ext": CmakeExtension, "develop": DevelopWithBuildExt, # 替换默认的develop命令 }, ext_modules= [ Extension('a', sources=[]), Extension('b', sources=[]), ] )
执行pip install -e .时,会先自动运行CMake构建扩展,再完成可编辑安装。
2. 确保共享对象被Python识别
默认build_ext会把编译后的共享对象放到build/lib.*目录,可编辑安装时Python可能找不到该路径,可通过以下方式处理:
- 修改
CmakeExtension类,将编译好的.so/.pyd文件直接复制到包的源码目录:
import shutil from pathlib import Path class CmakeExtension(build_ext): def install(self): super().install() # 获取编译后的扩展文件 build_lib = Path(self.build_lib) ext_files = list(build_lib.rglob("*.so")) + list(build_lib.rglob("*.pyd")) # 复制到包源码目录(假设包目录为`mypackage`) package_dir = Path("mypackage") for ext_file in ext_files: dest = package_dir / ext_file.name shutil.copy(ext_file, dest)
这样Python在可编辑模式下能直接从包目录读取到扩展文件。
3. 用pyproject.toml配置可编辑安装行为(推荐)
从setuptools 61.0.0开始,可通过pyproject.toml配置让pip install -e .自动构建并安装扩展:
在项目根目录创建pyproject.toml:
[build-system] requires = ["setuptools>=61.0.0", "wheel"] build-backend = "setuptools.build_meta" [tool.setuptools.editable_mode] build-command = ["python", "-m", "pip", "wheel", "-w", "{wheel_dir}", "."] install-command = ["python", "-m", "pip", "install", "--no-deps", "--force-reinstall", "{wheel_path}"]
这种方式会让可编辑安装先构建wheel(包含扩展),再以可编辑模式安装,兼顾了可编辑特性和二进制扩展的构建需求。
注意事项
- 确保setuptools版本≥61.0.0,避免兼容性问题
- 修改C++代码后,需重新执行
pip install -e .或单独运行python setup.py build_ext重新构建扩展 - 操作需在激活的虚拟环境中进行
内容的提问来源于stack exchange,提问作者CIsForCookies

