Windows 上での C および C++ 拡張モジュールのビルド¶
この章では Windows 向けの Python 拡張モジュールを Microsoft Visual C++ を使って作成する方法について簡単に述べ、その後に拡張モジュールのビルドがどのように動作するのかについて詳しい背景を述べます。この説明は、Python 拡張モジュールを作成する Windows プログラマと、Unix と Windows の双方でうまくビルドできるようなソフトウェアの作成に興味がある Unix プログラマの双方にとって有用です。
モジュールの作者には、この節で説明している方法よりも、distutils によるアプローチで拡張モジュールをビルドするよう勧めます。また、Python をビルドした際に使われた C コンパイラが必要です; 通常は Microsoft Visual C++です。
注釈
この章では、Python のバージョン番号が符号化されて入っているたくさんのファイル名について触れます。これらのファイル名は XY で表されるバージョン名付きで表現されます; 'X' は使っている Python リリースのメジャーバージョン番号、'Y' はマイナーバージョン番号です。例えば、Python 2.2.1 を使っているなら、XY は実際には 22 になります。
型どおりのアプローチ¶
Unix と同様、Windows で拡張モジュールをビルドする方法には、setuptools パッケージでビルドを制御する方法と、手動で行う方法の 2 つがあります。
ほとんどの拡張では setuptools が適しています。
setuptools による拡張モジュールのビルドとパッケージ化の説明は、setuptools による C および C++ 拡張のビルド にあります。
手動で行う必要がある場合は、標準ライブラリの winsound モジュールのプロジェクトファイルを調べると参考になるでしょう。
Unix と Windows の相違点¶
Unix と Windows では、コードの実行時読み込みに全く異なるパラダイムを用いています。動的ロードされるようなモジュールをビルドしようとする前に、自分のシステムがどのように動作するか知っておいてください。
Unix の共有オブジェクト (.so) ファイルには、プログラムが使うコードに加え、プログラム内に存在すると想定する関数やデータの名前が含まれています。
ファイルをプログラムに組み込むと、ファイル内のコードからそれらの関数やデータへの参照は、メモリ上の実際の配置先を指すように変更されます。
これは基本的にリンク操作です。
Windows では、動的リンクライブラリ (.dll) ファイルにはぶら下がり参照 (dangling reference) はありません。その代わり、関数やデータへのアクセスはルックアップテーブルを介します。従って DLL コードの場合、実行時にポインタがプログラムメモリ上の正しい場所を指すように修正する必要はありません; その代わり、コードは常に DLL のルックアップテーブルを使い、ルックアップテーブル自体は実行時に実際の関数やデータを指すように修正されます。
Unix には、唯一のライブラリファイル形式 (.a) しかありません。 .a ファイルには複数のオブジェクトファイル (.o) 由来のコードが入っています。共有オブジェクトファイル (.so) を作成するリンク処理の段階中に、リンカは定義場所の不明な識別子に遭遇することがあります。このときリンカはライブラリ内のオブジェクトファイルを検索します; もし識別子が見つかると、リンカはそのオブジェクトファイルから全てのコードを取り込みます。
Windows では、二つの形式のライブラリ、静的ライブラリとインポートライブラリがあります (どちらも .lib と呼ばれています)。静的ライブラリは Unix における .a ファイルに似ています; このファイルには、必要に応じて取り込まれるようなコードが入っています。インポートライブラリは、基本的には特定の識別子が不正ではなく、 DLL がロードされた時点で存在することを保証するためにだけ使われます。リンカはインポートライブラリからの情報を使ってルックアップテーブルを作成し、DLL に入っていない識別子を使えるようにします。アプリケーションや DLL がリンクされるさい、インポートライブラリが生成されることがあります。このライブラリは、アプリケーションや DLL 内のシンボルに依存するような、将来作成される全ての DLL で使うために必要になります。
二つの動的ロードモジュール、B と C を作成し、別のコードブロック A を共有するとします。Unix では、 A.a を B.so や C.so をビルドするときのリンカに渡したりは しません ; そんなことをすれば、コードは二度取り込まれ、B と C のそれぞれが自分用のコピーを持ってしまいます。 Windows では、 A.dll をビルドすると A.lib もビルドされます。 B や C のリンクには A.lib を渡します。 A.lib にはコードは入っていません; 単に A のコードにアクセスするするために実行時に用いられる情報が入っているだけです。
Windows ではインポートライブラリの使用は import spam とするようなものです; この操作によって spam の名前にアクセスできますが、コードのコピーを個別に作成したりはしません。Unix では、ライブラリとのリンクはむしろ from spam import * に似ています; この操作では個別にコードのコピーを生成します。
-
Py_NO_LINK_LIB¶
CPython のヘッダーファイル内で行われる、
#pragmaによる Python ライブラリへの暗黙のリンクを無効にします。Added in version 3.14.
DLL 使用の実際¶
Windows 版 Python は Microsoft Visual C++ でビルドされています。 ほかのコンパイラで動作するかどうかは分かりません。 この節の残りは、MSVC++ 固有の説明です。
Windows で DLL を作成する場合、CPython のライブラリを使う方法は 2 つあります。
デフォルトでは、
PC/pyconfig.hを直接、またはPython.hを通じてインクルードすると、設定に応じたライブラリが暗黙にリンクされます。 ヘッダーファイルは、Debug ではpythonXY_d.lib、Release ではpythonXY.lib、Limited API を有効にした Release ではpythonX.libを選びます。spam と、spam 内の C 関数を使う ni という 2 つの DLL をビルドするには、次のコマンドを使えます。
cl /LD /I/python/include spam.c cl /LD /I/python/include ni.c spam.lib
最初のコマンドは、
spam.obj、spam.dll、spam.libの 3 つのファイルを作成します。Spam.dllにはPyArg_ParseTuple()などの Python の関数は含まれていませんが、暗黙にリンクされたpythonXY.libによって Python のコードを見つけられます。二つ目のコマンドでは、
ni.dll(および.objと.lib) ができ、このライブラリは spam と Python 実行形式中の必要な関数をどうやって見つければよいか知っています。Python.hをインクルードする前にPy_NO_LINK_LIBマクロを定義し、手動でリンクします。 リンカーにpythonXY.libを渡さなければなりません。spam と、spam 内の C 関数を使う ni という 2 つの DLL をビルドするには、次のコマンドを使えます。
cl /LD /DPy_NO_LINK_LIB /I/python/include spam.c ../libs/pythonXY.lib cl /LD /DPy_NO_LINK_LIB /I/python/include ni.c spam.lib ../libs/pythonXY.lib
最初のコマンドで、三つのファイル:
spam.obj、spam.dllおよびspam.libができます。Spam.dllには (PyArg_ParseTuple()のような) Python 関数は全く入っていませんが、pythonXY.libのおかげで Python コードを見つけることはできます。二つ目のコマンドでは、
ni.dll(および.objと.lib) ができ、このライブラリは spam と Python 実行形式中の必要な関数をどうやって見つければよいか知っています。
全ての識別子がルックアップテーブル上に公開されるわけではありません。他のモジュール (Python 自体を含みます) から、自作の識別子が見えるようにするには、void _declspec(dllexport) initspam(void) や PyObject _declspec(dllexport) *NiGetSpamData(void) のように、_declspec(dllexport) で宣言せねばなりません。
Developer Studio will throw in a lot of import libraries that you do not really
need, adding about 100K to your executable. To get rid of them, use the Project
Settings dialog, Link tab, to specify ignore default libraries. Add the
correct msvcrtxx.lib to the list of libraries.