【问题标题】:Install header-only library with Python使用 Python 安装仅标头库
【发布时间】:2018-10-10 14:54:42
【问题描述】:

我有一个仅在我的 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

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

/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

从而扁平化原始文件结构。

如何解决这两个问题?

【问题讨论】:

    标签: python python-3.x setuptools setup.py header-only


    【解决方案1】:

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

    很遗憾,只要您使用的是setuptools,您就不能。当您致电 setuptools.setup() 时,幕后会发生什么?正在构建一个 egg 安装程序(bdist_egg 命令)并安装(通过easy_install),并且bdist_eggeasy_install 都不支持包含/安装标头。尽管distribution 对象带有标头信息,但在install 命令期间从未请求过它。这是一个从未解决的老问题,因为很明显,头文件的安装不适合 egg 构建/安装过程。

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

    distutils安装(不推荐)

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

    这样,在执行python setup.py install 时,优秀的distutils 将负责安装,不会构建鸡蛋安装程序并且将调用install_headers。但是,这也包括放弃setuptools 的所有功能,包括setup() 中的附加关键字args 和所有其他好东西,不用说通过distutils 安装的软件包不能用pip 卸载。

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

    运行安装

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

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

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

    python setup.py install 替换为pip install(推荐)

    pip 能够从源目录安装包;刚刚发布

    $ pip install dir/
    

    假设dir 包含setup.py。这样,从源文件(与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_headerscopies the header files directly to 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
    

    【讨论】:

    • 您可能还会在 Python 的错误跟踪器中找到有用的:related issue。我想自 2012 年以来没有任何变化......
    • 太棒了,非常感谢您的明确回答!我是否正确地观察到标题只能与setup.py 在同一个文件夹中或从它下面?例如。 ../src/so.h 不行吗?我也可以同样使用pip 将包含标头的包上传到 PyPi 吗?我该怎么做?
    • 这些文件可能位于项目目录之外,甚至位于某个系统目录中(我实际上必须维护一个从/usr/include/ 复制标头的模糊项目)。
    • 但是:您必须小心../src/so.h 这样的相对路径,因为它们将相对于工作目录进行解析,并且不一定包含setup.py。例如,我可以从任何其他工作目录运行脚本:cd / && python /home/me/myproj/setup.py cmd。如果您指定绝对路径,或者至少根据某个固定位置解析相对路径,例如__file__(这将是setup.py 脚本的路径),那么您是安全的。
    • 至于您的其他问题-您可以使用pip wheel 命令构建轮子,但没有任何东西可以上传包。但是,您在问题中描述的构建过程(bdist_wheel + twine upload)是正确的,我通常使用相同的命令来构建包并将其推送到 PyPI。我不会改变这一点。顺便说一句,这也是 PyPA 开发人员推荐的方式,according to these docs
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-12-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多