Previous: renamed objects, Up: FAQ
このセクションでは,複数の出力ファイルを生成するときに使用可能な 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の実装では,最初の一つがビルドされた後に二番目のファイル の存在を調査するよう十分に賢いものなので,普通は二回実行されないでしょ う.すなわち,すでに存在していることがわかっています.しかし,場合によっ ては二回実行される可能性もあります.
foo data.foo
コマ
ンドが同時に実行されます.これは有害です.
data.foo
)が偽のターゲットであ
る(またはそれに依存する)状況です.
並列した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 Pattern Rule Examples).パター ンルールは移植性がないので,我々はここで議論しませんが,GNU makeを想定したパッケージでは便利になるはずです.