Previous: renamed objects, Up: FAQ


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

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

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

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

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

上記のルールが実際に伝えているのは,data.cdata.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.cdata.hの連続し たビルドになり,二番目は一回目で終っているので,もはや不要であることを 検出します.

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

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

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

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

これでほとんど完璧ですが,我々は,data.hdata.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

上記では,出力と入力が多くなっても簡単です.出力の一つは,コマンド実行 の証拠として提供されるために必要なものが分かり,それはすべての入力に依 存し,それ以外の出力ファイルもすべてそれに依存します.例えば, foodata.barの読み込みを追加し,data.wdata.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.hdata.cの前に作成されると仮定します.そうなると奇 妙な状況に陥ります.次にmakeが実行されると,data.hdata.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.stampfooの実行後に名前を変更します.

このような専用の証拠を使用することで,出力ファイルのリストが前もって分 からないときも簡単に処理できます.例として,単一のコマンドで,たくさん の*.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を想定したパッケージでは便利になるはずです.