You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

pip install -e无法构建CMake扩展,如何实现带二进制扩展的可编辑安装?

可编辑安装结合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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 12:30:18