如何从Setuptools向CMake传递Numpy头文件目录
我完全懂你的痛点——硬编码路径实在太不灵活了,换个Python环境或者Numpy版本就容易出问题。你之前尝试的ext.include_dirs.append(numpy.get_include())没效果,核心原因是咱们用的是自定义CMakeExtension,setuptools的include_dirs是给它原生编译流程用的,而我们实际是通过调用独立的CMake进程构建,所以得把路径通过CMake参数主动传递过去,具体步骤如下:
1. 改造setup.py:获取Numpy路径并传递给CMake
首先,我们需要在构建流程里获取Numpy的include目录,然后把它作为CMake的定义参数加入到cmake_args列表中。另外,为了避免构建时找不到Numpy,最好在setup声明依赖,确保构建前先安装Numpy。
修改后的setup.py关键部分:
# 先导入numpy,同时处理可能未安装的情况 import numpy as np # ... 保留原来的CMakeExtension和CMakeBuild类 ... class CMakeBuild(build_ext): def run(self): # 确保构建前numpy已安装 self.distribution.fetch_build_eggs(['numpy>=1.18']) super().run() def build_extension(self, ext): extdir = os.path.abspath( os.path.dirname(self.get_ext_fullpath(ext.name))) # 获取numpy的include路径 numpy_include = np.get_include() cmake_args = [ '-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=' + extdir, '-DPYTHON_EXECUTABLE=' + sys.executable, # 新增:把numpy路径作为参数传递给CMake '-DNUMPY_INCLUDE_DIR=' + numpy_include ] # ... 保留原来的cfg、build_args、env等逻辑 ... setup( name='python_cpp_example', version='0.1', # ... 其他元信息 ... setup_requires=['numpy>=1.18'], # 构建前自动安装numpy install_requires=['numpy>=1.18'], # 运行时依赖声明 ext_modules=[CMakeExtension('python_cpp_example')], cmdclass=dict(build_ext=CMakeBuild), zip_safe=False, )
2. 修改CMakeLists.txt:使用传递过来的路径
把原来硬编码的Numpy路径删掉,改用我们从setup.py传递过来的NUMPY_INCLUDE_DIR变量:
# 替换硬编码路径,改用传递的变量 if(NOT NUMPY_INCLUDE_DIR) message(FATAL_ERROR "NUMPY_INCLUDE_DIR not provided by setup.py!") endif() include_directories(${NUMPY_INCLUDE_DIR}) # 保留你的Pybind11配置,比如: find_package(pybind11 REQUIRED) pybind11_add_module(your_module_name your_source_files.cpp)
为什么之前的方法没用?
咱们用的CMakeExtension是自定义的,它的sources字段是空的,setuptools的include_dirs属性是给它原生的Extension编译流程(比如用distutils直接编译C/C++代码)设计的。但我们实际是通过调用外部CMake进程构建,setuptools不会自动把include_dirs传递给CMake,所以必须显式地通过-D参数把路径传给CMake,让CMake在构建时识别并使用这个路径。
这样修改后,不管你的Python环境里Numpy安装在哪个位置,setup.py都会自动找到正确的路径并传递给CMake,彻底摆脱硬编码的麻烦!
内容的提问来源于stack exchange,提问作者Santi Peñate-Vera

