[ << ] [ >> ]           [Top] [Contents] [Index] [ ? ]

26. Automakeに関するよくある質問と答え

この章では,メーリングリストによく上がる質問をカバーします.


26.1 CVSと生成されるファイル


26.1.1 背景:生成されるファイルの配布

AutoconfとAutomakeを用いて作成したパッケージは,`configure'や `Makefile.in'といった生成されるファイルとともに配布されます.これ らのファイルは開発者のホストで生成され,それらをビルドするためにエンド ユーザが管理用のツールをインストールする必要が無いように配布されます. Lexスキャナー,Yaccパーサ,またInfoドキュメントのような,それ以外に生 成されるファイルは,同じ理由で通常配布されます.

例えば,`configure.ac'が変更されたときは`configure'をリビル ドするためにautoconfを実行します.`configure'が対応する `configure.ac'より古くないように,開発者が確実に行なえます.

パッケージで配布されている,生成されたファイルは最新で,tar でタイムスタンプを保存しているので,これらのリビルドのルールはユーザが パッケージを展開しビルドするときも開始されません.


26.1.2 背景:CVSとタイムスタンプ

CVSキーワードを使用していない限り(この状況ではコミット時にファイルを更 新する必要があります),cvs commitcvs import -dの処理中 にCVSが保持しているタイムスタンプは保持されます.

cvs checkoutを使用してファイルをチェックアウトしたとき,そのタ イムスタンプは,チェックアウトしたリビジョンで設定されたタイムスタンプ になります.

しかし,cvs updateしたとき,ファイルの日付が更新されますが, オリジナルのタイムスタンプはこのリビジョンになります.これは, makeでソースファイルが更新されたことに確実に気付くことを意味 します.

このタイムスタンプの変更は,ソースと生成されたファイルの両方をCVSに保 持しているときは面倒です.CVSはアルファベット順にファイルを処理するの で,cvs updateで両方のファイルを更新した後は,チェックイン時 に`configure.ac'より`configure'のほうが新しい場合でも, `configure'より`configure.ac'が古いものとして表されます. makeを呼び出すと,間違って`configure'のリビルドが開始され ます.


26.1.3 Autoconfを利用したプロジェクトにおけるCVSとの共存

基本的に二種類の管理者がいます.生成されるファイルを含め,すべての配布 されるファイルをCVSの元に保持している人,そして,生成されたファイルを CVSの外部に保持している人です.

すべてのファイルをCVSに入れる

生成されるファイルをCVSに入れない

CVSとmakeを平和に動作させる一つの方法は,生成されるファイルを CVSに保存しないことで,すなわち,Makefileのターゲット(派 生ファイルとも呼ばれます)をCVSの制御下におかないことです.

この方法では,開発者は生成されるファイルの変更で悩むことはありません. 全員が異なるバージョンを所有している場合は問題になります(もちろん互換 性があると仮定してもです).結局,タイムスタンプは失われ,ソースファイ ルへの変更は,これまでに議論してきた `Makefile.am'/`Makefile.in'の例のように失われてしまうはずで す.

欠点は,配布されるものの正確なコピーがCVSリポジトリに無いことと,チェッ クアウトしたものをビルド可能にする前に,様々な開発ツール(バージョンが 指定されるかもしれません)をユーザがインストールする必要があるというこ とです.しかし,結局は,CVSの仕事はバージョン管理であり,配布ではあり ません.

開発者が異なるバージョンのツールの使用を可能にすると,配布された開発物 のバグを隠すことにもなります.事実,開発者は,実際のリリースで生成され るファイルの代わりに,(テストであっても)自分が生成したファイルを使用し ます.tarballを準備している開発者は,使用しているツールのバージョンが 間違った出力を生成するものを使用している可能性があり(例えば,移植性の ないCファイル),他の開発者が,独自のバージョンのこのツールを使用してい ない場合,注意されることになるでしょう.


26.1.4 サードパーティーのファイル

(タイムスタンプの問題が無いので)ここでは議論しませんが,それ以外に分類 されるファイルとして,パッケージとともに配布されるけれども,どこでも管 理されていないファイルがあります.例えば,gettextizeautopoint(Gettext由来)や,libtoolize(Libtool由来) のようなツールは,パッケージにファイルをインストールしたり更新したりし ます.

これらのファイルは,CVSに保持しようが保持しまいが,開発者のツール間の バージョンの違いについて,似たようなことが生じます.Gettextのマニュア ルにはこれに関するセクションがあります.CVS Issues: (gettext)CVS Issues section `Integrating with CVS' in GNU gettext toolsを参照して下さい.


26.2 missingAM_MAINTAINER_MODE


26.2.1 missing

missingスクリプトは,いくつかの管理用ツールのラッパーで,要 求される管理用ツールを持っていないユーザのために設計されています.通常, 管理用ツールとは,autoconfautomakebison等です.これらのツールで生成されるファイルは,パッケー ジのその他のファイルとともに配布されるので,ユーザがビルドしたり,それ らがconfigureで調査されている間は,これらのツールは要求されません.

しかし,理由があってリビルドのルールが開始され,足りないツールが呼び出 される場合,missingはユーザに警告します.ツールが無いとき, 警告はされますが,ビルドの継続を可能にする方向で,missingは タイムスタンプを修正を試みます.例えば,autoconfがインストー ルされていない場合,missingは`configure'を touchします.配布されるすべてのファイルがCVSに保持されている 場合,missingのこの機能で,ユーザは管理用ツールが無く ても,cvs updateで暗黙に指定されたタイムスタンプを回避して, CVSからのパッケージをビルドすることが可能になります.

要求されるツールがインストールされている場合,missingはそれ を実行し,異常終了した後は継続しようとしません.これは開発時には正しい もので,開発者は異常終了を修正したいものです.しかし,管理用ツールの違 うバージョンを持っているユーザは,リビルドルールが間違って開始されると き,ビルドが終了しエラーになるかもしれません.ビルドの継続による異常終 了は,AM_MAINTAINER_MODEで主張している論点の一つです.


26.2.2 AM_MAINTAINER_MODE

AM_MAINTAINER_MODEは,"リビルドのルール"の呼び出しをデフォルト で利用不可能にします.`configure.ac'にAM_MAINTAINER_MODEが あって,./configure && makeを実行する場合,makeは `configure',`Makefile.in',LexやYaccの出力などのリビルドを* 決して*試みません.すなわち,一般的に配布されるが,通常ユーザが更新す る必要が無いファイルに対するビルドルールを利用不可能にします.

./configure --enable-maintainer-modeを実行する場合,これらのリ ビルドルールが利用されるようになります.

ユーザ(や自分自身が)が失われたタイムスタンプ(see section CVSと生成されるファイル)でうんざりし たくないからとか,単純に,リビルドルールを使わないようにして管理用ツー ルを明示的に実行したいから,といった理由でAM_MAINTAINER_MODEを 使用します.

AM_MAINTAINER_MODEでは,条件的なカスタムビルドのルールも利用不 可能にすることが可能になります.ユーザが利用不可能なおそれのある外部ツー ルのルールを利用不可能にするため,この機能を使用する管理者もいます.

数年前,François PinardはAM_MAINTAINER_MODEにいくつかの引数 を付けるよう指示しました.それらのほとんどは不安定になり得ます.依存性 を削除することで,非依存のビルドにすることができます.ソースファイルを 変更することで,生成されるファイルに影響がなくなり,このことで,注意さ れないときでも非常に混乱するはずです.彼は,安定は管理者に限定すべきで はなく(--enable-maintainer-modeで提案されたもの),反対だと付け 加えました.ユーザが`Makefile.am'を編集すると,`Makefile.in' を更新する,または警告を出力すべきですが(これがAutomakeで missingを使用する理由です),最終的に望むことは,何も起こらず, ユーザは注意もされないことです(これは,AM_MAINTAINER_MODEでリビ ルドのルールを利用不可能にすることです).

AM_MAINTAINER_MODEマクロを開発したJim Meyeringは,François との議論に動揺し,パッケージからAM_MAINTAINER_MODEを取り除きま した.

すべてのファイルをCVSに保持しているプロジェクトで作業する手助けとなり, 違うバージョンのツールを所有している場合はmissingが十分では ないので,今でも,AM_MAINTAINER_MODEを使用し続けている人はたく さんいます.


26.3 なぜAutomakeはワイルドカードをサポートしないのですか?

開発者は怠けものです.ファイルの追加,削除,または名前の変更のたびに, `Makefile.am'の更新を忘れないようにする必要がないよう, `Makefile.am'でワイルドカードを使用したいときもよくあります.

これにはいくつかの欠点があります.

それらすべてを却下するほとワイルドカードに十分な価値があるという反対意 見があるかもしれませんが,まだ哲学的な欠点もあります.Automakeにワイル ドカードを伝えるためのパッチを書き始める前に,主な技術的な問題を見てい きましょう.それは移植性です.

$(wildcard ...)はGNU makeで動作しますが,他の makeの実装では移植性がありません.

Automakeで$(wildcard ...)をサポートする唯一の方法は, automakeの実行時に$(wildcard ...)を展開することで す.結果として得られる`Makefile.in'には,$(wildcard ...)が 使用されておらず,すべてのファイルをリストアップしているので移植性があ ります.しかしそれは,ファイルを追加,削除,または名前の変更をするたび に,開発者がautomakeを実行する必要があるということを意味します.

`Makefile.am'を編集するより,実際は若干勝っています.確かに, emacs Makefile.am; makeと入力するよりautomake; makeと入 力する方が簡単で速いでしょう.しかし,この構文のサポートを追加するのに 十分なパッチを書くことを邪魔する人はいません.`Makefile.am'や個別 の`Makefile'の断片にファイルリストを生成するスクリプトを使う人も います.

移植性に気を付けていなくても,GNU Makeだけをターゲットにしていて,なん とかして$(wildcard ...)を使用したい場合でも,処理されるファイル をAutomakeが正確に知っている必要がある場所がたくさんあることを知ってお くべきです.Automakeは$(wildcard ...)を展開する方法を知らないの で,これらの場所で使用することは不可能です.$(wildcard ...)は, Automakeが尊重するAC_SUBSTされている変数と比べてブラックボック スになります.

-Wportabilityフラグを使用すると,$(wildcard ...)の構成物 は警告されるはずです.


26.4 distclean後にビルドディレクトリに残っているファイル

これは(Files left in build directory after distclean),make distcheck時に遭遇する可能性がある診断結果です.

配布物に含まれるもので説明したように,make distcheckでは,このようなエラー に対し,パッケージのビルドと調査を試みます.

make distcheckは,パッケージのVPATHのビルドを実行し, make distcleanを呼び出します.make distcleanが実行された 後で,ビルドディレクトリに残っているファイルは,このエラーの後でリスト アップされます.

この診断結果は,実際には二種類のエラーをカバーしています.

残っている前者のファイルは配布されないので,クリーンするよう (see section クリーンされるもの)印が付いているものを修正します.これは明白で,文句を言 われても仕方がありません.

後者のバグは,理解し修正するのが常に容易だというわけではないので,例を 用いて説明します.パッケージにhelp2manを使用してmanページを ビルドしたいプログラムが含まれていると仮定します.GNU help2manは,コマンドの--help--versionの出力 から簡単なマニュアルページを生成します(see (help2man)Top section `Overview' in The Help2man Manual).help2manのインストールをユー ザに強制したくないので,以下のような設定を使用して生成されたmanページ を配布するように決めました.

 
# This Makefile.am is bogus.
bin_PROGRAMS = foo
foo_SOURCES = foo.c
dist_man_MANS = foo.1

foo.1: foo$(EXEEXT)
	help2man --output=foo.1 ./foo$(EXEEXT)

これで,manページを効果的に配布します.しかし,make distcheckは 以下のように異常終了するでしょう.

 
ERROR: files left in build directory after distclean:
./foo.1

なぜ`foo.1'がリビルドされたのでしょうか?その理由は,配布はされな いものの,`foo.1'は配布されないファイル`foo$(EXEEXT)'に依存 しているためです.`foo$(EXEEXT)'はユーザがビルドするので,それは 配布されている`foo.1'より常に新しいものになります.

make distcheckはパッケージ内の矛盾をとらえます.ユーザが help2manをインストールする必要が無いように,`foo.1'を配 布するのが目的でしたが,このルールで今ではファイルが常にリビルドされ, ユーザはどうしてもhelp2manが必要になります. `foo.1'がユーザにリビルドされないことを確実にする,または `foo.1'の配布を諦めるかのいずれかにすべきです.

より一般的には,配布されるファイルのルールには,配布されないビルドされ るファイルに決して依存しないようにすべきです.生成されたものを配布する 場合,そのソースを配布してください.

上記の例を修正する一つの方法として,配布される`foo.1'が `foo$(EXEEXT)'に依存しないようにします.例えば,foo --versionfoo --helpが,`foo.c'や`configure.ac' が変更されない限り変更されない状況では,以下のような`Makefile.am' を書くことが可能です.

 
bin_PROGRAMS = foo
foo_SOURCES = foo.c
dist_man_MANS = foo.1

foo.1: foo.c $(top_srcdir)/configure.ac
        $(MAKE) $(AM_MAKEFLAGS) foo$(EXEEXT)
	help2man --output=foo.1 ./foo$(EXEEXT)

この方法では,`foo.1'は`foo$(EXEEXT)'が変更されるたびにリビ ルドされません.makehelp2manの前に `foo$(EXEEXT)'を確実に更新します.これを確実にするもう一つの方法 は,バイナリとmanページに対して別のディレクトリを使用し, SUBDIRSをmanページがビルドされる前にバイナリがビルドされるよう に設定することです.

`foo.1'を配布しないように決定することも可能です.この状況では, `foo.1'が`foo$(EXEEXT)'に依存するようにすると両方ともリビル ドされるので優れています.しかし,`foo.1'のビルドで `foo$(EXEEXT)'を実行するので,クロスコンパイルでパッケージ をリビルドすることは不可能です.

そのようなエラーがあるもう一つの状況は,配布されるファイルがパッケージ でビルドされるツールを用いてビルドされるときです.パターンは似ています.

 
distributed-file: built-tools distributed-sources
        build-command

以下のように変更すべきです.

 
distributed-file: distributed-sources
        $(MAKE) $(AM_MAKEFLAGS) built-tools
        build-command

または,クロスコンパイルで問題になる場合は`distributed-file'を配 布しないように選択することも可能です.

これらの例に意味がある状況をまとめます.

  • 配布されるファイルは,配布されないがビルドはされるファイルに決して依存 すべきではない.

  • 配布されるファイルは,その依存性で配布されるべきである.

  • ファイルがユーザにリビルドされるよう意図される場合,それを配布 する場所はない.

絶望的な状況では,配布物に含まれるもので説明したように, distcleancheck_listfilesを設定することで,この調査を利用不可能 にすることが可能です.こうする前に,make distcheckが文句を言う 理由を必ず理解して下さい.distcleancheck_listfilesエラー を隠す方法で,それを修正するものではありません.良いようにして下さい.


26.5 なぜオブジェクトファイルの名前を変更することがあるのですか?

これは,ターゲットごとにコンパイルフラグが使用されているとき発生します. オブジェクトファイルは,同じソースから異なるフラグを用いてコンパイルさ れるオブジェクトが互いに壊されないように,名前を変更する必要があります. 以下の例を考えて下さい.

 
bin_PROGRAMS = true false
true_SOURCES = generic.c
true_CPPFLAGS = -DEXIT_CODE=0
false_SOURCES = generic.c
false_CPPFLAGS = -DEXIT_CODE=1

二つのプログラムが同じソースからコンパイルされ,同じオブジェクトが共有 される場合は問題になるのは明らかで,それは,`generic.o'は -DEXIT_CODE=0-DEXIT_CODE=1の両方を用いてコンパ イルすることが不可能だからです.このため,automakeは二つの異 なるオブジェクト,`true-generic.o'と`false-generic.o'をビル ドするルールを出力します.

automakeは,オブジェクトファイルの名前を変更する必要があるか どうかを決定するため,実際にはソースファイルが共有されているかどうかを 見ていません.ターゲットごとのコンパイルフラグを使用していることが判明 したときに,すべてのターゲットのオブジェクトの名前を変更するだけです.

ターゲットごとのコンパイルフラグが使用されていないときは,オブジェクト ファイルを共有しても問題ありません.例えば以下の例のように, `true'と`false'が両方とも`version.o'を使用しているとき です.

 
AM_CPPFLAGS = -DVERSION=1.0
bin_PROGRAMS = true false
true_SOURCES = true.c version.c
false_SOURCES = false.c version.c

オブジェクトファイルの名前の変更は,_SHORTNAME変数にも影響され ることに注意して下さい(see section プログラムとライブラリの変数).


26.6 多くの出力を生成するツールを処理する

このセクションでは,複数の出力ファイルを生成するときに使用可能な makeの特色を記述します.それはAutomake特有のものではありませ んが,通常の`Makefile'で使用することが可能です.

一つの`data.foo'というファイルを読み込み,二つの`data.c'と `data.h'という名前のファイルを生成する,fooというプログ ラムを想定します.我々は,以下の一つのものから二つのものへの依存をとら える`Makefile'ルールを書こうとしています.

そのままのルールは間違っています.

 
# This is incorrect.
data.c data.h: data.foo
        foo data.foo

上記のルールが実際に伝えているのは,`data.c'と`data.h'がそれ ぞれ`data.foo'に依存していて,それぞれがfoo data.fooの実行 でビルド可能であるということです.言い替えると,以下と等価です.

 
# We do not want this.
data.c: data.foo
        foo data.foo
data.h: data.foo
        foo data.foo

これは,fooを二回実行するはずだということを意味します. makeの実装では,最初の一つがビルドされた後に二番目のファイル の存在を調査するよう十分に賢いものなので,普通は二回実行されないでしょ う.すなわち,すでに存在していることがわかっています.しかし,場合によっ ては二回実行される可能性もあります.

並列したmakeの動作を解決し,偽の依存性は解決できない方法とし て以下のものがあります.

 
data.c data.h: data.foo
        foo data.foo
data.h: data.c

上記のルールは以下と等価です.

 
data.c: data.foo
        foo data.foo
data.h: data.foo data.c
        foo data.foo

これで,並列したmakeは,`data.c'と`data.h'の連続し たビルドになり,二番目は一回目で終っているので,もはや不要であることを 検出します.

このパターンを使用することで,おそらくほとんどの状況を十分満たすでしょ う.しかし,出力ファイルが多くなるにつれ(この手法では,すべての出力ファ イルを依存関係の完全な順番にする必要があり)難しくなるので,我々はより 複雑な解決方法を探りました.

もう一つの考えは,以下のように書くことです.

 
# There is still a problem with this one.
data.c: data.foo
        foo data.foo
data.h: data.c

この考えは,foo data.fooは`data.c'を更新する必要があるとき だけ実行されますが,我々は,`data.h'が`data.c'に依存するとい う一歩先の立場にいることになります.つまり,`data.h'が必要になり, `data.foo'が古い場合,`data.c'の依存性によってビルドが開始さ れるでしょう.

これでほとんど完璧ですが,我々は,`data.h'と`data.c'をビルド しておき,その後で`data.h'を削除することを提案します.そうするこ とで,make data.hのとき`data.h'はリビルドされません.上記 のルールでは,`data.foo'に対応して`data.c'は更新する必要があ りますが,既にそうなっています.

我々が必要なものは,`data.h'がないときにリビルドを強制するルール です.以下のようにします.

 
data.c: data.foo
        foo data.foo
data.h: data.c
        @if test -f $@; then :; else \
          rm -f data.c; \
          $(MAKE) $(AM_MAKEFLAGS) data.c; \
        fi

上記では,出力と入力が多くなっても簡単です.出力の一つは,コマンド実行 の証拠として提供されるために必要なものが分かり,それはすべての入力に依 存し,それ以外の出力ファイルもすべてそれに依存します.例えば, fooに`data.bar'の読み込みを追加し,`data.w'と `data.x'も出力させる場合,以下のように書くでしょう.

 
data.c: data.foo data.bar
        foo data.foo data.bar
data.h data.w data.x: data.c
        @if test -f $@; then :; else \
          rm -f data.c; \
          $(MAKE) $(AM_MAKEFLAGS) data.c; \
        fi

この設定では,ちょっとした問題が残っています.fooの出力は四 つのファイルですが,これらのファイルが生成される順序が分かりません. `data.h'が`data.c'の前に作成されると仮定します.そうなると奇 妙な状況に陥ります.次にmakeが実行されると,`data.h'は `data.c'より古くなり,二番目のルールが開始され,シェルは if...fiコマンドを実行しますが,実際にはthenの中だけが実 行され,すなわち何も起こりません.言い替えると,我々が選択した証拠は fooが生成する最初のファイルではないので,makeは実 行しても何もしないシェルを開始します.

簡単な答えは,これが生じたときにタイムスタンプを修正することです.

 
data.c: data.foo data.bar
        foo data.foo data.bar
data.h data.w data.x: data.c
        @if test -f $@; then \
          touch $@; \
        else \
          rm -f data.c; \
          $(MAKE) $(AM_MAKEFLAGS) data.c; \
        fi

もう一つの解決方法は,以前のものとは互換性がありませんが, fooの出力するものではなく,別の専用ファイルを証拠として使用 することです.

 
data.stamp: data.foo data.bar
        @rm -f data.tmp
        @touch data.tmp
        foo data.foo data.bar
        @mv -f data.tmp $@
data.c data.h data.w data.x: data.stamp
        @if test -f $@; then \
          touch $@; \
        else \
          rm -f data.stamp; \
          $(MAKE) $(AM_MAKEFLAGS) data.stamp; \
        fi

fooが実行される前に`data.tmp'が作成されるので,そのタイ ムスタンプはfooが出力するファイルより古くなります.そして, fooが失敗した場合は`data.stamp'を更新したくないので, `data.stamp'をfooの実行後に名前を変更します.

このような専用の証拠を使用することで,出力ファイルのリストが前もって分 からないときも簡単に処理できます.例として,単一のコマンドで,たくさん の`*.el'ファイルを`*.elc'にコンパイルする,以下のルールを考 えてください.ELFILESの定義方法は(それが空ではない限り)問題にな りません(空のターゲットはPOSIXでは受け入れられません).

 
ELFILES = one.el two.el three.el …
ELCFILES = $(ELFILES:=c)

elc-stamp: $(ELFILES)
        @rm -f elc-temp
        @touch elc-temp
        $(elisp_comp) $(ELFILES)
        @mv -f elc-temp $@

$(ELCFILES): elc-stamp
        @if test -f $@; then \
          touch $@; \
        else \
          rm -f elc-stamp; \
          $(MAKE) $(AM_MAKEFLAGS) elc-stamp; \
        fi

仕上げに,GNU makeは,パターンルールを使用することで複数の出 力ファイルを用いたルールを表現することが可能です(see (make)Pattern Examples section `Pattern Rule Examples' in The GNU Make Manual).パター ンルールは移植性がないので,我々はここで議論しませんが,GNU makeを想定したパッケージでは便利になるはずです.


[ << ] [ >> ]           [Top] [Contents] [Index] [ ? ]

This document was generated by Akihiro Sagawa on June, 8 2005 using texi2html 1.70.