mod_pythonをコンパイルしようとしてapxsのバグを見つけた

Pylonsmod_pythonで動かすためにmod_pythonコンパイルしたら次のようなエラーが出た。

$ cd mod_python-3.3.1
$ ./configure
$ make
...
/usr/lib/apr-1/build/libtool --silent --mode=link gcc -o mod_python.la  -rpath  -module -avoid-version    finfoobject.lo hlistobject.lo hlist.lo filterobject.lo connobject.lo serverobject.lo util.lo tableobject.lo requestobject.lo _apachemodule.lo mod_python.lo -L/usr/lib/python2.4/config -Xlinker -export-dynamic -lm -lpython2.4 -lpthread -ldl -lutil -lm
libtool: link: only absolute run-paths are allowed
apxs:Error: Command failed with rc=65536

どうやらlibtoolが-rpathの引数が絶対パスではないと文句を言っているらしい。そもそも-rpathに引数が渡っていないのが原因なので、調べてみるとどうもapxs (APache eXtenSion tool)のバグっぽい。

まず、Makefileからapxsは次のように呼び出されていて、

/usr/bin/apxs -I/home/nozom/rpm/BUILD/mod_python-3.3.1/src/include -I/usr/include/apache2 -I/usr/include/python2.4  -c mod_python.c _apachemodule.c requestobject.c tableobject.c util.c serverobject.c connobject.c filterobject.c hlist.c hlistobject.c finfoobject.c -L/usr/lib/python2.4/config  -Xlinker -export-dynamic        -lm  -lpython2.4   -lpthread -ldl  -lutil   -lm

さらにapxsの中でlibtoolが呼ばれている。
そこでlibtoolを呼ぶ際のコマンドラインがどのように組み立てられているのか見てみると、次のような行が見つかる。

/usr/bin/apxs:418:

        $opt .= " -rpath $CFG_LIBEXECDIR -module -avoid-version $apr_ldflags";

$CFG_LIBEXECDIRを設定しているのは次の箇所:

/usr/bin/apxs:45:

my $libexecdir     = get_vars("libexecdir");
my $CFG_LIBEXECDIR = eval qq("$libexecdir");

get_vars()の中身では以下のようなことをしている:

/usr/bin/apxs:204:

        if (exists $config_vars{$arg} or exists $config_vars{lc $arg}) {
            my $val = exists $config_vars{$arg}
                ? $config_vars{$arg}
                : $config_vars{lc $arg};
            $val =~ s/[()]//g;
            $result .= eval "qq($val)" if defined $val;
            $result .= ";;";
            $ok = 1;
        }

ごちゃごちゃやっているけど要するにここでは$config_vars{"libexecdir"}がevalされている。

%config_varsがどこから来ているかというと、以下の部分。

/usr/bin/apxs:28:

my $installbuilddir = "/usr/lib/apache2/build";
get_config_vars("$installbuilddir/config_vars.mk",\%config_vars);

get_config_vars()は単にファイルの中身を読んでハッシュを作っているだけなのでいいとして、/usr/lib/apache2/build/config_vars.mkを見てみると、

/usr/lib/apache2/build/config_vars.mk:40:

libdir = ${prefix}/lib
libexecdir = ${libdir}/apache2/modules

libexecdirの定義はここにあった。
これなら get_vars("libexecdir") でこの値が返ってきそうなものだが、問題なのはget_vars()の中のeval文が次のように評価されること。

$result .= eval "qq(${libdir}/apache2/modules)"

このとき$libdirという変数は定義されていないので、このeval文は例外で終了してundefを返す。

解決策は簡単で、$libexecdirより前に$libdirを設定しておけばいい。例えば/usr/bin/apxs 45行目の$libexecdirの設定の前に以下を追加する。

my $libdir         = get_vars("libdir");
my $CFG_LIBDIR     = eval qq("$libdir");

修正前のapxsの出力:

$ apxs -q LIBEXECDIR
Use of uninitialized value in concatenation (.) or string at /usr/bin/apxs line 209.
Use of uninitialized value in concatenation (.) or string at /usr/bin/apxs line 209.

修正後のapxsの出力:

$ apxs -q LIBEXECDIR
/usr/lib/apache2/modules

問題はこの修正をどう反映するか。正しい方法はRPMパッケージを作り直すことなんだけど、apxsはapache2の一部なので、このためだけにパッケージを修正するとなるとかなり大がかりな事になってしまう。考えた末に、/usr/bin/apxsを直接修正することにした。apacheがバージョンアップするたびに必要ならこの修正を毎回手作業でしないといけないけど、apacheのバージョンアップ頻度はそんなに高くないし、次のバージョンアップでこの問題が修正されている可能性も十分考えられるから、パッケージ管理にこだわる必要はそれほどないだろう。