首页 > 解决方案 > 使用 Python 安装仅标头库

问题描述

我有一个仅在我的 Python 扩展中使用的标头 C++ 库。我希望能够将它们安装到 Python 的包含路径中,这样我就可以使用python3 setup.py build. 我有部分能力,但有两件事我无法工作(见下文):

  1. 如何使用python3 setup.py install安装头文件?目前我只得到一些*.egg文件,但没有安装头文件。

  2. 如何保留模块的文件结构?当前文件结构被错误地展平。

什么有效

与以下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

然后在我的系统上找到标题here

/usr/local/include/python3.6m/so/so.h

当我用 Python 编译扩展时可用。

如何使用“python3 setup.py install”?

使用这种策略我不能简单地运行

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

从而使原始文件结构变平。

如何解决这两个问题?

标签: pythonpython-3.xsetuptoolssetup.pyheader-only

解决方案


如何使用python3 setup.py install安装头文件?

不幸的是,只要您使用setuptools. 当你打电话时,引擎盖下会发生什么setuptools.setup()?正在构建(bdist_egg命令)和安装(通过easy_install)的 egg 安装程序,既不bdist_egg也不easy_install支持包含/安装标头。尽管该对象带有标头信息,但在命令distribution期间从不请求它。install这是一个从未解决的老问题,因为很明显,头文件的安装不适合 egg 构建/安装过程。

因此,您有三个选项(或者我知道的至少三个选项)。不推荐使用其中两个(都导致切换到distutils),仅出于完整性考虑而提供:

裸机distutils安装(不推荐)

$ sed 's/from setuptools import setup/from distutils.core import setup/' setup.py

这样,good ol'distutils将在安装时负责安装python setup.py install,不会构建并install_headers调用 egg 安装程序。但是,这也包括放弃setuptools包含附加关键字 argssetup()和所有其他好东西的所有功能,不用说distutils通过pip.

old-and-unmanageable安装(不推荐)

运行安装

$ python setup.py install --old-and-unmanageable

setuptools如果您明确希望运行distutils安装,这是一个开关。egg 安装程序没有被构建,而是distutils.command.install.install被调用。因此,安装与裸distutils安装相同。

这种方法的缺点:与bare distutilsinstall plus相同:setuptools谴责使用开关;如果您忘记提供它,您将以安装鸡蛋结束,并且必须重新安装。

替换python setup.py installpip 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))

vanillainstall_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

推荐阅读