使用distutils.core构建Python3扩展时链接仍默认指向Python2.7
问题根因
distutils生成的所有编译参数、链接参数,完全由当前执行setup.py的Python解释器自带的配置决定,和系统全局python命令的软链指向没有必然绑定关系,也不会被你临时设置的CC环境变量覆盖链接侧的配置。
你现在遇到“手动指定3.6头文件能过编译、链接还是硬找python2.7库”的问题,本质是两个常见情况:
- 你敲构建命令时,实际拉起
setup.py的解释器还是Python2.7,哪怕表面看python指向3.0,可能是shell别名、PATH路径优先级、甚至setup.py里的shebang写死了解释器路径,导致distutils全程按Python2的配置走流程 - 之前编译残留的旧构建缓存,把Python2的参数写进了临时配置文件,被后续构建流程读到了
分步排障&配置适配
- 先别靠
python软链判断解释器版本。直接用绝对路径调用Python3:- 执行
ls /usr/bin/python3*找到你对应的3.6版本解释器路径,比如/usr/bin/python3.6 - 用这个绝对路径验证版本:
/usr/bin/python3.6 -c "import sys; print(sys.version)",确认输出是3.6.x版本,再用这个解释器跑构建,不要依赖全局python命令
- 执行
- 删掉旧的构建残留缓存。之前在Python2配置下生成的
build/目录里存了大量临时目标文件、链接参数记录;如果项目根目录下有SWIG生成的xxx_wrap.c包装代码文件,是之前Python2环境生成的,也可以一并删掉,后续构建流程会自动重新生成适配当前Python版本的包装代码。不删干净的话很容易复用旧配置,执行rm -rf build/ *wrap.c清理完再构建 - 先做参数dry-run检查,不要直接跑完整构建。执行
/usr/bin/python3.6 setup.py build_ext --dry-run,这个命令只会打印所有即将执行的编译、链接指令,不会真的生成目标文件,你可以直接在输出里确认:- 编译参数里的头文件路径是不是自动变成了
-I/usr/include/python3.6m - 链接参数里的库名是不是自动变成了
-lpython3.6m
如果这里还是输出python2.7的相关参数,说明你调用的解释器不对,回头核对第一步的路径
- 编译参数里的头文件路径是不是自动变成了
- 你之前手动设置
CC="gcc -I/usr/include/python3.6m"的操作是完全多余的。只要解释器路径正确,distutils会自动从当前解释器的sysconfig模块读取正确的头文件路径、动态库搜索路径、链接库名,不需要手动通过环境变量塞编译参数。之前能过编译、链接报错,就是因为链接参数完全从distutils绑定的Python2解释器配置里取,你只改了编译侧的头文件路径,没碰链接侧的配置来源
特殊场景排查
- 如果确认用Python3.6解释器跑构建,还是出现找python2.7库的报错,检查你的
setup.py文件:看Extension类的定义里是不是硬编码写了libraries=['python2.7']这类链接参数,把硬编码的Python2相关配置删掉,让distutils自动取当前解释器对应的库名即可 - CentOS最小化安装的Python3默认不带开发头文件和动态库,要确保已经安装
python36-devel包,执行yum install -y python36-devel即可,装完会自动把libpython3.6m.so放到系统库搜索路径下,避免出现链接不到Python3动态库的报错
内容的提问来源于stack exchange,提问作者Ethan Bradford
相关产品推荐
相关产品推荐

