使用distutils构建一个基于ctypes的“基础”C库

时间:2010-12-25 06:56:39

标签: python ctypes distutils

this recommendation之后,我编写了一个本机C扩展库,通过ctypes优化Python模块的一部分。我之所以选择ctypes而不是编写CPython本地库,因为它更快更容易(只有一些函数内部包含所有紧密循环)。

我现在遇到了麻烦。如果我希望使用python setup.py install使用distutils轻松安装我的工作,那么distutils需要能够构建我的共享库并安装它(可能是/usr/lib/myproject)。但是,这不是一个Python扩展模块,所以据我所知,distutils不能这样做。

我发现了一些人提到其他人有这个问题:

我知道我可以做一些原生的东西而不是为共享库使用distutils,或者确实使用我的发行版的包装系统。我担心这会限制可用性,因为不是每个人都能轻松安装它。

所以我的问题是:当前分配共享库的最佳方法是什么,这些共享库将由ctypes使用,但是其他是OS本机而不是Python扩展模块?

如果您可以对其进行扩展并证明为什么这是最好的方法,请随意回答上面链接的其中一个黑客。如果没有更好的信息,至少所有信息都会在一个地方。

3 个答案:

答案 0 :(得分:3)

distutils文档here声明:

  

CPython的C扩展是一个共享库(例如Linux上的.so文件,Windows上的.pyd),它导出一个初始化函数。

因此,普通共享库的唯一区别似乎是初始化函数(除了合理的文件命名约定之外,我认为你没有任何问题)。现在,如果您查看distutils.command.build_ext,您会看到它定义了get_export_symbols()方法:

  

返回共享扩展程序必须导出的符号列表。这要么使用' ext.export_symbols'或者,如果没有提供," PyInit _" + module_name。仅适用于Windows,其中.pyd文件(DLL)必须导出模块" PyInit _"功能

因此,将它用于普通共享库应该是开箱即用的,除了在 视窗。但是也很容易解决这个问题。 get_export_symbols()的返回值传递给distutils.ccompiler.CCompiler.link(),文档说明:

  

' export_symbols'是共享库将导出的符号列表。 (这似乎仅适用于Windows。)

因此,不将初始化函数添加到导出符号就可以了。为此,您只需简单地覆盖build_ext.get_export_symbols()

此外,您可能希望简化模块名称。以下是可以构建ctypes模块和扩展模块的build_ext子类的完整示例:

from distutils.core import setup, Extension
from distutils.command.build_ext import build_ext


class build_ext(build_ext):

    def build_extension(self, ext):
        self._ctypes = isinstance(ext, CTypes)
        return super().build_extension(ext)

    def get_export_symbols(self, ext):
        if self._ctypes:
            return ext.export_symbols
        return super().get_export_symbols(ext)

    def get_ext_filename(self, ext_name):
        if self._ctypes:
            return ext_name + '.so'
        return super().get_ext_filename(ext_name)


class CTypes(Extension): pass


setup(name='testct', version='1.0',
      ext_modules=[CTypes('ct', sources=['testct/ct.c']),
                   Extension('ext', sources=['testct/ext.c'])],
      cmdclass={'build_ext': build_ext})

答案 1 :(得分:0)

这里有一些澄清:

  1. 这不是“基于ctypes”的库。它只是一个标准的C库,你想用distutils安装它。如果您使用C扩展名,ctypes或cython来包装该库与该问题无关。

  2. 由于库显然不是通用的,但只是包含应用程序的优化,因此您链接的建议不适用于您,在您的情况下,可能更容易编写C扩展或者使用Cython,在这种情况下可以避免问题。

  3. 对于实际问题,您总是可以使用自己的自定义distutils命令,事实上其中一个讨论链接到这样一个命令OOF2 build_shlib命令,它可以执行您想要的操作。在这种情况下,虽然你想安装一个真正没有共享的自定义库,然后我认为你不需要在/ usr / lib / yourproject中安装它,但你可以将它安装到/ usr的包目录中/ lib / python-xx / site-packages / yourmodule,以及你的python文件。但我不是百分之百确定,所以你必须尝试。

答案 2 :(得分:0)

我在这里设置了一个最小工作的python包,带有ctypes扩展名: https://github.com/himbeles/ctypes-example 可在Windows,Mac,Linux上运行。

  • 采用上述方法memeplex覆盖build_ext.get_export_symbols(),并强制所有操作系统的库扩展名相同(.so)。
  • 此外,在Windows和Unix的情况下,c / c ++源代码中的编译器指令可确保正确导出共享库符号。
  • 此外,GitHub Action会自动为所有操作系统编译二进制轮子:-)