3. 使用 distutils 构建 C 和 C++ 扩展 — Python 文档

来自菜鸟教程
Python/docs/2.7/extending/building
跳转至:导航、​搜索

3. 使用 distutils 构建 C 和 C++ 扩展

从 Python 1.4 开始,Python 在 Unix 上提供了一个特殊的 make 文件,用于构建用于构建动态链接扩展和自定义解释器的 make 文件。 从 Python 2.0 开始,不再支持此机制(称为与 Makefile.pre.in 和 Setup 文件相关)。 很少使用构建自定义解释器,并且可以使用 distutils 构建扩展模块。

使用 distutils 构建扩展模块需要在构建机器上安装 distutils,它包含在 Python 2.x 中,可单独用于 Python 1.5。 由于 distutils 也支持创建二进制包,用户不一定需要编译器和 distutils 来安装扩展。

distutils 包包含一个驱动程序脚本,setup.py。 这是一个普通的 Python 文件,在最简单的情况下,它可能如下所示:

from distutils.core import setup, Extension

module1 = Extension('demo',
                    sources = ['demo.c'])

setup (name = 'PackageName',
       version = '1.0',
       description = 'This is a demo package',
       ext_modules = [module1])

有了这个 setup.py 和一个文件 demo.c,运行

python setup.py build

将编译 demo.c,并在 build 目录中生成名为 demo 的扩展模块。 根据系统的不同,模块文件将在子目录 build/lib.system 中结束,并且可能具有类似 demo.sodemo.pyd 的名称。

setup.py中,所有的执行都是通过调用setup函数来完成的。 这需要可变数量的关键字参数,上面的示例仅使用其中的一个子集。 具体来说,该示例指定了构建包的元信息,并指定了包的内容。 通常,一个包将包含附加模块,如 Python 源模块、文档、子包等。 请参考 Distributing Python Modules (Legacy version) 中的 distutils 文档,了解更多关于 distutils 的特性; 本节仅解释构建扩展模块。

通常预先计算 setup() 的参数,以更好地构建驱动程序脚本。 在上面的例子中,setup()ext_modules 参数是一个扩展模块列表,每个模块都是 Extension 的一个实例。 在示例中,该实例定义了一个名为 demo 的扩展名,它是通过编译单个源文件 demo.c 来构建的。

在许多情况下,构建扩展更为复杂,因为可能需要额外的预处理器定义和库。 这在下面的示例中进行了演示。

from distutils.core import setup, Extension

module1 = Extension('demo',
                    define_macros = [('MAJOR_VERSION', '1'),
                                     ('MINOR_VERSION', '0')],
                    include_dirs = ['/usr/local/include'],
                    libraries = ['tcl83'],
                    library_dirs = ['/usr/local/lib'],
                    sources = ['demo.c'])

setup (name = 'PackageName',
       version = '1.0',
       description = 'This is a demo package',
       author = 'Martin v. Loewis',
       author_email = 'martin@v.loewis.de',
       url = 'https://docs.python.org/extending/building',
       long_description = '''
This is really just a demo package.
''',
       ext_modules = [module1])

在这个例子中,setup() 被调用时带有额外的元信息,推荐在必须构建分发包时使用。 对于扩展本身,它指定了预处理器定义、包含目录、库目录和库。 根据编译器的不同,distutils 以不同的方式将此信息传递给编译器。 例如,在 Unix 上,这可能会导致编译命令

gcc -DNDEBUG -g -O3 -Wall -Wstrict-prototypes -fPIC -DMAJOR_VERSION=1 -DMINOR_VERSION=0 -I/usr/local/include -I/usr/local/include/python2.2 -c demo.c -o build/temp.linux-i686-2.2/demo.o

gcc -shared build/temp.linux-i686-2.2/demo.o -L/usr/local/lib -ltcl83 -o build/lib.linux-i686-2.2/demo.so

这些线路仅用于演示目的; distutils 用户应该相信 distutils 会正确调用。

3.1. 分发扩展模块

成功构建扩展后,可以通过三种方式使用它。

最终用户通常希望安装该模块,他们通过运行

python setup.py install

模块维护者应该制作源码包; 为此,他们运行

python setup.py sdist

在某些情况下,源代码分发中需要包含其他文件; 这是通过 MANIFEST.in 文件完成的; 有关详细信息,请参阅 distutils 文档。

如果源分发已成功构建,维护人员还可以创建二进制分发。 根据平台,可以使用以下命令之一来执行此操作。

python setup.py bdist_wininst
python setup.py bdist_rpm
python setup.py bdist_dumb