python用c语言编写的扩展模块,在setup.py sdist或者bdist打包时,默认是把c源码编译进去,在pip install的时候先编译再安装的,即使我把so加到了MANIFEST.in文件中,查看编出来的包里包含了so,但是仍然不会进行so的安装。搜索查阅了一下,没有找到相关的文章可以解决这个问题。因此只能自己通过debug研究python-prctl的安装流程,把相关的流程走通了。总结记录一下:
背景
首先说明一下背景:
我们的业务逻辑有一部分需要使用c语言来写,这部分c语言写的代码只能在特定的编译机上编译成.so,我们使用的python版本统一定为python3.8。因此我编写了一个python的c扩展模块,并对底层c接口进行了相应的python函数包装,在使用的时候,需要放到内网pip源,用pip install的方式安装。所有使用人的os版本、python版本都是一致的,且没有编译需要的相关文件。
原理
调试方式
由于时间比较紧,所以没有详细研究pip install的流程,python-prctl这个模块的项目结构和我们的类似,不同的是它是pip install的时候先编译再安装的,因此用它当作例子来学习。
在pycharm上配置ssh远程解释器,然后配置执行方式:
一些简单观察到的流程(没有实际看完整流程,只是靠debug和猜测推断了下)
pip install的流程
1 | 1. egg_info |
编译的.so,会拷贝到build/lib.linux-x86_64-3.8目录
实现思路
经过多次踩坑(一直不安装so,甚至改install命令自己把so拷贝到site-packages,但是uninstall时又不会卸载等等),最终确定一个实现的思路,虽然不编译,但是在build_ext的流程中,把对应的so拷贝到build/lib.linux-x86_64-3.8目录,让后续的安装流程能够正常安装进行,这样后面的流程能够正常的把so拷贝到site-packages目录,且卸载时正常删除。这样对原流程的改动最小,影响也最少。
build_ext命令的定制
我们的业务工程代码,对于Makefile有整体的封装,怎么搜索头文件,怎么搜索lib库都有封装,我们想要调用这些封装的内容来去找我们的依赖模块,这样我们依赖的模块有修改的话,该模块不用修改。所以不能在setup.py中写死include目录及libraries。因此对build_ext做一些定制。把include目录、链接的.a文件、源码通过Makefile传递进去。
1 | class my_build_ext(build_ext): |
setup.py的定制
根据实现思路的描述,这里主要处理pip install的时候的流程,跳过编译阶段,直接把so拷贝到build目录
1 | def build_extensions(self): |
makefile编写
在定义make all和make clean
make all时,先调用setup.py build_ext把so编译出来,然后拷贝到当前目录,最后调用setup.py sdist编译为可pip安装的包
1 | all: |
最终效果
可以看到最终实现了预期, 安装的时候, 自动把so给安装上了. 卸载的时候, 也顺利把so及相关文件卸载了.
本文转载自: 掘金