如何使用setuptools编译混合C/C++代码以供Cython调用
实现多阶段构建并复用setuptools指定编译器的方案
核心逻辑:不要手动初始化distutils的编译器实例,直接复用build_ext流程中已经完成初始化、完全匹配了用户指定编译器参数(如命令行--compiler参数、环境变量CC/CXX指定值)的编译器实例即可。
具体实现步骤如下:
- 导入所需依赖模块,自定义
build_ext子类重写构建逻辑,在默认的Cython/C++扩展构建流程前插入第三方C源码的编译步骤,全程复用当前实例已初始化的compiler对象 - 在
setup函数中通过cmdclass参数替换默认的build_ext命令为你自定义的子类
参考实现代码:
from setuptools import setup, Extension from setuptools.command.build_ext import build_ext from Cython.Build import cythonize class CustomBuildExt(build_ext): def run(self): # 阶段1:使用当前实例的编译器(即setup实际要使用的编译器)编译第三方C依赖 c_source_files = [ "third_party/lib1/xxx.c", "third_party/lib2/yyy.c" ] c_include_dirs = [ "third_party/lib1/include", "third_party/lib2/include" ] # 编译生成目标文件,输出到构建临时目录避免污染源码 compiled_c_objects = self.compiler.compile( sources=c_source_files, include_dirs=c_include_dirs, output_dir=self.build_temp, debug=self.debug, # 可根据需要传入C源码专属编译参数 extra_preargs=["-O2", "-fPIC"] if self.compiler.compiler_type != "msvc" else ["/O2"] ) # 将编译好的C目标文件挂载到扩展属性中,后续链接阶段会自动引入 for ext in self.extensions: ext.extra_objects = ext.extra_objects or [] ext.extra_objects.extend(compiled_c_objects) # 阶段2:执行原生构建逻辑,完成Cython、C++代码的编译和链接 super().run() # 定义你的Python扩展 your_extension = Extension( name="your_package.module_name", sources=[ "your_code/cython_wrapper.pyx", "your_code/cpp_logic.cpp" ], include_dirs=c_include_dirs, language="c++", # 可根据需要传入C++专属编译、链接参数 extra_compile_args=["-std=c++17"] if self.compiler.compiler_type != "msvc" else ["/std:c++17"] ) setup( name="your_package", version="0.1.0", ext_modules=cythonize([your_extension]), cmdclass={ "build_ext": CustomBuildExt } )
额外注意事项:
- 如需区分不同编译器的参数差异,可通过
self.compiler.compiler_type判断当前编译器类型,常见取值为gcc、clang、msvc - 如果第三方C依赖使用独立构建系统(如CMake、Makefile),也可在自定义
build_ext的run方法中调用对应构建命令,编译器路径可直接从self.compiler.cc(C编译器路径)、self.compiler.cxx(C++编译器路径)获取传递给子构建系统 - 所有用户通过命令行、环境变量传入的构建配置都会自动被
self.compiler继承,无需手动解析处理
内容的提问来源于stack exchange,提问作者feenor
相关产品推荐
相关产品推荐

