4. C および C++ 拡張のビルド

CPython の C 拡張は 初期化関数 をエクスポートした共有ライブラリ (例、 Linux の .so ファイルや Windows の .pyd ファイル) です。

インポートできるように、共有ライブラリは使える状態で PYTHONPATH 上になければならず、ファイル名をモジュール名に揃え、適切な拡張子になっていなければいけません。 distutils を使っているときは、自動的に正しいファイル名が生成されます。

初期化関数のシグネチャは次のとおりです:

PyObject* PyInit_modulename(void)

この関数は初期化がモジュールか、 PyModuleDef インスタンスを返します。 詳しいことは Cモジュールの初期化 を参照してください。

名前にASCIIしか使っていないモジュールの場合、関数名は PyInit_<modulename><modulename> をモジュール名で置き換えたものでなければなりません。 多段階初期化 を使っているときは、モジュール名にASCII以外の文字も使えます。 この場合、初期化関数の名前は PyInitU_<modulename> で、 <modulename> はハイフンをアンダースコアで置き換えて Python の punycode エンコーディングでエンコードしたものになります。 Python で書くと次のような処理になります:

def initfunc_name(name):
    try:
        suffix = b'_' + name.encode('ascii')
    except UnicodeEncodeError:
        suffix = b'U_' + name.encode('punycode').replace(b'-', b'_')
    return b'PyInit' + suffix

1つの共有ライブラリに複数の初期化関数を定義することで、複数のモジュールをエクスポートすることは可能です。 しかし、デフォルトではファイル名に対応した関数しか見付けようとしないので、複数のモジュールをインポートさせるにはシンボリックリンクか独自のインポーターを使う必要があります。 詳しいことは PEP 489"Multiple modules in one library" 節を参照してください。

4.1. distutils による C および C++ 拡張モジュールのビルド

拡張モジュールは Python に含まれる distutils を使ってビルドできます。 distutils はバイナリパッケージの作成もサポートしているので、ユーザが拡張モジュールをインストールする際に、必ずしもコンパイラや distutils が必要というわけではありません。

distutils ベースのパッケージには、駆動スクリプト (driver script) となる setup.py が入っています。 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 をコンパイルして、 demo という名前の拡張モジュールを build ディレクトリ内に生成します。システムによってはモジュールファイルは build/lib.system サブディレクトリに生成され、 demo.sodemo.pyd といった名前になることがあります。

setup.py 内では、コマンドの実行はすべて setup 関数を呼び出して行います。 この関数は可変個のキーワード引数を取ります。上の例ではその一部を使っているにすぎません。 もっと具体的にいうと、この例の中ではパッケージをビルドするためのメタ情報と、パッケージの内容を指定しています。 通常、パッケージには Python ソースモジュールやドキュメント、サブパッケージ等といった別のモジュールも入ります。 distutils の機能に関する詳細は、 Python モジュールの配布 (レガシーバージョン) に書かれている 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 が正しくコマンドを実行すると信用してください。

4.2. 拡張モジュールの配布

拡張モジュールをうまくビルドできたら、三通りの使い方があります。

エンドユーザは普通モジュールをインストールしようと考えます; これには、次を実行します

python setup.py install

モジュールメンテナはソースパッケージを作成します; これには、次を実行します

python setup.py sdist

場合によってはソース配布物に追加のファイルを含める必要があります; これには MANIFEST.in ファイルを使います; 詳しくは 配布するファイルを指定する を参照してください。

ソースコード配布物をうまく構築できたら、メンテナはバイナリ配布物も作成できます。プラットフォームに応じて、以下のコマンドのいずれかを使います。

python setup.py bdist_wininst
python setup.py bdist_rpm
python setup.py bdist_dumb
関連キーワード:  モジュール, 拡張, setup, demo, distutils, 関数, パッケージ, 配布, python, Extension