読者です 読者をやめる 読者になる 読者になる

OTOBANK Engineering Blog

オトバンクはコンテンツが大好きなエンジニアを募集しています!

configureスクリプトとは何なのか

勉強会

おはこんばんちは!! 尾藤 a.k.a. BTO です。

みなさん Unix のパッケージをインストールする時、configureスクリプト実行しますよね。 なんかいっぱいいろんな事してるみたいで便利そうですよね。 でも実際は何をしてるんでしょうね。

configureスクリプトの出力見てますか? エラーが起きたときしか見てない事ないですか?

先日、よく使ってるんだけどよく知らないと思われる configure スクリプトの正体について、オトバンクで勉強会をやりました。

魔法の呪文

./configure && make && make install

よく打っているコマンドだと思いますが、これだけで解決します。 素晴らしいですね!!

GNU Autotools

configure スクリプトはもちろん手動で作りません。 あんなもの手動でメンテナンスしてたら日が暮れてしまいますよ。

configure スクリプトと、それに伴う各種ファイルは、GNU Autotoolsと呼ばれるパッケージで生成します。 GNU Autotoolsは、下記の3つのパッケージで構成されます。

  • autoconf
  • automake
  • libtool

では、GNU Autotools はどのような機能を提供してくれるのでしょうか。

非互換性の解消

C や C++ のコンパイラはたくさん存在します。 またヘッダファイルも書いてる内容が全て同じではなく、微妙に内容が違ってる事もあります。 同じ言語なのに、このような非互換性に昔から開発者は悩まされていました。 この非互換性を解消し、ポータビリティの高いプログラムを書けるようにするのが、GNU Autotoolsの最大の目的です。

どの環境でも必ず動く

GNU Autotoolsが生成する configure や Makefile は、Bourne Shellmake さえあれば動きます。 この2つのツールは、ほとんどのUnixシステムで動作するので、Unixシステムであればどの環境でも必ず動作します。 ビルド環境を提供するのに、最低限の依存関係になっていますので、ポータビリティが向上します。

よく configure の実行に autoconf が必要だとか、make の実行に automake が必要だと勘違いしている人がいますが、大きな間違いです。

ちなみに Bourne Shell が最初に登場したのは1977年で、僕が産まれた年と同じです。 Bourne Shell と同級生!!

いろいろな便利機能

インストールディレクトリが簡単に変更できたりとか、make distってやるだけで foo-1.0.0.tar.gz 作ってくれたりとか、いろいろ便利機能ついてきます。

C言語の非互換性問題

上述した通り、C には非互換性の問題がいろいろあります。

  • ANSI準拠の標準ヘッダがあるか
  • size_t が typedef されてるか
  • const が使えるか
  • ...

といくつかあげてみましたが、まあいろいろ問題があるわけです。 これをあらゆる環境を考慮してプログラムを書くのは、とても大変です。

独自の対応

簡単なものであれば、独自に対応するので大丈夫でしょう。

例えば、BSD だと BSD_SIZE_T が定義されているので、下記のように定義できます。

#define _BSD_SIZE_T_ typedef unsigned int size_t;
#ifdef  _BSD_SIZE_T_
typedef _BSD_SIZE_T_    size_t;
#undef  _BSD_SIZE_T_
#endif

ただこれは BSD ではうまく動作しますが、size_t が typedef されてない環境では動作しません。

imake の登場

C の非互換性を解消するツールとして、X Window System のツールのコンパイルに使用されている imake が登場しました。 imake はポータビリティの高い Makefile を生成してくれます。 我々はついに非互換性を解消するツールを手に入れた!!

imake の問題点

imake は巨大なデータベースでした。 OS、コンパイラ、ライブラリ、バージョンの違いをデータベース化し、それを元に Makefile を生成します。 しかし、このアプローチには様々な問題がありました。

  • データベースの更新が大変
  • 細かなバージョン差異の吸収が大変
    • 同じバージョンでも、インストールされるサブセットが違う
    • 不具合が修正されて、逆にコンパイルが通らなくなった(本当は良い事なのに)

プラットフォームやバージョンの差異で分ける事に限界がありました。

configureスクリプトの登場

(いつなのか、どこなのか僕も知りませんが、)とあるプロジェクトで configure スクリプトが登場したそうです。 その configure スクリプトは imake と違って情報をデータベース化しませんでした。 代わりにその場で非互換性のある問題点をチェックし、ヘッダファイルや Makefile を生成するようにしたのです。

その場で機能毎に個別にチェックするわけですから、環境やバージョンの違いは関係ありません。 またバージョンが上がって、変更された場合でもうまく動作します。

我々はついに理想のビルドツールを手に入れた!!

configureでチェックする例

例えばこんなプログラムのコンパイルができるか調べてみます。

#include <stdio.h>
int main() {
  size_t t;
  printf("%lu\n", sizeof(t));
}

コンパイルに失敗したら typedef する

typedef unsigned int size_t;

とまあ各機能がちゃんと使えるかどうか、その場で確認すればいいわけです。

初期の configure

初期の configure は、ただの shell script ですから、人が管理してました。 他のプロジェクトの良さそうな configure を持ってきて、自分のプロジェクトに取り込む。 当然この方法では、まともにメンテナンスできません。

autoconf の登場

そこで登場したのが autoconf です。 autoconf は主な目的は configure スクリプトの生成です。

autoconf は shell script 用の GNU m4 マクロの塊になってます。 m4 って今では使われる事がほとんどないので、聞き慣れないと思いますが、テキスト出力用のテンプレート言語です。 悪名高い sendmail.cf の生成なんかにも使われています。

configure.ac に autoconf の m4 マクロを書いて、configure スクリプトを生成します。

configure

生成された configure スクリプトは、ただの Bourne Shell script です。 実行すると、関数, typedef, ライブラリ等の各種チェックをしてくれます。 チェックした結果を元に config.h というヘッダファイルや Makefile を生成してくれます。

config.h や Makefile に限らず、configure の結果を元に任意のファイルを生成できます。 基本的には、xxxxxx.yyy.in から xxxxxx.yyy を生成するというように、ファイル名の .in をとったファイルを生成してくれます。 実際、 config.h.inMakefile.in がプロジェクトのファイルに見つかるはずです。

configure はファイルを生成するときに、@変数名@ の部分を、実際の変数の値に置換してくれます。 例えば Makefile.in の中を見ると、

prefix = @prefix@

と書かれた行が見つかります。 生成された Makefile を見ると、@prefix@ の部分が置き換わっているのが分かります。

prefix = /usr/local

もちろん、configure の --prefix オプションを指定すると、ここの値が変わります。

他にも config.h.in にある余計な定義のコメントアウトもしてくれます。 例えば、config.h.in に ulong を undef するように定義していたとします。

/* Define if ulong is not defined */
#undef ulong

configure をした結果、これが必要ないと分かったので、config.h にはこの行をコメントアウトした結果が出力されます。

/* Define if ulong is not defined */
/* #undef ulong */

configure.ac

configure.ac の例をあげます。

AC_INIT([foo],[0.0.1],[otobank@example.com])
AM_INIT_AUTOMAKE(foo, 0.0.1)
AC_CONFIG_SRCDIR([config.h.in])
AC_CONFIG_HEADERS([config.h])
AC_PROG_CC
AC_HEADER_STDC
AC_CHECK_HEADERS([unistd.h])
AC_TYPE_SIZE_T
AC_FUNC_MALLOC
AC_CHECK_FUNCS([strdup])
AC_CONFIG_FILES([Makefile])
AC_OUTPUT

意味が分からないかもしれないですが、AC_XXXX みたいなのが全部 m4 マクロです。 各マクロの詳細は autoconf のマニュアルにのってます。

重要なのは、こんな短い定義で、あの糞長い configure スクリプトを生成してくれるところですね。

autoscan

いきなり configure.ac を書けと言われても、何を書けばいいのかまったく分かりません。 そんなあなたは autoscan を実行しましょう。 autoscan を実行すると、ソースコードを再帰的に検査し、お勧めの設定を configure.scan に書き出してくれます。 最初は configure.scan をベースに configure.ac を作るといいでしょう。

aclocal

説明してませんでしたが、configure.ac の中に AM で始まるマクロがありました。 このマクロは、autoconf ではなく automake に含まれます。

このままだと automake のマクロが認識できないので、必要なマクロをローカルに取り込むのが aclocal です。 aclocal を実行すると、aclocal.m4が生成され、無事に automake のマクロが使えるようになります。

他にもプロジェクト独自のマクロを取り込むのにも使われます。

autoheader

autoheaderを実行すると、configure.acを読み込んで、config.h.inを生成してくれます。

automake

automake は autoconf から独立したツールです。 独立したツールですが、autoconf の add-on として設計されているので、autoconf とセットで使うと覚えておいて間違いないでしょう。

automake は Makefile.am から Makefile.in を生成します。

Makefile.am

Makefile.am の例です。 基本的には、コンパイルに必要なソースファイルを定義します。 それだけであとはよろしくやってくれます。

bin_PROGRAMS = foo
foo_SOURCES = foo.c foo.h
lib_LTLIBRARIES = libbar.la
libbar_la_SOURCES = bar.c bar.h

この例だと、foo というコマンドを生成するのに、foo.c, foo.h が必要で、libbar というライブラリを生成するのに必要なのが、bar.c, bar.h という風に定義しています。

便利な Makefile

automake が生成してくれる Makefile は機能満載で、とても便利です。 例えば、下記のような機能を提供してくれます。

  • インストール先を簡単に変更
  • make dist 一発で tarball を生成
  • インクルードパスは自動で追加(ディレクトリを変更しても深くなっても大丈夫)
  • make distclean 一発で初期化
  • configure.ac, Makefile.am が変わった場合は自動で更新してくれる

libtool

libtool はライブラリを生成するコマンド用のラッパースクリプトです。 中身はただの Bourne Shell script なので、どの環境でも動作します。

ライブラリの生成は、バイナリの形式(a.out, ELF, Mach-O ...)によってコマンドが違い、オプションも異なってきます。 それを全て統一的に扱うのは大変なので、libtool ではその差異を吸収し、同じインターフェースを提供しています。

libtoolize

libtool をプロジェクト内部に取り込んでくれるコマンドです。 普通は autoconf が呼び出してくれるので、直接実行することはほぼありません。

結論

オトバンクではインフルエンザが流行っています。