基于Setuptools与CMake构建Python2/3双版本HDBSCAN C绑定扩展的库名冲突问题
解决同时构建HDBSCAN C实现的Python2/Python3绑定问题
针对你遇到的「无法同时构建Python2和Python3版本扩展、库名冲突」的问题,我们可以从CMake动态生成版本适配的库名和Setuptools多版本构建流程两个层面入手解决,以下是具体方案:
一、调整CMakeLists.txt,让库名随Python版本自动适配
核心思路是在CMake中检测当前构建目标的Python版本,分别设置符合Python2/3规范的库名和后缀,同时将两个版本的输出目录分开,避免覆盖:
# 清理可能存在的重复目标 if(TARGET PyHdbscan) unset(TARGET PyHdbscan) endif() # 构建Python2版本 find_package(Python2 REQUIRED COMPONENTS Development) add_library(PyHdbscan_py2 SHARED # 填入你的C实现源文件,示例: hdbscan_core.c py_bindings.c ) target_link_libraries(PyHdbscan_py2 PRIVATE Python2::Python) # 设置Python2的库名和输出目录 set_target_properties(PyHdbscan_py2 PROPERTIES OUTPUT_NAME "PyHdbscan" LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/py2" ) # 构建Python3版本 find_package(Python3 REQUIRED COMPONENTS Development) add_library(PyHdbscan_py3 SHARED # 复用相同的源文件即可 hdbscan_core.c py_bindings.c ) target_link_libraries(PyHdbscan_py3 PRIVATE Python3::Python) # 设置Python3的库名(自动带上SOABI后缀,比如cpython-38-x86_64-linux-gnu)和输出目录 set_target_properties(PyHdbscan_py3 PROPERTIES OUTPUT_NAME "PyHdbscan" SUFFIX ".${Python3_SOABI}.so" LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/py3" )
这段代码会分别在build/py2和build/py3目录生成两个版本的库,完全避免命名冲突。
二、用Setuptools封装多版本构建流程
如果你希望通过setup.py一键触发两个版本的构建,可以自定义build_ext命令,分别调用CMake针对Python2和Python3构建:
from setuptools import setup, Extension from setuptools.command.build_ext import build_ext import subprocess import sys import os class DualVersionCMakeBuild(build_ext): def run(self): # 构建Python2版本(确保系统中python2命令可用) self._build_with_python("python2") # 构建Python3版本 self._build_with_python(sys.executable) def _build_with_python(self, python_exec): # 为每个Python版本创建独立的临时构建目录 build_temp = os.path.join(self.build_temp, os.path.basename(python_exec)) os.makedirs(build_temp, exist_ok=True) cmake_args = [ f"-DPython_EXECUTABLE={python_exec}", f"-DCMAKE_LIBRARY_OUTPUT_DIRECTORY={self.build_lib}", "../" # 假设CMakeLists.txt在项目根目录,根据实际路径调整 ] # 执行CMake配置和构建 subprocess.check_call(["cmake"] + cmake_args, cwd=build_temp) subprocess.check_call(["cmake", "--build", "."], cwd=build_temp) # 定义扩展(实际源文件由CMake处理,这里留空即可) ext = Extension("PyHdbscan", sources=[]) setup( name="PyHdbscan", version="0.1", ext_modules=[ext], cmdclass={"build_ext": DualVersionCMakeBuild} )
简化方案:分两次调用setup.py
如果不需要一键构建,也可以直接分别调用两个版本的Python解释器执行构建:
# 构建Python2版本 python2 setup.py build_ext --inplace # 构建Python3版本 python3 setup.py build_ext --inplace
此时只需要修改CMakeLists.txt为单版本适配的版本即可:
find_package(Python REQUIRED COMPONENTS Development) add_library(PyHdbscan SHARED hdbscan_core.c py_bindings.c ) target_link_libraries(PyHdbscan PRIVATE Python::Python) # 根据Python版本设置后缀 execute_process( COMMAND ${Python_EXECUTABLE} -c "import sys; print(sys.version_info.major)" OUTPUT_VARIABLE PY_MAJOR OUTPUT_STRIP_TRAILING_WHITESPACE ) if(PY_MAJOR EQUAL 3) set_target_properties(PyHdbscan PROPERTIES SUFFIX ".${Python_SOABI}.so" ) endif()
三、关键注意事项
- 确保系统已安装Python2和Python3的开发包(比如Debian系的
python-dev和python3-dev) - 如果你的C绑定代码中有Python2/3兼容逻辑,要确保在编译时通过宏定义区分(比如
#ifdef PY_MAJOR_VERSION) - 构建完成后,可以通过
python2 -c "import PyHdbscan"和python3 -c "import PyHdbscan"验证是否正常加载
内容的提问来源于stack exchange,提问作者Onalenna Junior Makhura
相关产品推荐
相关产品推荐

