使用Python安装仅包含头的库

时间:2018-04-30 13:38:34

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

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

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

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

  3. 什么有效

    使用以下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
    

    因此扁平化原始文件结构。

    如何解决这两个问题?

1 个答案:

答案 0 :(得分:4)

  

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

不幸的是,只要您使用setuptools,就无法做到。当你致电setuptools.setup()时,幕后会发生什么?正在构建一个egg安装程序(bdist_egg)并安装(通过easy_install),bdist_eggeasy_install都不支持包含/安装标头。虽然distribution对象带有头信息,但在install命令期间从不请求它。这是一个古老的众所周知的问题,从未得到解决,因为显然,头文件的安装不适合egg build / install过程。

因此,您有三个选项(或至少我知道的三个选项)。其中两个(均指示切换到distutils)不建议使用,仅为完整起见而提供:

distutils安装(不推荐)

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

这样,好的distutils将在执行python setup.py install时处理安装,不会构建任何egg安装程序,并且将调用install_headers。但是,这还包括放弃setuptools的所有功能,包括setup()中的其他关键字参数以及所有其他好东西,不用说无法卸载通过distutils安装的软件包与pip

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

使用

运行安装
$ python setup.py install --old-and-unmanageable

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

这种方法的缺点:与裸distutils安装加上相同:setuptools谴责交换机的使用;如果你忘了提供它,最后安装鸡蛋并重新安装。

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 copies 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