使用 Python 安装 header-only 库

Install header-only library with Python

我有一个 header-only C++ 库用于我的 Python 扩展。我希望能够将它们安装到 Python 的包含路径,这样我就可以很容易地使用 python3 setup.py build 编译扩展。我部分能,但有两件事我无法工作(见下文):

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

  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

在我的系统上,然后我在此处找到 header

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

当我使用 Python.

编译扩展时可用

如何使用'python3 setup.py install'?

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

python3 setup.py install

在那种情况下,安装了一些 so*.egg,但 header 没有存储在编译器可用的地方。

如何保留文件结构?

当模块有点复杂,并且有一些目录层次我也运行问题。对于以下 setup.py

from setuptools import setup

setup(
  name        = 'so',
  description = 'Example',
  headers     = [
    'so.h',
    'so/implementation.h',
  ],
)

问题是 header 被安装到

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

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

如何解决这两个问题?

How can I use python3 setup.py install to install the header files?

不幸的是,只要您正在使用 setuptools,就不能。当您调用 setuptools.setup() 时,幕后会发生什么?正在构建一个 egg 安装程序(bdist_egg 命令)并安装(通过 easy_install),bdist_eggeasy_install 都不支持 including/installing headers .虽然 distribution object 携带 headers 信息,但在 install 命令期间从未请求过。这是一个从未解决的老 well-known 问题,因为显然 header 文件的安装不适合 egg build/install 过程。

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

distutils 安装(不推荐)

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

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

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

运行安装

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

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

这种方法的缺点:与裸机相同 distutils 安装加上:setuptools 谴责开关的使用;如果忘记提供,则以安装 egg 结束,必须重新安装。

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

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

$ pip install dir/

假设 dir 包含 setup.py。这样,一个 wheel 文件是从源代码构建的(与 bdist_wheel 相同;实际上,这个命令首先被 运行 执行)并安装,管理 header 文件的安装就好了。

How can I retain the module's file structure?

您需要稍微调整 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 命令中的上述行还额外处理 header 文件名中的最终子目录。安装包时,现在要保留子目录:

$ pip show -f so | grep include
  ../../../include/site/python3.6/so/h1.h
  ../../../include/site/python3.6/so/subtree/h2.h