5. 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" になります。


5.1. 型どおりのアプローチ
=========================

Windows での拡張モジュールのビルドには、Unix と同じように、
"distutils" パッケージを使ったビルド作業の制御と手動の二通りのアプロー
チがあります。 distutils によるアプローチはほとんどの拡張モジュールで
うまくいきます; "distutils" を使った拡張モジュールのビルドとパッケージ
化については、 Python モジュールの配布 (レガシーバージョン) にあります
。これらを本当に手動で行わなければならないとわかった場合、標準ライブラ
リモジュールの winsound のプロジェクトファイルが学習に有益かもしれませ
ん。


5.2. 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 *" に似ています;  この操作では個別にコードのコピ
ーを生成します。


5.3. DLL 使用の実際
===================

Windows 版の Python は Microsoft Visual C++でビルドされています; 他の
コンパイラを使うと、うまく動作したり、しなかったりします (Borland も一
見うまく動作しません)。この節の残りの部分は MSVC++ 向けの説明です。

Windows で DLL を作成する際は、 "pythonXY.lib" をリンカに渡さねばなり
ません。例えば二つの DLL 、spam と ni (spam の中には C 関数が入ってい
るとします) をビルドするには、以下のコマンドを実行します:

   cl /LD /I/python/include spam.c ../libs/pythonXY.lib
   cl /LD /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 は必要もなく大量のインポートライブラリを DLL に突っ込
んで、実行形式のサイズを 100K も大きくしてしまいます。不用なライブラリ
を追い出したければ、「プロジェクトのプロパティ」ダイアログを選び、「リ
ンカ」タブに移動して、 *インポートライブラリの無視* を指定します。その
後、適切な "msvcrtxx.lib" をライブラリのリストに追加してください。
