OSDN Git Service

9章途中まで追加
[omake-japanese/omake_trans.git] / build-examples.rst
1 .. 3-build-examples
2
3 .. index::
4    single: OMakefile
5    single: OMakeroot
6    single: OSTYPE
7 .. _label3:
8
9 3. OMakeビルドサンプル
10 ==================================
11 この章ではOMakeのビルド体系をさらにもう少し解説します。この議論を決定するただ一つの結論としては、OMakeは全体のプロジェクト解析を元にしているということです。これはあなたがプロジェクト全体の設定を決めて、そしてOMakeのインスタンスを実行することを意味しています。
12
13 一つのディレクトリで構成されたプロジェクトではあまり意味がないかもしれませんが、多数のディレクトリからなるプロジェクトでは大きな意味を持ちます。 ``GNU make`` では、あなたは通常、プロジェクトの各々のディレクトリにおいて ``make`` プログラムを再帰的に呼び出すでしょう。例えば、あなたが現在サブディレクトリ ``lib`` と ``main`` が入っているソースディレクトリ ``src`` を含んでいるプロジェクトをいくつか持っているものとしましょう。具体的には、あなたのプロジェクト構成は以下のアスキーアートのようになります。 ::
14
15     my_project/
16     |--> Makefile
17     `--> src/
18          |---> Makefile
19          |---> lib/
20          |     |---> Makefile
21          |     `---> source files...
22          `---> main/
23                |---> Makefile
24                `---> source files...
25
26 一般的に ``GNU make`` では、初めに ``my_project/`` の ``make`` インスタンスを呼び出します。この ``make`` インスタンスは ``src/`` ディレクトリ内の ``make`` インスタンスを呼び出し、そして ``lib/`` と ``main/`` の新しいインスタンスを呼び出します。つまり、 ``GNU make`` では単純に、プロジェクト内の ``Makefile`` の数だけ ``make`` インスタンスが生成されることになります。
27
28 大量のプロセスを処理することは今日のコンピュータにとってさほど大きな問題ではありません(ときどき著者の意見に反対する人もいるようですが、私たちはもはや1970年代に住んでいるわけではないのです)。この構造に関する問題としては、各々の ``make`` プロセスが分けられた設定を用いており、そしてそのすべてが調和のとれたものにするには、非常に多くの負担になってしまうという点です。さらには、プログラマが ``main/`` ディレクトリで ``make`` プログラムを実行し、 ``lib/`` がもう使われていない物である場合を考えてみましょう。この場合、 ``make`` は幸せそうにあちこちに曲がりくねった挙句、恐らく ``lib/`` をリビルドしようと奮起して、恐らく諦めることになるでしょう。
29
30 OMakeではこの構造を抜本的に変更します。とは言っても実際の変更点はそれほどありません。ソース構造は非常に似通っています。私たちは単純にいくつかの"O"を以下のアスキーアートのように加えただけです。 ::
31
32     my_project/
33     |--> OMakeroot   (or Root.om)
34     |--> OMakefile
35     `--> src/
36          |---> OMakefile
37          |---> lib/
38          |     |---> OMakefile
39          |     `---> source files...
40          `---> main/
41                |---> OMakefile
42                `---> source files...
43
44 各々の ``<dir>/OMakefile`` の役割は各々の ``<dir>/Makefile`` の役割と同様で、どのように ``<dir>`` のソースファイルをビルドするのかを設定します。 ``OMakefile`` は ``Makefile`` の構造や文法を保持しておりますが、ほとんどの場合 ``make`` よりも簡単に記述することができます。
45
46 一つ小さな違いがあるとすれば、プロジェクトのルートディレクトリに ``OMakeroot`` が存在している点です。このファイルの主要な目的は、第一にプロジェクトのルートディレクトリがどこにあるかを示すことです(omakeがサブディレクトリから呼び出されたときのためです)。 ``OMakeroot`` はブートストラップとして働きます。omakeはこのファイルを最初に読み込むことで実行されます。それ以外では、 ``OMakeroot`` の文法と機能は他の ``OMakefile`` と全く同様です。
47
48 *大きな* 違いは、OMakeは *グローバルな* 解析を行うという点です。以下はどのようにomakeが動作するのかを示します。
49
50 1. omakeはOMakerootファイルがあるディレクトリに移動し、読んでいきます。
51 2. 各々の ``OMakefile`` は ``.SUBDIRS`` ターゲットを使用して ``OMakefile`` があるサブディレクトリを指し示します。例えば、 ``my_project/OMakefile`` は以下のルールを持っていたとします。 ::
52
53      .SUBDIRS: src
54
55   omakeはこれらのルールを読んで、プロジェクト内のすべてのOMakefileを評価します。読み込みや評価は高速に行われるので、このプロセスは大変ではありません。
56
57 3. 全体の設定が読まれたら、omakeはどのファイルが使われていないのかを決定し(グローバルな解析を使用します)、ビルド作業を開始します。これはどのファイルのビルドが実際に必要なのかに依存した、ビルド時間がかかります。
58
59 このモデルではいくつかの利点があります。初めに、解析をグローバルにしたことで、結局たった一つの設定ファイルを用いることとなり、ビルドに関する設定の一致が保証されるという点です。別の利点はビルドに関する設定が継承され、再利用可能となり、さらに階層構造となる点です。概して、ルートの ``OMakefile`` はいくつかの標準的な決まり文句と設定を定義しており、これはサブディレクトリによって継承され、調整したり、変更することができます(全体を書き換える必要はありません)。この方法の欠点は容量が増大することで、これは結局グローバルな解析を行っているためです。が、実際にはめったに考慮する必要があるようには見えません。OMakeは大きなプロジェクトにおいてさえ、あなたが使っているウェブブラウザよりもはるかに小さい容量しか使いません。
60
61 GNU/BSDのmakeユーザは以下の点を頭に留めておいてください。
62
63 * OMakeは ``Makefile`` と同じくらい多くのファイルを作ります。文法は似ており、makeと同様に多くのビルドイン関数が用意されています。しかしながら、この2つのビルドシステムは同じではありません。OMakeではいくつかの酷い機能(これは著者の意見です)が外されており、それに代わって新しい機能が追加されています。
64 * OMakeはWin32を含んだ、複数のプラットフォーム上で同様に動きます。あなたは複数のプラットフォーム上で動かすためにコードを変更したり、いくつかのトリッキーなテクを使ったり、 ``$(OSTYPE)`` 変数を使ってビルド設定を調節する必要はありません。
65 * OMakeの依存関係の解析はMD5によるファイルの要約を元にしています。これはつまり、依存関係の解析はファイルの「修正日時」ではなくファイルの「内容」を元にしていることを表しています。さあ、ローカル日時とファイルサーバの日時から生じるミスマッチや、間違ったタイムスタンプの変更によるビルドミスにおさらばしましょう。
66
67 .. index::
68    single: OMakeroot
69    single: OMakefile
70 .. _label3.1:
71
72 3.1  OMakeroot vs. OMakefile
73 ----------------------------------
74 さて、例を見せる前に、一つ質問をしてみましょう。それは「プロジェクトルートの ``OMakeroot`` と ``OMakefile`` の違いは何か?」というものです。その質問に関する端的な答えは「違いはないが、あなたは必ず ``OMakeroot`` ファイル(あるいは ``Root.om`` ファイル)を作らなければならない」です。
75
76 しかしながら、通常の範囲で用いるならば ``OMakeroot`` は変更する必要のない決まり文句が並んでいるファイルであり、すべてのプロジェクトの ``OMakeroot`` は多かれ少なかれ同じような内容となるでしょう。
77
78 OMakeを始めるために、あなたがこのような決まり文句を入力する必要はありません。ほとんどの場合において、あなたは以下の手順をプロジェクトのルートディレクトリで実行するだけです。
79
80 * omake --installをプロジェクトのルートで実行する。
81
82 これで最初の ``OMakeroot`` と ``OMakefile`` が生成され、編集できるようになりました。
83
84 .. index::
85    single: DefineCommandVars()
86 .. _label3.2:
87
88 3.2  Cプロジェクトのサンプル
89 ------------------------------
90 OMakeを始めるためには、まず簡単なサンプルを見せるのが手っ取り早いでしょう。いま私たちは以下のファイルを含んだディレクトリツリーを持っているものとします。 ::
91
92     my_project/
93     |--> OMakeroot
94     |--> OMakefile
95     `--> src/
96          |---> OMakefile
97          |---> lib/
98          |     |---> OMakefile
99          |     |---> ouch.c
100          |     |---> ouch.h
101          |     `---> bandaid.c
102          `---> main/
103                |---> OMakefile
104                |---> horsefly.c
105                |---> horsefly.h
106                `---> main.c
107
108 以下は ``OMakeroot`` , ``OMakefile`` リストのサンプルです。 ::
109
110     my_project/OMakeroot:
111         # Cアプリケーションの標準的な設定をインクルード
112         open build/C
113         
114         # コマンドライン上で処理
115         DefineCommandVars()
116         
117         # このディレクトリのOMakefileをインクルード
118         .SUBDIRS: .
119
120     my_project/OMakefile:
121         # 標準的なコンパイルオプションを設定
122         CFLAGS += -g
123
124         # srcディレクトリをインクルード
125         .SUBDIRS: src
126
127     my_project/src/OMakefile:
128         # あなたの好きなようにオプションを付け加えます
129         CFLAGS += -O2
130
131         # サブディレクトリをインクルード
132         .SUBDIRS: lib main
133
134     my_project/src/lib/OMakefile:
135         # 静的ライブラリとしてライブラリをビルドします。
136         # これはUnix/OSX上ではlibbug.aとして、
137         # Win32上ではlibbug.libとしてビルドされます。
138         # 引数にはソースファイルの拡張子を入れていないことに注意してください。
139         StaticCLibrary(libbug, ouch bandaid)
140
141     my_project/src/main/OMakefile:
142         # いくつかのファイルは../libディレクトリ上の
143         # .hファイルをインクルードしています。
144         INCLUDES += ../lib
145
146         # どのライブラリに対してリンクしたいのかを指定
147         LIBS[] +=
148             ../lib/libbug
149
150         # プログラムをビルドします。
151         # Win32上ではhorsefly.exe、
152         # Unix上ではhorseflyとしてビルドされます。
153         # 最初の引数はアプリケーション名を指定します。
154         # 二番目の引数はソースファイルの配列を指定します(拡張子は抜いてください)。
155         # これらの配列はビルドするプログラムの一部となります。
156         CProgram(horsefly, horsefly main)
157
158         # デフォルトでこのプログラムをビルドします
159         # (他の引数を指定しないでomakeが実行される場合です)。
160         # EXE変数はWin32上では.exeとして定義されていますが、
161         # 他のプラットフォーム上では空の変数です。
162         .DEFAULT: horsefly$(EXE)
163
164 ほとんどの設定は ``build/C.om`` (これはOMakeの機能の一部です)ファイルに定義されています。このファイルはほとんどの作業の面倒を見てくれます。具体的には、
165
166 * Cライブラリやプログラムを正当な方法でビルドするための、 ``StaticCLibrary`` と ``CProgram`` 関数を定義しています。
167 * 依存関係を特定するために各々のソースコードを調べていくメカニズムを定義しています。これはつまり、Cソースファイルのための ``.SCANNER`` ルールを定義していることを意味しています。変数はサブディレクトリにも継承されていき、例えば、 ``src/main/OMakefile`` の ``CFLAGS`` 変数の値は ``"-g -O2"`` となります。
168
169 .. index::
170    single: OCaml
171    single: OCamlLibrary()
172    single: OCamlProgram()
173 .. _label3.3:
174
175 3.3  OCamlプロジェクトのサンプル
176 ------------------------------------
177 前回のCの代わりにOCamlを使った状態で、簡単なサンプルを作ってみましょう。今回では、ディレクトリツリーは以下のようになります。 ::
178
179     my_project/
180     |--> OMakeroot
181     |--> OMakefile
182     `--> src/
183          |---> OMakefile
184          |---> lib/
185          |     |---> OMakefile
186          |     |---> ouch.ml
187          |     |---> ouch.mli
188          |     `---> bandaid.ml
189          `---> main/
190                |---> OMakefile
191                |---> horsefly.ml
192                |---> horsefly.mli
193                `---> main.ml
194
195 ``OMakeroot`` , ``OMakefile`` のリストは前回と少し違います。 ::
196
197     my_project/OMakeroot:
198         # OCamlアプリケーションの標準的な設定をインクルード
199         open build/OCaml
200         
201         # コマンドライン上で処理
202         DefineCommandVars()
203         
204         # このディレクトリのOMakefileをインクルード
205         .SUBDIRS: .
206
207     my_project/OMakefile:
208         # 標準的なコンパイルオプションを設定
209         OCAMLFLAGS += -Wa
210
211         # バイトコードのコンパイラを使いたいですか?
212         # それともネイティブコードのコンパイラを使いたいですか?
213         # 今回は両方とも使ってみましょう。
214         NATIVE_ENABLED = true
215         BYTE_ENABLED = true
216
217         # srcディレクトリをインクルード
218         .SUBDIRS: src
219
220     my_project/src/OMakefile:
221         # サブディレクトリをインクルード
222         .SUBDIRS: lib main
223
224     my_project/src/lib/OMakefile:
225         # ネイティブコードにおいて、積極的にインライン化を行う
226         OCAMLOPTFLAGS += -inline 10
227
228         # 静的ライブラリとしてライブラリをビルドします。
229         # これはUnix/OSX上ではlibbug.aとして、
230         # Win32上ではlibbug.libとしてビルドされます。
231         # 引数にはソースファイルの拡張子を入れていないことに注意してください。
232         OCamlLibrary(libbug, ouch bandaid)
233
234     my_project/src/main/OMakefile:
235         # いくつかのファイルは../libディレクトリ上の
236         # インターフェースに依存しています。
237         OCAMLINCLUDES += ../lib
238
239         # どのライブラリに対してリンクしたいのかを指定
240         OCAML_LIBS[] +=
241             ../lib/libbug
242
243         # プログラムをビルドします。
244         # Win32上ではhorsefly.exe、
245         # Unix上ではhorseflyとしてビルドされます。
246         # 最初の引数はアプリケーション名を指定します。
247         # 二番目の引数はソースファイルの配列を指定します(拡張子は抜いてください)。
248         # これらの配列はビルドするプログラムの一部となります。
249         OCamlProgram(horsefly, horsefly main)
250
251         # デフォルトでこのプログラムをビルドします
252         # (他の引数を指定しないでomakeが実行される場合です)。
253         # EXE変数はWin32上では.exeとして定義されていますが、
254         # 他のプラットフォーム上では空の変数です。
255         .DEFAULT: horsefly$(EXE)
256
257 この場合、ほとんどの設定は ``build/OCaml.om`` ファイルで定義されています。今回は特に、 ``my_project/src/lib`` ファイルを ``-inline 10`` オプションを用いて積極的にコンパイルするが、 ``my_project/src/lib`` は普通にコンパイルする設定となっています。
258
259 .. _label3.4:
260
261 3.4  新しい言語を扱う
262 ------------------------
263 前回の二つのサンプルは十分簡単なように見えますが、これはOMakeの標準ライブラリ( ``build/C`` と ``/build/OCaml`` ファイル)がすべての仕事を行ってしまったためです。もし私たちがOMakeの標準ライブラリでサポートされていないような言語のビルド設定を書こうとした場合、一体どうすれば良いのでしょうか?
264
265 例えば、私たちはOMakeに新しい言語を適用させているものとしましょう。この言語は標準的なコンパイル/リンクモデルを用いていますが、OMakeの標準ライブラリには存在していません。今回は問題をはっきりさせるため、以下のように動作する手順について考えてみましょう。
266
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`` オプションで依存関係を示した行を探索することができます。
271
272 (訳注: これはcat, woof, dogにもじって作られた仮のソースコードであり、実際に存在しているわけではありません)
273
274 ビルド設定を定義するために、私たちは以下の3つの作業を行う必要があります。
275
276 1. 依存関係の情報をソースファイルから探索するための ``.SCANNER`` ルールを定義する。
277 2. ``.cat`` ファイルを ``.woof`` ファイルにコンパイルするための普遍的なビルドルールを定義する。
278 3. 実行可能な ``.dog`` ファイルを生成するために、 ``.woof`` ファイルをリンクするためのルールを一つの関数として定義する。
279
280 初めに、これらの定義はプロジェクトルートの ``OMakefile`` に置くことになります。
281
282 .. index::
283    single: 遅延評価変数
284    single: mapprefix()
285 .. _label3.4.1:
286
287 3.4.1  通常の編集ルールの定義
288 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
289 さて、パート2に移って、通常の編集ルールを定義していきましょう。今回私たちはビルドルールについて、ソースコードに直接ルールを定義することにします。まずはインクルードパスを扱うために、インクルードパスを指定した変数 ``CAT_INCLUDES`` を定義します。これはディレクトリが格納されている配列です。そしてオプションを定義するために、私たちは「遅延評価変数(lazy variable)(":ref:`label7.5`"を参照)」を使用します。この場合は他にも標準的なフラグが存在していますので、 ``CAT_FLAGS`` 変数も定義することにしましょう。 ::
290
291    # 私たちは今回CATC変数ををオーバーライドしたいので、catcコマンドを定義します
292    CATC = catc
293
294    # 通常のフラグは空にします
295    CAT_FLAGS =
296    
297    # インクルードパスの辞書(通常は空です)
298    INCLUDES[] =
299
300    # インクルードパスによるインクルードオプションを計算します
301    PREFIXED_INCLUDES[] = $`(mapprefix -I, $(INCLUDES))
302
303    # 通常の方法で.woofファイルをビルドします
304    %.woof: %.cat
305        $(CATC) $(PREFIXED_INCLUDES) $(CAT_FLAGS) -c $<
306
307 最後の部分では、インクルードパスと前に定義されている ``CAT_FLAGS`` 変数を含んだ、 ``catc`` コンパイラを呼び出すというビルドルールを定義しています。 ``$<`` 変数はソースファイル名に置き換わります。
308
309 .. index::
310    single: addsuffix()
311 .. _label3.4.2:
312
313 3.4.2  リンクするためのルールを定義
314 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
315 .woofファイルをリンクするために、どのようにビルド作業を行うのかについて記述した、別のルールを記述します。ここでソースに直接ルールを定義するかわりに、リンク作業を記述した関数を定義することにします。この関数は二つの引数をとり、最初の引数は実行ファイル名(拡張子なし)で、二つ目の引数はリンクするためのファイル名(これもまた拡張子はなし)を指定します。以下はコードの断片です。 ::
316
317     # 副次的なリンクオプション
318     CAT_LINK_FLAGS =
319
320     # どのように.dogプログラムをビルドするのかを定義した関数
321     CatProgram(program, files) =
322         # 拡張子を追加
323         file_names = $(addsuffix .woof, $(files))
324         prog_name = $(addsuffix .dog, $(files))
325
326         # ビルドルール
327         $(prog_name): $(file_names)
328             $(CATC) $(PREFIXED_INCLUDES) $(CAT_FLAGS) $(CAT_LINK_FLAGS) -o $@ $+
329
330         # プログラム名を返す
331         value $(prog_name)
332
333 ``CAT_LINK_FLAGS`` 変数はちょうど私たちがリンク作業において、追加フラグを渡したいような場合に定義される変数です。さて、新しく関数が定義されましたので、私たちがプログラムをビルドするためのルールを定義したいと思った場合はいつでも、単純にこの関数を呼ぶだけで完了します。前回のような暗黙のルールを記述する場合ですと、どのように各々のソースファイルがコンパイルされるのかについていちいち指定する必要がありましたが、 ``CatProgram`` 関数はどのように実行ファイルをビルドするのか指定するだけで完了します。 ::
334
335     # rover.dogプログラムをソースファイルneko.catとchat.catからビルドします。
336     # rover.dogは普通にコンパイルされます。
337     .DEFAULT: $(CatProgram rover, neko chat)
338
339 .. index::
340    single: .SCANNER
341    single: 暗黙的な依存関係
342    single: 正規表現
343    single: find-in-path()
344    single: awk()
345 .. _label3.4.3:
346
347 3.4.3  依存関係の解析
348 ^^^^^^^^^^^^^^^^^^^^^^^^^
349 これでほとんどの作業が終わりましたが、まだ依存関係の解析を自動的に行わせる部分が残っています。これはOMakeの利点の一つであり、さらにロバストで書きやすいビルド設定を作る手助けとなっています。厳密に言うと、この箇所は必要というわけではありませんが、あなたは切実にこの機能を欲しがっているでしょう。
350
351 このメカニズムは通常のルールのように、 ``.SCANNER`` ルールを定義することで得られます。しかし ``.SCANNER`` ルールはどのように依存関係を解析するのかについて指定するのであって、ターゲット自身を指定しているわけではありません。私たちは以下のような形で ``.SCANNER`` ルールを定義したいものとします。 ::
352
353     .SCANNER: %.woof: %.cat
354         <commands>
355
356 このルールは ``<commands>`` を実行することで ``.cat`` ファイルを収集し、その収集されたファイルを展開することができるいくつかの依存関係を新たに追加することのできる ``.woof`` ファイルを指定しています???。 ``<commands>`` の実行結果は、通常の端末で出力できる、OMake形式の依存関係の配列である必要があります。
357
358 心配している通り、各々の ``.cat`` ファイルは ``open`` 構文を用いて、 ``.woof`` ファイルに依存していることを指定していたとします。例えば、もし ``neko.cat`` ファイルが行 ``open chat`` コマンドを含んでいたとするならば、 ``neko.woof`` ファイルは ``chat.woof`` ファイルに依存しています。この場合、 ``<commands>`` は以下の行を出力しなければなりません。 ::
359
360     neko.woof: chat.woof
361
362 この類推は、 ``.o`` ファイルが ``.c`` ファイルをコンパイルすることで生成されるC言語について考えるとより明瞭になります。もしファイル ``foo.c`` が ``#include "fum.h"`` のような行を含んでいたとすると、 ``foo.c`` は ``fum.c`` が変更されたときはいつでも再コンパイルを行う必要があります。これはつまり、ファイル ``foo.o`` がファイル ``fum.h`` に依存していることを表しています。OMakeの用語では、このことを「暗黙的な依存関係(implicit dependency)」と呼んでおり、 ``.SCANNER <commands>`` は以下のような行を出力する必要があるでしょう。 ::
363
364     foo.o: fum.h
365
366 それでは動物の世界へと戻ってみましょう。 ``neko.woof`` の依存関係を解析するために、私たちは一行一行 ``neko.cat`` ファイルをスキャンして、 ``open <name>`` のような形の構文を含んだ行を探す必要があります。私たちはこのようなプログラムを書かなければなりませんが、OMakeはこのような作業を簡略化することができます。この例ですと、ソースファイルをスキャンする ``awk`` 関数がビルドインで用意されているので、これを使ってみましょう。一つ難しいことがあるとするならば、それは依存関係が ``INCLUDE`` パスに依存していることです。そのためにOMakeでは探索するための ``find-in-path`` 関数を用意しています。それでは以下のように書いてみます。 ::
367
368     .SCANNER: %.woof: %.cat
369         section
370             # ファイルをスキャン
371             deps[] =
372             awk($<)
373             case $'^open'
374                 deps[] += $2
375                 export
376
377             # 重複を削除し、インクルードパスのファイルを探索する
378             deps = $(find-in-path $(INCLUDES), $(set $(deps)))
379
380             # 依存関係を出力
381             println($"$@: $(deps)")
382
383 それでは上のソースコードを見てみましょう。初めに、全体の文はシェルコマンドのシーケンスとして扱われずに内部で計算されるよう、 ``section`` 文の中で定義されています。
384
385 今回私たちはすべての依存関係を集めるために、 ``deps`` 変数を用いました。 ``awk`` 関数はソースファイル ``($<)`` を一行一行スキャンしていきます。正規表現 ``^open`` (これはこの行が単語 ``open`` で始まることを表しています)が見つかった場合、 ``deps`` 変数に二番目の単語を追加します。具体的には、もし入力された行が ``open chat`` であった場合、 ``deps`` 配列に ``chat`` 文字列を追加することになります。ソースファイル中のその他すべての行は無視されます。
386
387 次に、 ``$(set $(deps))`` 文によって ``deps`` 配列の重複された文字列は削除されます(このとき、アルファベット順に配列をソートします)。 ``find-in-path`` 関数はインクルードパス中の各々のファイルの絶対パスを探索します。
388
389 最後に、文字列 ``$"$@: $(deps)"`` を結果として出力します。クオーテーションには ``deps`` 配列を単純な文字列に変換した状態で追加されます。
390
391 .. _label3.4.4:
392
393 3.4.4  まとめ
394 ^^^^^^^^^^^^^^^^^^^
395 例がすべて終わったので、この成果を一つのプロジェクトにまとめてみましょう。前回の例は以下のような構成とします。 ::
396
397     my_project/
398     |--> OMakeroot
399     |--> OMakefile
400     `--> src/
401          |---> OMakefile
402          |---> lib/
403          |     |---> OMakefile
404          |     |---> neko.cat
405          |     `---> chat.cat
406          `---> main/
407                |---> OMakefile
408                `---> main.cat
409
410 この全体のプロジェクトのリストは以下のようになります。私たちはまたライブラリにいくつかの ``.woof`` ファイルをリンクさせるために、 ``CatLibrary`` 関数を定義していることに注意してください。 ::
411
412     my_project/OMakeroot:
413         # コマンドライン上で処理
414         DefineCommandVars()
415         
416         # このディレクトリのOMakefileをインクルード
417         .SUBDIRS: .
418
419     my_project/OMakefile:
420        ########################################################################
421        # .catファイルをコンパイルするための標準設定
422        #
423
424        # 私たちは今回CATC変数ををオーバーライドしたいので、catcコマンドを定義します
425        CATC = catc
426
427        # 通常のフラグは空にします
428        CAT_FLAGS =
429        
430        # インクルードパスの辞書(通常は空です)
431        INCLUDES[] =
432
433        #  インクルードパスによるインクルードオプションを計算します
434        PREFIXED_INCLUDES[] = $`(mapprefix -I, $(INCLUDES))
435
436        # .catファイルの依存関係を解析するスキャナ
437        .SCANNER: %.woof: %.cat
438             section
439                 # ファイルをスキャン
440                 deps[] =
441                 awk($<)
442                 case $'^open'
443                     deps[] += $2
444                     export
445
446                 # 重複を削除し、インクルードパスのファイルを探索する
447                 deps = $(find-in-path $(INCLUDES), $(set $(deps)))
448
449                 # 依存関係を出力
450                 println($"$@: $(deps)")
451
452        # 通常の方法で.catファイルをコンパイルする
453        %.woof: %.cat
454            $(CATC) $(PREFIXED_INCLUDES) $(CAT_FLAGS) -c $<
455
456        # 副次的なリンクオプション
457        CAT_LINK_FLAGS =
458
459        # いくつかの.woofファイルを用いてライブラリをビルド
460        CatLibrary(lib, files) =
461            # 拡張子を追加
462            file_names = $(addsuffix .woof, $(files))
463            lib_name = $(addsuffix .woof, $(lib))
464
465            # ビルドルール
466            $(lib_name): $(file_names)
467                $(CATC) $(PREFIXED_INCLUDES) $(CAT_FLAGS) $(CAT_LINK_FLAGS) -a $@ $+
468
469            # プログラム名を返す
470            value $(lib_name)
471
472        # どのように.dogプログラムをビルドするのかを定義した関数
473        CatProgram(program, files) =
474            # 拡張子を追加
475            file_names = $(addsuffix .woof, $(files))
476            prog_name = $(addsuffix .dog, $(program))
477
478            # ビルドルール
479            $(prog_name): $(file_names)
480                $(CATC) $(PREFIXED_INCLUDES) $(CAT_FLAGS) $(CAT_LINK_FLAGS) -o $@ $+
481
482            # プログラム名を返す
483            value $(prog_name)
484
485        ########################################################################
486        # これで正しくプログラムが動きます
487        #
488
489        # srcサブディレクトリをインクルード
490        .SUBDIRS: src
491
492     my_project/src/OMakefile:
493        .SUBDIRS: lib main
494
495     my_project/src/lib/OMakefile:
496        CatLibrary(cats, neko chat)
497
498     my_project/src/main/OMakefile:
499        # ../libディレクトリからのインクルードを許可
500        INCLUDES[] += ../lib
501
502        # プログラムをビルド
503        .DEFAULT: $(CatProgram main, main ../cats)
504
505 注意点としては、 ``OMakeroot`` では依存関係の解析や、ソースファイルをコンパイルするための通常のルール、ライブラリやプログラムをビルドするいくつかの関数を含んだ、標準的な設定を定義しています。
506
507 これらのルールや関数はサブディレクトリに継承されていますので、 ``.SCANNER`` とビルドルールは自動的に各々のサブディレクトリに使われます。よってあなたはこれらを繰り返し記述する必要はありません。
508
509 .. index::
510    single: OMAKEPATH
511 .. _label3.4.5:
512
513 3.4.5  終わりに
514 ^^^^^^^^^^^^^^^^^^
515 これで一通りの作業は終わりましたが、まだ考慮すべき点はいくつか残っています。
516
517 まず、 ``cat`` プログラムをビルドするためのルールはプロジェクトの ``OMakefile`` に定義しました。もしあなたがどこか別の ``cat`` プロジェクトを持っていた場合、 ``OMakeroot`` をコピーを(そしてもし必要ならば修正も)するかもしれません。その代わりに、あなたは設定ファイルを ``Cat.om`` のように名称を変更して、ライブラリの共有ディレクトリに移すべきです。これで、コードをコピーする代わりに、OMakeコマンド ``open Cat`` を用いてインクルードできるようになります。そのためには、あなたは共有ディレクトリを ``OMAKEPATH`` 環境変数に追加することで、omakeがどこを探せば良いのか分かるようにすべきです。
518
519 もしあなたがいい仕事をしたのなら、標準の設定となるようにあなたの設定ファイルを送ることを考えてみてください(``omake@metaprl.org`` 宛にリクエストを送ることで)。他の人の作業を省力化することになります。
520
521 .. index::
522    single: .SUBDIRS
523    single: absname()
524 .. _label3.5:
525
526 3.5  階層構造、.SUBDIRSの内容を並列化させる
527 -------------------------------------------
528 いくつかのプロジェクトは同一の設定を有した、数多くのディレクトリで構成されているものです。例えば、あなたは現在サブディレクトリが多数あり、その各々がウェブページの画像の集合であるというプロジェクトを持っているものとしましょう。ある特定の画像を除いて、各々のファイルの設定は同一です。
529
530 この設定をより強固に構築するため、以下のような場合を考えます。まず、このプロジェクトは4つのサブディレクトリ ``page1, page2, page3, page4`` を含んでいるものとします。また、各々のサブディレクトリは二つのファイル ``image1.jpg, image2.jpg`` を含んでおり、それらはプログラム ``genhtml`` によって生成されるウェブページの一部であるとします。
531
532 各々のディレクトリ中に ``OMakefile`` を定義する代わりに、OMakeでは ``.SUBDIRS`` コマンドの内容として定義することができます。 ::
533
534     .SUBDIRS: page1 page2 page3 page4
535         index.html: image1.jpg image2jpg
536             genhtml $+ > $@
537
538 ``.SUBDIRS`` の内容は、まるで ``OMakefile`` が内部にあるかのように正確にふるまい、通常の命令を任意の数だけ実行することができます。 ``.SUBDIRS`` の内容は各々のサブディレクトリの内部で評価されます。実際に何が行われているのかについては、現在のディレクトリ名を出力する命令 ``($(CWD))`` を追加することでより分かりやすくなるでしょう。 ::
539
540     .SUBDIRS: page1 page2 page3 page4
541         println($(absname $(CWD)))
542         index.html: image1.jpg image2jpg
543             genhtml $+ > $@
544   # 出力
545     /home/jyh/.../page1
546     /home/jyh/.../page2
547     /home/jyh/.../page3
548     /home/jyh/.../page4
549
550 .. index::
551    single: glob()
552    single: ls()
553 .. _label3.5.1:
554
555 3.5.1  バイナリデータのパターン(blob patterns)を扱う
556 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
557 もちろん、上述した指定は非常に強固なものとなっています。実際に、各々のサブディレクトリが異なった画像の集合であり、そのすべてがウェブページに含まれているような場合でも、記述方法は似ています。この問題に対するより簡単な解法の一つは、 ``glob`` や ``ls`` のようなディレクトリのリストを出力する関数を用いることです。 ``glob`` 関数はシェルのパターンを引数に持ち、現在のディレクトリ上でマッチしているファイル名の配列を返す関数です。 ::
558
559     .SUBDIRS: page1 page2 page3 page4
560         IMAGES = $(glob *.jpg)
561         index.html: $(IMAGES)
562             genhtml $+ > $@
563
564 .. index::
565    single: include
566 .. _label3.5.2:
567
568 3.5.2  簡略化されたサブディレクトリの設定
569 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
570 別の方法は、各々のサブディレクトリ固有の情報を定義した設定ファイルを、それぞれのディレクトリに追加することです。例えば、私たちは現在、各々のサブディレクトリ中に、ディレクトリ内部にある画像のリストを定義した ``BuildInfo.om`` ファイルを設置しているものとします。 ``.SUBDIRS`` の行は似ていますが、 ``BuildInfo`` ファイルをインクルードしている点が異なっています。 ::
571
572     .SUBDIRS: page1 page2 page3 page4
573         include BuildInfo   # IMAGES変数を定義
574
575         index.html: $(IMAGES)
576             genhtml $+ > $@
577
578 それぞれのBuildInfo.omの内容は以下のようになっています。 ::
579
580    page1/BuildInfo.om:
581        IMAGES[] = image.jpg
582    page2/BuildInfo.om:
583        IMAGES[] = ../common/header.jpg winlogo.jpg
584    page3/BuildInfo.om:
585        IMAGES[] = ../common/header.jpg unixlogo.jpg daemon.jpg
586    page4/BuildInfo.om:
587        IMAGES[] = fee.jpg fi.jpg foo.jpg fum.jpg
588
589 .. index::
590    single: subdirs()
591    single: find()
592    single: dirof()
593 .. _label3.5.3:
594
595 3.5.3  サブディレクトリのリストを計算
596 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
597 現在、サブディレクトリのリスト ``page1, ... , page4`` は直接指定しています。他のディレクトリが追加される度に ``OMakefile`` を編集するよりも、( ``glob`` を用いて)計算させたほうがはるかに合理的です。 ::
598
599     .SUBDIRS: $(glob page*)
600         index.html: $(glob *.jpg)
601             genhtml $+ > $@
602
603 ディレクトリ構造が階層的である場合を考えてみましょう。その場合 ``glob`` 関数を用いる代わりに、階層的に各々のディレクトリを返す ``subdirs`` 関数を使います。例えば、以下はOMakeプロジェクトのルート上で ``subdirs`` 関数を評価した結果です。最初の引数として渡したPオプションでは、OMakeのディレクトリ自身を含んでいない、「適切な」リストを返すことを指定しています。 ::
604
605     osh> subdirs(P, .)
606     - : <array
607             /home/jyh/.../omake/mk : Dir
608             /home/jyh/.../omake/RPM : Dir
609             ...
610             /home/jyh/.../omake/osx_resources : Dir>
611
612 ``subdirs`` を使用することで、上の例は以下のように表現できます。 ::
613
614     .SUBDIRS: $(subdirs P, .)
615         index.html: $(glob *.jpg)
616             genhtml $+ > $@
617
618 この場合ですと、プロジェクト中の *すべての* サブディレクトリが含まれることとなります。
619
620 もし私たちが ``BuildInfo.om`` オプションを使用する場合、すべてのサブディレクトリをインクルードする代わりに、 ``BuildInfo.om`` ファイルが含んであるディレクトリのみインクルードしたいと思うでしょう。これを実現するために、私たちはディレクトリを階層的に全走査し、特定の表現にマッチしたファイルを返す ``find`` 関数を使用します。この場合ですと、 ``BuildInfo.om`` という名前のファイルを探したいことになります。以下は ``find`` 関数を呼び出したサンプルです。 ::
621
622     osh> FILES = $(find . -name BuildInfo.om)
623     - : <array
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))
628     - : <array
629             /home/jyh/.../omake/doc/html : Dir
630             /home/jyh/.../omake/src : Dir
631             /home/jyh/.../omake/tests/simple : Dir>
632
633 この例では、プロジェクト中に3つの ``BuildInfo.om`` ファイルが ``doc/html, src, tests/simple`` ディレクトリに存在しています。また、 ``dirof`` 関数は各々のファイルのディレクトリを返します。
634
635 先の例に戻って、私たちは以下のように修正することにしました。 ::
636
637     .SUBDIRS: $(dirof $(find . -name BuildInfo.om))
638         include BuildInfo   # Defines the IMAGES variable
639
640         index.html: $(IMAGES)
641             genhtml $+ > $@
642
643 .. index::
644    single: 一時的なディレクトリ
645    single: CREATE_SUBDIRS
646 .. _label3.5.4:
647
648 3.5.4  一時的なディレクトリ
649 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
650 時々、プロジェクトでは中間ファイルを置いておくための一時的なディレクトリが必要となる場合があります。これらの一時ディレクトリはプロジェクトがクリーンアップされたときはいつでも消去されます。これは特に、ディレクトリが消去されたら同ディレクトリの ``OMakefile`` も消去されるために、 ``OMakefile`` を一時的なディレクトリに置くべきではないことを意味しています。
651
652 もしあなたがこれらのディレクトリに関する設定を行いたいのなら、あなたは ``OMakefile`` を設置する代わりに、 ``.SUBDIRS`` の内容について記述する必要があります。 ::
653
654     section
655         CREATE_SUBDIRS = true
656
657         .SUBDIRS: tmp
658             # MD5ハッシュを計算
659             %.digest: %.comments
660                echo $(digest $<) > $@
661
662             # ソースファイルからコメントを展開
663             %.comments: ../src/%.src
664                grep '^#' $< > $@
665
666             .DEFAULT: foo.digest
667
668     .PHONY: clean
669
670     clean:
671         rm -rf tmp
672
673 今回の例では、私たちは ``tmp`` ディレクトリが存在しない場合に新しくディレクトリを生成するため、 ``CREATE_SUBDIRS`` 変数を ``true`` に設定しました。 ``.SUBDIRS`` の内容は少々工夫してありますが、だいたいあなたが期待している通りに動作するはずです。 ``clean phony`` ターゲットでは、プロジェクトがクリーンアップされた場合は ``tmp`` ディレクトリが消去されるように指示しています。