我有一个在Python扩展中使用的仅标头C++库。我希望能够将它们安装到 Python 的包含路径中,这样我就可以使用 python3 setup.py build
非常轻松地编译扩展。我有部分能力,但有两件事我无法工作(见下文(:
-
如何使用
python3 setup.py install
安装头文件?目前我只得到一些*.egg
文件,但没有安装标题。 -
如何保留模块的文件结构?当前,文件结构被错误地展平。
什么有效
具有以下setup.py
from setuptools import setup
setup(
name = 'so',
description = 'Example',
headers = [
'so.h',
],
)
我可以将模块上传到 PyPi:
python3 setup.py bdist_wheel --universal
twine upload dist/*
然后使用 pip 安装它:
pip3 install so
在我的系统上,然后我在这里找到标题
/usr/local/include/python3.6m/so/so.h
当我使用 Python 编译扩展时,这是可用的。
如何使用"python3 setup.py 安装"?
使用这个策略,我不能简单地运行
python3 setup.py install
在这种情况下,会安装一些so*.egg
,但标头不会存储在编译器可用的某个位置。
如何保留文件结构?
当模块有点复杂,并且有一些目录层次结构时,我也会遇到问题。对于以下setup.py
from setuptools import setup
setup(
name = 'so',
description = 'Example',
headers = [
'so.h',
'so/implementation.h',
],
)
问题是标头安装到
/usr/local/include/python3.6m/so/so.h
/usr/local/include/python3.6m/so/implementation.h
从而展平原始文件结构。
如何解决这两个问题?
python3 setup.py install
安装头文件?
不幸的是,只要您使用setuptools
,您就不能。当你打电话给setuptools.setup()
时,引擎盖下会发生什么?正在构建(bdist_egg
命令(和安装(通过easy_install
(的鸡蛋安装程序,bdist_egg
和easy_install
都不支持包括/安装标头。尽管 distribution
对象带有标头信息,但在 install
命令期间永远不会请求它。这是一个众所周知的古老问题,但从未得到解决,因为显然,头文件的安装不适合鸡蛋构建/安装过程。
因此,您有三个选择(或者我知道的至少三个选项(。其中两个(都会导致切换到distutils
(不推荐使用,仅出于完整性考虑而提供:
裸distutils
安装(不推荐(
$ sed 's/from setuptools import setup/from distutils.core import setup/' setup.py
这样,好的老distutils
将在执行python setup.py install
时负责安装,不会构建鸡蛋安装程序并调用install_headers
。但是,这也包括放弃setuptools
的所有功能,包括setup()
中的其他关键字 args 和所有其他好东西,不用说通过 distutils
安装的软件包不能用 pip
卸载。
old-and-unmanageable
安装(不推荐(
运行安装
$ python setup.py install --old-and-unmanageable
这是setuptools
明确希望运行distutils
安装时提供的开关。不会构建鸡蛋安装程序,而是调用distutils.command.install.install
。因此,安装与裸distutils
安装相同。
这种方法的缺点:与裸distutils
安装相同 plus:setuptools
谴责交换机的使用;如果您忘记提供它,则以安装 egg 结束,并且必须重做安装。
将python setup.py install
替换为pip install
(推荐(
pip
能够从源目录安装软件包;只需发出
$ pip install dir/
假设dir
包含setup.py
.这样,从源代码构建了一个wheel文件(与bdist_wheel
相同;实际上,此命令首先运行(并安装,管理头文件的安装就可以了。
如何保留模块的文件结构?
您必须稍微调整一下install_headers
命令:
import os
from distutils.command.install_headers import install_headers as install_headers_orig
from setuptools import setup
class install_headers(install_headers_orig):
def run(self):
headers = self.distribution.headers or []
for header in headers:
dst = os.path.join(self.install_dir, os.path.dirname(header))
self.mkpath(dst)
(out, _) = self.copy_file(header, dst)
self.outfiles.append(out)
setup(
name='so',
headers=['h1.h', 'subtree/h2.h'],
cmdclass={'install_headers': install_headers},
...
)
这里重要的是线条
dst = os.path.join(self.install_dir, os.path.dirname(header))
原版install_headers
将头文件直接复制到install_dir
;重载的install_headers
命令中的上述行还负责头文件名中的最终子目录。安装软件包时,现在应保留子目录:
$ pip show -f so | grep include
../../../include/site/python3.6/so/h1.h
../../../include/site/python3.6/so/subtree/h2.h