ビルド時の留意点

このページでは、 『lsを読まずにプログラマを名乗るな!』 執筆のベースとなっている GNU coreutils 8.21 に関して、 書籍では紙面の都合から記載できなかったものを中心に、 ビルドを実施する場合の留意点について説明します。

Git経由入手時の留意点

patch コマンドのバージョン依存性

バージョン 2.6 よりも前の patch コマンドを使用する環境で ./bootstrap スクリプトを実行した場合、 以下のようなエラーで実行が中断されます。

gnulib/gnulib-tool: *** patch file gl/modules/tempname.diff didn't apply cleanly
gnulib/gnulib-tool: *** Stop.
missing header for unified diff at line 12 of patch
The text leading up to this was:
--------------------------
|
| Files:
| lib/tempname.c
--------------------------
File to patch: EOF ※ Ctrl-D による『EOF』入力
Skip this patch? [y] ※ Enter 入力
1 out of 1 hunk ignored
gnulib/gnulib-tool: *** patch file gl/modules/tempname.diff didn't apply cleanly
gnulib/gnulib-tool: *** Stop.
./bootstrap[348]: build-aux/prefix-gnulib-mk: not found [No such file or directory]
./bootstrap: bootstrap_post_import_hook failed
./bootstrap 実行時のエラー

patch コマンドのバージョン間互換性 (ID:15255)』 に対する修正が取り込まれるまでは、 以下のいずれかの方法で回避してください。

なお、 事前に実行された ./bootstrap が正常終了していた場合、 その際に生成された build-aux/prefix-gnulib-mk が再利用されることにより、 ./bootstrap 実行はそのまま継続され、 パッチ適用が失敗したまま正常終了してしまいます。 patch コマンドのバージョンや、 gl/modules/tempname.diff の内容を変えながら、 ./bootstrap の挙動確認を行う際には注意してください。

patch コマンドのバージョンアップ

使用する patch コマンドを、 2.6 以降にバージョンアップすることで、 この問題は回避可能です。

但し、 2.6 以降の patch コマンドを使用した場合、 『パッチ適用が成功する』のではなく、 『適用できないハンクがあっても無視して正常終了する』 という挙動になってしまいます。

本来意図した成果物をビルドしたい場合は、 次に述べる『パッチファイル自体の修正』を行ってください。

パッチファイル自体の修正

本来意図した成果物をビルドしたい場合は、 不正な内容を持つ gl/modules/tempname.diff に対して、 以下の修正を実施する必要があります。

diff -u a/gl/modules/tempname.diff b/gl/modules/tempname.diff
--- a/gl/modules/tempname.diff
+++ b/gl/modules/tempname.diff
@@ -2,7 +2,7 @@ diff --git a/modules/tempname b/modules/tempname
 index 7fafd72..4703517 100644
 --- a/modules/tempname
 +++ b/modules/tempname
-@@ -1,2 +1,2 @@
+@@ -1,5 +1,5 @@
  Description:
 -gen_tempname() function: create a private temporary file or directory.
 +gen_tempname, gen_tempname_len: create a private temporary file or directory.

上記差分をファイル tempname.diff.patch ファイルに保存した場合、 ソースツリーのルートにおいて以下のコマンドを実行することで、 GNU coreutils に差分が適用されます。

$ patch -p1 < tempname.diff.patch

configure/make 実行時の留意点

GNU MP ライブラリに関するコンパイルエラー

GNU coreutils が提供する exprfactor といったコマンドは、 多倍長演算を行うための GNU MP (gmp) ライブラリ を利用可能な環境では、 GNU MP ライブラリを利用する実装になっています。

GNU coreutils では、 gmp ライブラリの利用可否を以下の方法で判定し、 利用可能な場合はソースコード中の #include <gmp.h> が実施されるようにしています。

  1. __gmpz_init() を参照するソースコードを生成
  2. 上記ソースコードを -lgmp オプション付きでコンパイル
  3. 上記コンパイルの成否で、GNU MP ライブラリの利用可否を判定

上記の判定方法は、 libgmp.so ファイルの有無 (+それが GNU MP ライブラリのものか否か) の判定に関しては妥当なのですが、 『GNU MP ライブラリの利用可否= #include <gmp.h> 実施の可否』 は必ずしも成立するとは限りません。

例えば、 Open Solaris 系列の OS である Open Indiana (Oracle Solaris 11 も?) 環境では、 libgmp.so ファイルは /usr/lib に置かれていますが、 gmp.h ヘッダファイルは /usr/include/gmp/gmp.h に置かれています。 つまり #include <gmp/gmp.h> と記述しなければ、 ヘッダファイル不在でコンパイルエラーとなってしまいます。

  CC     src/expr.o
"src/expr.c", line 54: cannot find include file: <gmp.h>
"src/expr.c", line 182: syntax error before or at: mpz_t
    :
    :

この問題に関しては、 状況を整理した上で GNU coreutils 宛にバグ報告しようと思っていますが、 当面は以下のいずれかの方法で回避してください。

GNU MP ライブラリの無効化

一番簡単な回避方法は、 ./configure 実施の際に 『GNU MP ライブラリを利用しない』旨を表明する --without-gmp オプションを指定する方法です。

$ ./configure --without-gmp

この方法を使用した場合、 exprfactor コマンドは、 GNU MP ライブラリを利用しない実装となるため、 計算可能範囲/実行性能等の面で制約を受ける可能性があります。

『とりあえず ls コマンドのビルドができればよい』 というケースでは、この方法が一番手軽でしょう。

ヘッダファイル読み込みパスの追加指定

この問題の原因は、 ソースコード中の #include <gmp.h> 記述が /usr/include/gmp/gmp.h におかれたヘッダファイルを読み込めないことにあります。

コンパイル時オプションとして -I/usr/include/gmp が指定されれば、 #include <gmp.h> 記述でも /usr/include/gmp/gmp.h ヘッダファイルが読み込まれるようになりますので、 この問題は発生しません。

CPPFLAGS 環境変数に -I/usr/include/gmp を指定した上で ./configure を実施することで、 生成される Makefile では、 CPPFLAGS マクロのデフォルト値として -I/usr/include/gmp が設定されます。

$ (export CPPFLAGS="-I/usr/include/gmp"; ./configure)

以後の make 経由でのコンパイルの際には、 -I/usr/include/gmp が指定されるようになります。

既に ./configure の実行が済んでしまっている場合は、 CPPFLAGS 環境変数を設定した上で再度 ./configure を実行するよりは、 make コマンド実行の際に、 コマンドラインで明示的にマクロ値指定する方が手軽かもしれません。

$ make CPPFLAGS="-I/usr/include/gmp" all-am

GNU coreutils では、 "make" あるいは "make all" を実行した場合、 実際のソースコードのコンパイルは、 内部で再帰的に実行された "make all-am" によって実施されます。

しかし、 『内部で再帰的な "make all-am" を実行』する際には、 コマンドラインでの各種マクロ値指定 (この場合は CPPFLAGS マクロ指定) が反映されません。 結果として、 明示的なマクロ値指定を行っても、 コンパイルの際には無視されることになります。

そのため、 コマンドラインで make に明示的なマクロ値指定をする場合は、 "make all" ではなく "make all-am" を実行する必要があります。

備考: Makefile を書き換えずに、 マクロ定義値を make 実行時に変更するには、 以下のような方法があります。 『make マクロ』として指定する方法と、 『環境変数』として指定する方法では、 値指定の有効範囲が異なりますので注意してください。

make FOO=bar

make 実行の際に、 マクロ FOO の値に bar を設定します。

Makefile 中での FOO マクロ定義よりも、 コマンドラインでの指定値の方が優先されます。

export FOO=bar; make

環境変数 FOO の値に bar を設定した状態で make を実行します。

Makefile 中で FOO マクロが定義されている場合は、 Makefile での定義の方が優先されます。 ("FOO=" のような『空定義』でも、 Makefile 側定義の方が優先されます)

export FOO=bar; make -e

環境変数 FOO の値に bar を設定した状態で make を実行します。

Makefile 中で FOO マクロが定義されている場合でも、 環境変数での定義の方が優先されます。

ソースコードの直接変更

以下の差分を適用して、 ヘッダファイル読み込み処理を、 直接変更してしまう方法もあります。

diff -u a/src/expr.c b/src/expr.c
--- a/src/expr.c        Tue Apr 16 16:01:24 2013 +0900
+++ b/src/expr.c        Fri Jul 26 04:02:59 2013 +0900
@@ -51,7 +51,7 @@
 #endif

 #if HAVE_GMP
-# include <gmp.h>
+# include <gmp/gmp.h>
 #else
 /* Approximate gmp.h well enough for expr.c's purposes.  */
 typedef intmax_t mpz_t[1];
diff -u a/src/factor.c b/src/factor.c
--- a/src/factor.c      Tue Apr 16 16:01:24 2013 +0900
+++ b/src/factor.c      Fri Jul 26 04:02:59 2013 +0900
@@ -88,7 +88,7 @@
 #include <getopt.h>
 #include <stdio.h>
 #if HAVE_GMP
-# include <gmp.h>
+# include <gmp/gmp.h>
 # if !HAVE_DECL_MPZ_INITS
 #  include <stdarg.h>
 # endif

上記差分をファイル gmp.patch ファイルに保存した場合、 ソースツリーのルートにおいて以下のコマンドを実行することで、 GNU coreutils に差分が適用されます。

$ patch -p1 < gmp.patch

lib/parse-datetime.y によるビルドエラー

GNU coreutils の lib/parse-datetime.h および lib/parse-datetime.c は、 lib/parse-datetime.y から GNU bison によって生成されたファイルです。

何らかの契機で、 lib/parse-datetime.h あるは lib/parse-datetime.c よりも、 lib/parse-datetime.y のファイルタイムスタンプ値の方が新しくなってしまった場合、 Makefile における依存定義によって、 GNU bison によるファイル生成処理が実施されてしまいます。

(1) GNU bison がインストールされていない場合や、 (2) インストールされている GNU bison のバージョンが、 lib/parse-datetime.y の処理に必要なバージョン未満の場合、 lib/parse-datetime.hlib/parse-datetime.c の生成が失敗するため、 ビルドが中断されてしまいます。 以下は bison がインストールされていない場合のビルド失敗例です。

  GEN    lib/parse-datetime.c
./build-aux/ylwrap: line 113: bison: command not found

lib/parse-datetime.hlib/parse-datetime.c のタイムスタンプ値の方が、 lib/parse-datetime.y よりも新しければ、 このような生成処理が抑止されますので、 以下のような方法で、 これらのファイルのタイムスタンプを更新してください。

$ touch lib/parse-datetime.[ch]