Python bite: OpenSolaris/SunStudio11 で Python 2.5.2 をビルドする
Tagged:  •  

弊社は受託開発を主業務としていますので、 顧客の要望次第では、 あまり一般的ではない環境での開発も行います。

例えば:

といった環境での開発だったりします。

社内の構成管理(私の趣味?)の都合上、 何とかして Mercurial ぐらいは使えるようにしたいのですが、 そもそも Python をソースからインストールしなければならない、 といった事態もあります。

世間的には、オープンソースプロダクトは configure & make で一発インストール、 と思われている節もありますが、 世の中それ程甘くはありません(特に GCC が無い場合)。

そういうわけで、 非常にニッチではありますが、 折角あちこち咬まれたのですから、 OpenSolaris(Solaris10) + SunStudio11 環境での Pytho 2.5.2 導入に関する情報を公開しておこうと思います (いずれ本家にパッチを contrib したいなぁ)。

基本的な手順

ビルド~インストールまでの基本的な手順を以下に示します。

% export CC="/opt/SUNWspro/bin/cc"
% export CFLAGS="-xarch=v8"
% export CPP="${CC} -xarch=v8 -E"
% ./configure \
        --prefix=/usr/local \
        --without-gcc \
% make \
        CC="/opt/SUNWspro/bin/cc -xarch=v8" \
        CPP="/opt/SUNWspro/bin/cc -xarch=v8 -E" \
        all

% make \
        CC="/opt/SUNWspro/bin/cc -xarch=v8" \
        CPP="/opt/SUNWspro/bin/cc -xarch=v8 -E" \
        install
基本的な手順

CC/CPP 等を環境変数/make マクロの両方で指定しているのは、 本来冗長な筈なのですが、 GNU の make なのか否かとか、 内部的なサブコマンド起動の手順に依存して、 make マクロは引継ぎが想定と異なる場合があるため、 覚悟の上での冗長指定です。

また、32bit バイナリ指定("-xarch=v8") は無くても良いのですが、 うっかり 64bit バイナリでビルドされて変な地雷を踏むことのないように、 念のため指定しています。 人柱体質の人は、 ぜひとも "-xarch=v9" とかでビルドして欲しいものです。

本当は、 これで無事ビルドできれば良いのですが、 残念ながらそうは問屋が卸しません。 以下、トラブル対処の様子を時系列を追って見て行きます。

__attribute__ 対策

Python のソースでは、 ISO C99 標準に準拠していない GCC の固有拡張機能である __attribute__ が使用されています (C99 仕様を確認したわけではないですが、 「Some people object to the __attribute__ feature, suggesting that ISO C's #pragma should be used instead.」 と記されているぐらいですので、 「どちらが適切か?」の議論は別として、 標準化はされていないと見ました)。

一応、 非 GCC 環境で __attribute__ を除外するために、 Py_GCC_ATTRIBUTE マクロが用意されているのですが、 この利用が徹底されていない部分があるため、 以下のような対処が必要です。

diff -r 1d96914254ef Modules/_ctypes/libffi/include/ffi.h.in
--- a/Modules/_ctypes/libffi/include/ffi.h.in   Thu Apr 10 12:53:35 2008 +0900
+++ b/Modules/_ctypes/libffi/include/ffi.h.in   Fri Apr 11 22:35:29 2008 +0900
@@ -60,6 +60,7 @@ extern "C" {

 /* ---- System configuration information --------------------------------- */

+#include <pyport.h> /* ensure Py_GCC_ATTRIBUTE definition */
 #include <ffitarget.h>

 #ifndef LIBFFI_ASM
@@ -218,7 +219,7 @@ typedef struct {
   ffi_cif   *cif;
   void     (*fun)(ffi_cif*,void*,void**,void*);
   void      *user_data;
-} ffi_closure __attribute__((aligned (8)));
+} ffi_closure Py_GCC_ATTRIBUTE((aligned (8)));

 ffi_status
 ffi_prep_closure (ffi_closure*,
diff -r 1d96914254ef Modules/_ctypes/libffi/include/ffi_common.h
Py_GCC_ATTRIBUTE マクロ利用の徹底

各アーキテクチャ固有のヘッダ・ソース (e.g.: Modules/_ctypes/libffi/src/sparc 配下のファイル) なら兎も角、 思い切りアーキテクチャ中立な Modules/_ctypes/libffi/include 配下にこういったソースがあることから、 SunStudio を用いたビルド率が極めて低いことが伺えます。

ssize_t 対策

Include/pyport.h で使用している ssize_t 型は、 OpenSolaris では sys/types.htypedef されているのですが、 このヘッダの読み込みが保証されていないらしく、 以下のような対処が必要です。

diff -r 1d96914254ef Include/pyport.h
--- a/Include/pyport.h  Thu Apr 10 12:53:35 2008 +0900
+++ b/Include/pyport.h  Fri Apr 11 22:35:29 2008 +0900
@@ -110,6 +110,9 @@ typedef PY_LONG_LONG                Py_intptr_t;
  * unsigned integral type).  See PEP 353 for details.
  */
 #ifdef HAVE_SSIZE_T
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
 typedef ssize_t                Py_ssize_t;
 #elif SIZEOF_VOID_P == SIZEOF_SIZE_T
 typedef Py_intptr_t    Py_ssize_t;
ssize_t 型の保証
インラインアセンブラ記述の差異

ここまでの対処で、 随分目処が立ったような気になるのですが、 結局のところ、 以下のような GCC 固有のインラインアセンブラ記述が邪魔をするため、 _ctypes モジュールのビルドは諦めざるを得ません

asm volatile ("iflush %0" : : "r" (closure) : "memory");
asm volatile ("iflush %0" : : "r" (((char *) closure) + 8) : "memory");
互換性の無い asm 記述

alpha プラットフォームだと対処済みの模様ですが、 これは命令キャッシュをクリアする命令の体系が、 レジスタによるアドレス渡しが必要な SPARC に対して、 alpha では簡単な記述 (asm("call_pal 0x86;"); で済む) だからなのでしょう(詳細求ム)。

完全アセンブラの関数を組むのも、 SPRAC なら save/restore を使えば簡単なのですが、 そこまでやるか?というと、 心情的には微妙な感じです (後述するように、_ctypes は Mercurial 稼動上は必要無いですし…)。

モジュールの除外

RELEASE NOTE を読むに、 どうやら SunStudio 12 を使えば、 ここまで述べてきた問題が実は殆ど解決する模様なのですが、 顧客が SunStudio 11 と言ったなら、 SunStudio 11 を使わなければならないところが、 下請けの悲しい宿命です。

結局、与えられた条件下では _ctypes モジュールはビルドできませんから、 これをビルド対象から除外する必要が有ります。

古い世代の人間としては、 Makefile にモジュール一覧を定義したマクロか、 Modules ディレクトリ配下を走査するルール記述等がある、 と思っていたのですが、 Python は素のビルドで出来上がったインタプリタを使用して、 setup.py 経由で拡張モジュールインストール用のライブラリを起動しているのでした。

特定のモジュールをビルド対象から除外するには、 setup.py を直接編集して、 除外対象モジュール名を disabled_module_list 配列の定義に追加する必要が有ります。

diff -r 1d96914254ef setup.py
--- a/setup.py  Thu Apr 10 12:53:35 2008 +0900
+++ b/setup.py  Fri Apr 11 22:49:39 2008 +0900
@@ -15,7 +15,7 @@ from distutils.command.install_lib impor
 from distutils.command.install_lib import install_lib

 # This global variable is used to hold the list of modules to be disabled.
-disabled_module_list = []
+disabled_module_list = [ '_ctypes' ]

 def add_dir_to_list(dirlist, dir):
     """Add the directory 'dir' to the list 'dirlist' (at the front) if
_ctypes モジュールの除外

Google で disabled_module_list を検索しても、 ヒット数が非常に少ないことから、 一般にはフルビルドしかされていないのか、 あるいは setup.py 界隈ではあえて書くまでも無い常識なのでしょう (今時の情報氾濫を考えると、絶対前者だと思いますが)。

ありがたいことに、 _ctypes モジュールが無くても Mercurial の稼動に支障はありませんので、 OpenSolaris において SunStudio11 で生成した Python バイナリで _ctypes を使用したくてこのページを読んでいる方には申し訳ありませんが、 このエントリはこれで終わりです。