10 ==================================
11 この章ではOMakeのビルド体系をさらにもう少し解説します。この議論を決定するただ一つの結論としては、OMakeは全体のプロジェクト解析を元にしているということです。これはあなたがプロジェクト全体の設定を決めて、そしてOMakeのインスタンスを実行することを意味しています。
13 一つのディレクトリで構成されたプロジェクトではあまり意味がないかもしれませんが、多数のディレクトリからなるプロジェクトでは大きな意味を持ちます。 ``GNU make`` では、あなたは通常、プロジェクトの各々のディレクトリにおいて ``make`` プログラムを再帰的に呼び出すでしょう。例えば、あなたが現在サブディレクトリ ``lib`` と ``main`` が入っているソースディレクトリ ``src`` を含んでいるプロジェクトをいくつか持っているものとしましょう。具体的には、あなたのプロジェクト構成は以下のアスキーアートのようになります。 ::
21 | `---> source files...
26 一般的に ``GNU make`` では、初めに ``my_project/`` の ``make`` インスタンスを呼び出します。この ``make`` インスタンスは ``src/`` ディレクトリ内の ``make`` インスタンスを呼び出し、そして ``lib/`` と ``main/`` の新しいインスタンスを呼び出します。つまり、 ``GNU make`` では単純に、プロジェクト内の ``Makefile`` の数だけ ``make`` インスタンスが生成されることになります。
28 大量のプロセスを処理することは今日のコンピュータにとってさほど大きな問題ではありません(ときどき著者の意見に反対する人もいるようですが、私たちはもはや1970年代に住んでいるわけではないのです)。この構造に関する問題としては、各々の ``make`` プロセスが分けられた設定を用いており、そしてそのすべてが調和のとれたものにするには、非常に多くの負担になってしまうという点です。さらには、プログラマが ``main/`` ディレクトリで ``make`` プログラムを実行し、 ``lib/`` がもう使われていない物である場合を考えてみましょう。この場合、 ``make`` は幸せそうにあちこちに曲がりくねった挙句、恐らく ``lib/`` をリビルドしようと奮起して、恐らく諦めることになるでしょう。
30 OMakeではこの構造を抜本的に変更します。とは言っても実際の変更点はそれほどありません。ソース構造は非常に似通っています。私たちは単純にいくつかの"O"を以下のアスキーアートのように加えただけです。 ::
33 |--> OMakeroot (or Root.om)
39 | `---> source files...
44 各々の ``<dir>/OMakefile`` の役割は各々の ``<dir>/Makefile`` の役割と同様で、どのように ``<dir>`` のソースファイルをビルドするのかを設定します。 ``OMakefile`` は ``Makefile`` の構造や文法を保持しておりますが、ほとんどの場合 ``make`` よりも簡単に記述することができます。
46 一つ小さな違いがあるとすれば、プロジェクトのルートディレクトリに ``OMakeroot`` が存在している点です。このファイルの主要な目的は、第一にプロジェクトのルートディレクトリがどこにあるかを示すことです(omakeがサブディレクトリから呼び出されたときのためです)。 ``OMakeroot`` はブートストラップとして働きます。omakeはこのファイルを最初に読み込むことで実行されます。それ以外では、 ``OMakeroot`` の文法と機能は他の ``OMakefile`` と全く同様です。
48 *大きな* 違いは、OMakeは *グローバルな* 解析を行うという点です。以下はどのようにomakeが動作するのかを示します。
50 1. omakeはOMakerootファイルがあるディレクトリに移動し、読んでいきます。
51 2. 各々の ``OMakefile`` は ``.SUBDIRS`` ターゲットを使用して ``OMakefile`` があるサブディレクトリを指し示します。例えば、 ``my_project/OMakefile`` は以下のルールを持っていたとします。 ::
55 omakeはこれらのルールを読んで、プロジェクト内のすべてのOMakefileを評価します。読み込みや評価は高速に行われるので、このプロセスは大変ではありません。
57 3. 全体の設定が読まれたら、omakeはどのファイルが使われていないのかを決定し(グローバルな解析を使用します)、ビルド作業を開始します。これはどのファイルのビルドが実際に必要なのかに依存した、ビルド時間がかかります。
59 このモデルではいくつかの利点があります。初めに、解析をグローバルにしたことで、結局たった一つの設定ファイルを用いることとなり、ビルドに関する設定の一致が保証されるという点です。別の利点はビルドに関する設定が継承され、再利用可能となり、さらに階層構造となる点です。概して、ルートの ``OMakefile`` はいくつかの標準的な決まり文句と設定を定義しており、これはサブディレクトリによって継承され、調整したり、変更することができます(全体を書き換える必要はありません)。この方法の欠点は容量が増大することで、これは結局グローバルな解析を行っているためです。が、実際にはめったに考慮する必要があるようには見えません。OMakeは大きなプロジェクトにおいてさえ、あなたが使っているウェブブラウザよりもはるかに小さい容量しか使いません。
61 GNU/BSDのmakeユーザは以下の点を頭に留めておいてください。
63 * OMakeは ``Makefile`` と同じくらい多くのファイルを作ります。文法は似ており、makeと同様に多くのビルドイン関数が用意されています。しかしながら、この2つのビルドシステムは同じではありません。OMakeではいくつかの酷い機能(これは著者の意見です)が外されており、それに代わって新しい機能が追加されています。
64 * OMakeはWin32を含んだ、複数のプラットフォーム上で同様に動きます。あなたは複数のプラットフォーム上で動かすためにコードを変更したり、いくつかのトリッキーなテクを使ったり、 ``$(OSTYPE)`` 変数を使ってビルド設定を調節する必要はありません。
65 * OMakeの依存関係の解析はMD5によるファイルの要約を元にしています。これはつまり、依存関係の解析はファイルの「修正日時」ではなくファイルの「内容」を元にしていることを表しています。さあ、ローカル日時とファイルサーバの日時から生じるミスマッチや、間違ったタイムスタンプの変更によるビルドミスにおさらばしましょう。
72 3.1 OMakeroot vs. OMakefile
73 ----------------------------------
74 さて、例を見せる前に、一つ質問をしてみましょう。それは「プロジェクトルートの ``OMakeroot`` と ``OMakefile`` の違いは何か?」というものです。その質問に関する端的な答えは「違いはないが、あなたは必ず ``OMakeroot`` ファイル(あるいは ``Root.om`` ファイル)を作らなければならない」です。
76 しかしながら、通常の範囲で用いるならば ``OMakeroot`` は変更する必要のない決まり文句が並んでいるファイルであり、すべてのプロジェクトの ``OMakeroot`` は多かれ少なかれ同じような内容となるでしょう。
78 OMakeを始めるために、あなたがこのような決まり文句を入力する必要はありません。ほとんどの場合において、あなたは以下の手順をプロジェクトのルートディレクトリで実行するだけです。
80 * omake --installをプロジェクトのルートで実行する。
82 これで最初の ``OMakeroot`` と ``OMakefile`` が生成され、編集できるようになりました。
85 single: DefineCommandVars()
89 ------------------------------
90 OMakeを始めるためには、まず簡単なサンプルを見せるのが手っ取り早いでしょう。いま私たちは以下のファイルを含んだディレクトリツリーを持っているものとします。 ::
108 以下は ``OMakeroot`` , ``OMakefile`` リストのサンプルです。 ::
110 my_project/OMakeroot:
111 # Cアプリケーションの標準的な設定をインクルード
117 # このディレクトリのOMakefileをインクルード
120 my_project/OMakefile:
127 my_project/src/OMakefile:
128 # あなたの好きなようにオプションを付け加えます
134 my_project/src/lib/OMakefile:
135 # 静的ライブラリとしてライブラリをビルドします。
136 # これはUnix/OSX上ではlibbug.aとして、
137 # Win32上ではlibbug.libとしてビルドされます。
138 # 引数にはソースファイルの拡張子を入れていないことに注意してください。
139 StaticCLibrary(libbug, ouch bandaid)
141 my_project/src/main/OMakefile:
142 # いくつかのファイルは../libディレクトリ上の
143 # .hファイルをインクルードしています。
146 # どのライブラリに対してリンクしたいのかを指定
151 # Win32上ではhorsefly.exe、
152 # Unix上ではhorseflyとしてビルドされます。
153 # 最初の引数はアプリケーション名を指定します。
154 # 二番目の引数はソースファイルの配列を指定します(拡張子は抜いてください)。
155 # これらの配列はビルドするプログラムの一部となります。
156 CProgram(horsefly, horsefly main)
158 # デフォルトでこのプログラムをビルドします
159 # (他の引数を指定しないでomakeが実行される場合です)。
160 # EXE変数はWin32上では.exeとして定義されていますが、
161 # 他のプラットフォーム上では空の変数です。
162 .DEFAULT: horsefly$(EXE)
164 ほとんどの設定は ``build/C.om`` (これはOMakeの機能の一部です)ファイルに定義されています。このファイルはほとんどの作業の面倒を見てくれます。具体的には、
166 * Cライブラリやプログラムを正当な方法でビルドするための、 ``StaticCLibrary`` と ``CProgram`` 関数を定義しています。
167 * 依存関係を特定するために各々のソースコードを調べていくメカニズムを定義しています。これはつまり、Cソースファイルのための ``.SCANNER`` ルールを定義していることを意味しています。変数はサブディレクトリにも継承されていき、例えば、 ``src/main/OMakefile`` の ``CFLAGS`` 変数の値は ``"-g -O2"`` となります。
171 single: OCamlLibrary()
172 single: OCamlProgram()
176 ------------------------------------
177 前回のCの代わりにOCamlを使った状態で、簡単なサンプルを作ってみましょう。今回では、ディレクトリツリーは以下のようになります。 ::
195 ``OMakeroot`` , ``OMakefile`` のリストは前回と少し違います。 ::
197 my_project/OMakeroot:
198 # OCamlアプリケーションの標準的な設定をインクルード
204 # このディレクトリのOMakefileをインクルード
207 my_project/OMakefile:
211 # バイトコードのコンパイラを使いたいですか?
212 # それともネイティブコードのコンパイラを使いたいですか?
214 NATIVE_ENABLED = true
220 my_project/src/OMakefile:
224 my_project/src/lib/OMakefile:
225 # ネイティブコードにおいて、積極的にインライン化を行う
226 OCAMLOPTFLAGS += -inline 10
228 # 静的ライブラリとしてライブラリをビルドします。
229 # これはUnix/OSX上ではlibbug.aとして、
230 # Win32上ではlibbug.libとしてビルドされます。
231 # 引数にはソースファイルの拡張子を入れていないことに注意してください。
232 OCamlLibrary(libbug, ouch bandaid)
234 my_project/src/main/OMakefile:
235 # いくつかのファイルは../libディレクトリ上の
237 OCAMLINCLUDES += ../lib
239 # どのライブラリに対してリンクしたいのかを指定
244 # Win32上ではhorsefly.exe、
245 # Unix上ではhorseflyとしてビルドされます。
246 # 最初の引数はアプリケーション名を指定します。
247 # 二番目の引数はソースファイルの配列を指定します(拡張子は抜いてください)。
248 # これらの配列はビルドするプログラムの一部となります。
249 OCamlProgram(horsefly, horsefly main)
251 # デフォルトでこのプログラムをビルドします
252 # (他の引数を指定しないでomakeが実行される場合です)。
253 # EXE変数はWin32上では.exeとして定義されていますが、
254 # 他のプラットフォーム上では空の変数です。
255 .DEFAULT: horsefly$(EXE)
257 この場合、ほとんどの設定は ``build/OCaml.om`` ファイルで定義されています。今回は特に、 ``my_project/src/lib`` ファイルを ``-inline 10`` オプションを用いて積極的にコンパイルするが、 ``my_project/src/lib`` は普通にコンパイルする設定となっています。
262 ------------------------
263 前回の二つのサンプルは十分簡単なように見えますが、これはOMakeの標準ライブラリ( ``build/C`` と ``/build/OCaml`` ファイル)がすべての仕事を行ってしまったためです。もし私たちがOMakeの標準ライブラリでサポートされていないような言語のビルド設定を書こうとした場合、一体どうすれば良いのでしょうか?
265 例えば、私たちはOMakeに新しい言語を適用させているものとしましょう。この言語は標準的なコンパイル/リンクモデルを用いていますが、OMakeの標準ライブラリには存在していません。今回は問題をはっきりさせるため、以下のように動作する手順について考えてみましょう。
267 * ``.cat`` 拡張子(Categorical Abstract Terminology)では複数あるファイルの中のソースファイルを定義します。
268 * ``catc`` コンパイラを用いて ``.cat`` ファイルは ``.woof`` (Wicked Object-Oriented Format)ファイルにコンパイルされます。
269 * ``.woof`` ファイルは実行可能な ``.dog`` (Digital Object Group)ファイルを生成するために、 ``-c`` オプションを用いた ``catc`` コンパイラによってリンクされます。 ``catc`` はまた ``-a`` オプションで、いくつかの ``.woof`` ファイルをライブラリに結合させることができます。
270 * 各々の ``.cat`` ファイルは他のソースファイルに関連付けることができます。もしソースファイル ``a.cat`` が行 ``open b`` を含んでいた場合、 ``a.cat`` は ``b.woof`` ファイルに依存しており、 ``a.cat`` はもし ``b.woof`` が変更された場合再コンパイルしなければなりません。 ``catc`` 関数は ``-I`` オプションで依存関係を示した行を探索することができます。
272 (訳注: これはcat, woof, dogにもじって作られた仮のソースコードであり、実際に存在しているわけではありません)
274 ビルド設定を定義するために、私たちは以下の3つの作業を行う必要があります。
276 1. 依存関係の情報をソースファイルから探索するための ``.SCANNER`` ルールを定義する。
277 2. ``.cat`` ファイルを ``.woof`` ファイルにコンパイルするための普遍的なビルドルールを定義する。
278 3. 実行可能な ``.dog`` ファイルを生成するために、 ``.woof`` ファイルをリンクするためのルールを一つの関数として定義する。
280 初めに、これらの定義はプロジェクトルートの ``OMakefile`` に置くことになります。
288 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
289 さて、パート2に移って、通常の編集ルールを定義していきましょう。今回私たちはビルドルールについて、ソースコードに直接ルールを定義することにします。まずはインクルードパスを扱うために、インクルードパスを指定した変数 ``CAT_INCLUDES`` を定義します。これはディレクトリが格納されている配列です。そしてオプションを定義するために、私たちは「遅延評価変数(lazy variable)(":ref:`label7.5`"を参照)」を使用します。この場合は他にも標準的なフラグが存在していますので、 ``CAT_FLAGS`` 変数も定義することにしましょう。 ::
291 # 私たちは今回CATC変数ををオーバーライドしたいので、catcコマンドを定義します
297 # インクルードパスの辞書(通常は空です)
300 # インクルードパスによるインクルードオプションを計算します
301 PREFIXED_INCLUDES[] = $`(mapprefix -I, $(INCLUDES))
303 # 通常の方法で.woofファイルをビルドします
305 $(CATC) $(PREFIXED_INCLUDES) $(CAT_FLAGS) -c $<
307 最後の部分では、インクルードパスと前に定義されている ``CAT_FLAGS`` 変数を含んだ、 ``catc`` コンパイラを呼び出すというビルドルールを定義しています。 ``$<`` 変数はソースファイル名に置き換わります。
314 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
315 .woofファイルをリンクするために、どのようにビルド作業を行うのかについて記述した、別のルールを記述します。ここでソースに直接ルールを定義するかわりに、リンク作業を記述した関数を定義することにします。この関数は二つの引数をとり、最初の引数は実行ファイル名(拡張子なし)で、二つ目の引数はリンクするためのファイル名(これもまた拡張子はなし)を指定します。以下はコードの断片です。 ::
320 # どのように.dogプログラムをビルドするのかを定義した関数
321 CatProgram(program, files) =
323 file_names = $(addsuffix .woof, $(files))
324 prog_name = $(addsuffix .dog, $(files))
327 $(prog_name): $(file_names)
328 $(CATC) $(PREFIXED_INCLUDES) $(CAT_FLAGS) $(CAT_LINK_FLAGS) -o $@ $+
333 ``CAT_LINK_FLAGS`` 変数はちょうど私たちがリンク作業において、追加フラグを渡したいような場合に定義される変数です。さて、新しく関数が定義されましたので、私たちがプログラムをビルドするためのルールを定義したいと思った場合はいつでも、単純にこの関数を呼ぶだけで完了します。前回のような暗黙のルールを記述する場合ですと、どのように各々のソースファイルがコンパイルされるのかについていちいち指定する必要がありましたが、 ``CatProgram`` 関数はどのように実行ファイルをビルドするのか指定するだけで完了します。 ::
335 # rover.dogプログラムをソースファイルneko.catとchat.catからビルドします。
336 # rover.dogは普通にコンパイルされます。
337 .DEFAULT: $(CatProgram rover, neko chat)
343 single: find-in-path()
348 ^^^^^^^^^^^^^^^^^^^^^^^^^
349 これでほとんどの作業が終わりましたが、まだ依存関係の解析を自動的に行わせる部分が残っています。これはOMakeの利点の一つであり、さらにロバストで書きやすいビルド設定を作る手助けとなっています。厳密に言うと、この箇所は必要というわけではありませんが、あなたは切実にこの機能を欲しがっているでしょう。
351 このメカニズムは通常のルールのように、 ``.SCANNER`` ルールを定義することで得られます。しかし ``.SCANNER`` ルールはどのように依存関係を解析するのかについて指定するのであって、ターゲット自身を指定しているわけではありません。私たちは以下のような形で ``.SCANNER`` ルールを定義したいものとします。 ::
353 .SCANNER: %.woof: %.cat
356 このルールは ``<commands>`` を実行することで ``.cat`` ファイルを収集し、その収集されたファイルを展開することができるいくつかの依存関係を新たに追加することのできる ``.woof`` ファイルを指定しています???。 ``<commands>`` の実行結果は、通常の端末で出力できる、OMake形式の依存関係の配列である必要があります。
358 心配している通り、各々の ``.cat`` ファイルは ``open`` 構文を用いて、 ``.woof`` ファイルに依存していることを指定していたとします。例えば、もし ``neko.cat`` ファイルが行 ``open chat`` コマンドを含んでいたとするならば、 ``neko.woof`` ファイルは ``chat.woof`` ファイルに依存しています。この場合、 ``<commands>`` は以下の行を出力しなければなりません。 ::
362 この類推は、 ``.o`` ファイルが ``.c`` ファイルをコンパイルすることで生成されるC言語について考えるとより明瞭になります。もしファイル ``foo.c`` が ``#include "fum.h"`` のような行を含んでいたとすると、 ``foo.c`` は ``fum.c`` が変更されたときはいつでも再コンパイルを行う必要があります。これはつまり、ファイル ``foo.o`` がファイル ``fum.h`` に依存していることを表しています。OMakeの用語では、このことを「暗黙的な依存関係(implicit dependency)」と呼んでおり、 ``.SCANNER <commands>`` は以下のような行を出力する必要があるでしょう。 ::
366 それでは動物の世界へと戻ってみましょう。 ``neko.woof`` の依存関係を解析するために、私たちは一行一行 ``neko.cat`` ファイルをスキャンして、 ``open <name>`` のような形の構文を含んだ行を探す必要があります。私たちはこのようなプログラムを書かなければなりませんが、OMakeはこのような作業を簡略化することができます。この例ですと、ソースファイルをスキャンする ``awk`` 関数がビルドインで用意されているので、これを使ってみましょう。一つ難しいことがあるとするならば、それは依存関係が ``INCLUDE`` パスに依存していることです。そのためにOMakeでは探索するための ``find-in-path`` 関数を用意しています。それでは以下のように書いてみます。 ::
368 .SCANNER: %.woof: %.cat
377 # 重複を削除し、インクルードパスのファイルを探索する
378 deps = $(find-in-path $(INCLUDES), $(set $(deps)))
381 println($"$@: $(deps)")
383 それでは上のソースコードを見てみましょう。初めに、全体の文はシェルコマンドのシーケンスとして扱われずに内部で計算されるよう、 ``section`` 文の中で定義されています。
385 今回私たちはすべての依存関係を集めるために、 ``deps`` 変数を用いました。 ``awk`` 関数はソースファイル ``($<)`` を一行一行スキャンしていきます。正規表現 ``^open`` (これはこの行が単語 ``open`` で始まることを表しています)が見つかった場合、 ``deps`` 変数に二番目の単語を追加します。具体的には、もし入力された行が ``open chat`` であった場合、 ``deps`` 配列に ``chat`` 文字列を追加することになります。ソースファイル中のその他すべての行は無視されます。
387 次に、 ``$(set $(deps))`` 文によって ``deps`` 配列の重複された文字列は削除されます(このとき、アルファベット順に配列をソートします)。 ``find-in-path`` 関数はインクルードパス中の各々のファイルの絶対パスを探索します。
389 最後に、文字列 ``$"$@: $(deps)"`` を結果として出力します。クオーテーションには ``deps`` 配列を単純な文字列に変換した状態で追加されます。
395 例がすべて終わったので、この成果を一つのプロジェクトにまとめてみましょう。前回の例は以下のような構成とします。 ::
410 この全体のプロジェクトのリストは以下のようになります。私たちはまたライブラリにいくつかの ``.woof`` ファイルをリンクさせるために、 ``CatLibrary`` 関数を定義していることに注意してください。 ::
412 my_project/OMakeroot:
416 # このディレクトリのOMakefileをインクルード
419 my_project/OMakefile:
420 ########################################################################
421 # .catファイルをコンパイルするための標準設定
424 # 私たちは今回CATC変数ををオーバーライドしたいので、catcコマンドを定義します
430 # インクルードパスの辞書(通常は空です)
433 # インクルードパスによるインクルードオプションを計算します
434 PREFIXED_INCLUDES[] = $`(mapprefix -I, $(INCLUDES))
436 # .catファイルの依存関係を解析するスキャナ
437 .SCANNER: %.woof: %.cat
446 # 重複を削除し、インクルードパスのファイルを探索する
447 deps = $(find-in-path $(INCLUDES), $(set $(deps)))
450 println($"$@: $(deps)")
452 # 通常の方法で.catファイルをコンパイルする
454 $(CATC) $(PREFIXED_INCLUDES) $(CAT_FLAGS) -c $<
459 # いくつかの.woofファイルを用いてライブラリをビルド
460 CatLibrary(lib, files) =
462 file_names = $(addsuffix .woof, $(files))
463 lib_name = $(addsuffix .woof, $(lib))
466 $(lib_name): $(file_names)
467 $(CATC) $(PREFIXED_INCLUDES) $(CAT_FLAGS) $(CAT_LINK_FLAGS) -a $@ $+
472 # どのように.dogプログラムをビルドするのかを定義した関数
473 CatProgram(program, files) =
475 file_names = $(addsuffix .woof, $(files))
476 prog_name = $(addsuffix .dog, $(program))
479 $(prog_name): $(file_names)
480 $(CATC) $(PREFIXED_INCLUDES) $(CAT_FLAGS) $(CAT_LINK_FLAGS) -o $@ $+
485 ########################################################################
492 my_project/src/OMakefile:
495 my_project/src/lib/OMakefile:
496 CatLibrary(cats, neko chat)
498 my_project/src/main/OMakefile:
499 # ../libディレクトリからのインクルードを許可
503 .DEFAULT: $(CatProgram main, main ../cats)
505 注意点としては、 ``OMakeroot`` では依存関係の解析や、ソースファイルをコンパイルするための通常のルール、ライブラリやプログラムをビルドするいくつかの関数を含んだ、標準的な設定を定義しています。
507 これらのルールや関数はサブディレクトリに継承されていますので、 ``.SCANNER`` とビルドルールは自動的に各々のサブディレクトリに使われます。よってあなたはこれらを繰り返し記述する必要はありません。
515 これで一通りの作業は終わりましたが、まだ考慮すべき点はいくつか残っています。
517 まず、 ``cat`` プログラムをビルドするためのルールはプロジェクトの ``OMakefile`` に定義しました。もしあなたがどこか別の ``cat`` プロジェクトを持っていた場合、 ``OMakeroot`` をコピーを(そしてもし必要ならば修正も)するかもしれません。その代わりに、あなたは設定ファイルを ``Cat.om`` のように名称を変更して、ライブラリの共有ディレクトリに移すべきです。これで、コードをコピーする代わりに、OMakeコマンド ``open Cat`` を用いてインクルードできるようになります。そのためには、あなたは共有ディレクトリを ``OMAKEPATH`` 環境変数に追加することで、omakeがどこを探せば良いのか分かるようにすべきです。
519 もしあなたがいい仕事をしたのなら、標準の設定となるようにあなたの設定ファイルを送ることを考えてみてください(``omake@metaprl.org`` 宛にリクエストを送ることで)。他の人の作業を省力化することになります。
526 3.5 階層構造、.SUBDIRSの内容を並列化させる
527 -------------------------------------------
528 いくつかのプロジェクトは同一の設定を有した、数多くのディレクトリで構成されているものです。例えば、あなたは現在サブディレクトリが多数あり、その各々がウェブページの画像の集合であるというプロジェクトを持っているものとしましょう。ある特定の画像を除いて、各々のファイルの設定は同一です。
530 この設定をより強固に構築するため、以下のような場合を考えます。まず、このプロジェクトは4つのサブディレクトリ ``page1, page2, page3, page4`` を含んでいるものとします。また、各々のサブディレクトリは二つのファイル ``image1.jpg, image2.jpg`` を含んでおり、それらはプログラム ``genhtml`` によって生成されるウェブページの一部であるとします。
532 各々のディレクトリ中に ``OMakefile`` を定義する代わりに、OMakeでは ``.SUBDIRS`` コマンドの内容として定義することができます。 ::
534 .SUBDIRS: page1 page2 page3 page4
535 index.html: image1.jpg image2jpg
538 ``.SUBDIRS`` の内容は、まるで ``OMakefile`` が内部にあるかのように正確にふるまい、通常の命令を任意の数だけ実行することができます。 ``.SUBDIRS`` の内容は各々のサブディレクトリの内部で評価されます。実際に何が行われているのかについては、現在のディレクトリ名を出力する命令 ``($(CWD))`` を追加することでより分かりやすくなるでしょう。 ::
540 .SUBDIRS: page1 page2 page3 page4
541 println($(absname $(CWD)))
542 index.html: image1.jpg image2jpg
555 3.5.1 バイナリデータのパターン(blob patterns)を扱う
556 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
557 もちろん、上述した指定は非常に強固なものとなっています。実際に、各々のサブディレクトリが異なった画像の集合であり、そのすべてがウェブページに含まれているような場合でも、記述方法は似ています。この問題に対するより簡単な解法の一つは、 ``glob`` や ``ls`` のようなディレクトリのリストを出力する関数を用いることです。 ``glob`` 関数はシェルのパターンを引数に持ち、現在のディレクトリ上でマッチしているファイル名の配列を返す関数です。 ::
559 .SUBDIRS: page1 page2 page3 page4
560 IMAGES = $(glob *.jpg)
561 index.html: $(IMAGES)
568 3.5.2 簡略化されたサブディレクトリの設定
569 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
570 別の方法は、各々のサブディレクトリ固有の情報を定義した設定ファイルを、それぞれのディレクトリに追加することです。例えば、私たちは現在、各々のサブディレクトリ中に、ディレクトリ内部にある画像のリストを定義した ``BuildInfo.om`` ファイルを設置しているものとします。 ``.SUBDIRS`` の行は似ていますが、 ``BuildInfo`` ファイルをインクルードしている点が異なっています。 ::
572 .SUBDIRS: page1 page2 page3 page4
573 include BuildInfo # IMAGES変数を定義
575 index.html: $(IMAGES)
578 それぞれのBuildInfo.omの内容は以下のようになっています。 ::
583 IMAGES[] = ../common/header.jpg winlogo.jpg
585 IMAGES[] = ../common/header.jpg unixlogo.jpg daemon.jpg
587 IMAGES[] = fee.jpg fi.jpg foo.jpg fum.jpg
595 3.5.3 サブディレクトリのリストを計算
596 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
597 現在、サブディレクトリのリスト ``page1, ... , page4`` は直接指定しています。他のディレクトリが追加される度に ``OMakefile`` を編集するよりも、( ``glob`` を用いて)計算させたほうがはるかに合理的です。 ::
599 .SUBDIRS: $(glob page*)
600 index.html: $(glob *.jpg)
603 ディレクトリ構造が階層的である場合を考えてみましょう。その場合 ``glob`` 関数を用いる代わりに、階層的に各々のディレクトリを返す ``subdirs`` 関数を使います。例えば、以下はOMakeプロジェクトのルート上で ``subdirs`` 関数を評価した結果です。最初の引数として渡したPオプションでは、OMakeのディレクトリ自身を含んでいない、「適切な」リストを返すことを指定しています。 ::
607 /home/jyh/.../omake/mk : Dir
608 /home/jyh/.../omake/RPM : Dir
610 /home/jyh/.../omake/osx_resources : Dir>
612 ``subdirs`` を使用することで、上の例は以下のように表現できます。 ::
614 .SUBDIRS: $(subdirs P, .)
615 index.html: $(glob *.jpg)
618 この場合ですと、プロジェクト中の *すべての* サブディレクトリが含まれることとなります。
620 もし私たちが ``BuildInfo.om`` オプションを使用する場合、すべてのサブディレクトリをインクルードする代わりに、 ``BuildInfo.om`` ファイルが含んであるディレクトリのみインクルードしたいと思うでしょう。これを実現するために、私たちはディレクトリを階層的に全走査し、特定の表現にマッチしたファイルを返す ``find`` 関数を使用します。この場合ですと、 ``BuildInfo.om`` という名前のファイルを探したいことになります。以下は ``find`` 関数を呼び出したサンプルです。 ::
622 osh> FILES = $(find . -name BuildInfo.om)
624 /home/jyh/.../omake/doc/html/BuildInfo.om : File
625 /home/jyh/.../omake/src/BuildInfo.om : File
626 /home/jyh/.../omake/tests/simple/BuildInfo.om : File>
627 osh> DIRS = $(dirof $(FILES))
629 /home/jyh/.../omake/doc/html : Dir
630 /home/jyh/.../omake/src : Dir
631 /home/jyh/.../omake/tests/simple : Dir>
633 この例では、プロジェクト中に3つの ``BuildInfo.om`` ファイルが ``doc/html, src, tests/simple`` ディレクトリに存在しています。また、 ``dirof`` 関数は各々のファイルのディレクトリを返します。
635 先の例に戻って、私たちは以下のように修正することにしました。 ::
637 .SUBDIRS: $(dirof $(find . -name BuildInfo.om))
638 include BuildInfo # Defines the IMAGES variable
640 index.html: $(IMAGES)
645 single: CREATE_SUBDIRS
649 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
650 時々、プロジェクトでは中間ファイルを置いておくための一時的なディレクトリが必要となる場合があります。これらの一時ディレクトリはプロジェクトがクリーンアップされたときはいつでも消去されます。これは特に、ディレクトリが消去されたら同ディレクトリの ``OMakefile`` も消去されるために、 ``OMakefile`` を一時的なディレクトリに置くべきではないことを意味しています。
652 もしあなたがこれらのディレクトリに関する設定を行いたいのなら、あなたは ``OMakefile`` を設置する代わりに、 ``.SUBDIRS`` の内容について記述する必要があります。 ::
655 CREATE_SUBDIRS = true
660 echo $(digest $<) > $@
663 %.comments: ../src/%.src
673 今回の例では、私たちは ``tmp`` ディレクトリが存在しない場合に新しくディレクトリを生成するため、 ``CREATE_SUBDIRS`` 変数を ``true`` に設定しました。 ``.SUBDIRS`` の内容は少々工夫してありますが、だいたいあなたが期待している通りに動作するはずです。 ``clean phony`` ターゲットでは、プロジェクトがクリーンアップされた場合は ``tmp`` ディレクトリが消去されるように指示しています。