8. ビルドルール

OMakeで使われているルールは、どのようにしてファイルをビルドするのかについて指定しています。最も簡単な例は以下のような形です。

<target>: <dependencies>
    <commands>

<target> ではビルドするファイル名を記述します。 <dependencies> では <target> をビルドする前に必要なファイルのリストを記述します。 <commands> はターゲットをビルドするためのコマンドを、インデントした状態で記述します。例えば、以下のルールではどのようにファイル hello.c をコンパイルするのかについて指定しています。

hello.o: hello.c
    $(CC) $(CFLAGS) -c -o hello.o hello.c

このルールでは、 hello.o ファイルは hello.c ファイルに依存しています。 hello.c ファイルが変更された場合、コマンド $(CC) $(CFLAGS) -c -o hello.o hello.c が実行されて、ターゲットファイル hello.o は更新されます。

ルールの中には任意の数のコマンドを含めることができます。各々の独立したコマンドラインはコマンドシェルによって、独立した状態で実行されます。コマンドの最初にタブを含めることはできません。しかし、依存関係を指定している行からはインデントされなければなりません。

通常の変数に加えて、以下の特殊変数をルールの内容で使うことができます。

  • $* : 拡張子を除いたターゲットの名前
  • $@ : ターゲットの名前
  • $^ : 依存ファイルのリストをアルファベット順に並べ、かつ重複した内容を削除したもの
  • $+ : 元の順番で並んでいる依存ファイルのリスト
  • $< : 最初の依存ファイル

例えば、上の hello.c ルールは以下のように簡略化されます。

hello.o: hello.c
    $(CC) $(CFLAGS) -c -o $@ $<

通常の値とは異なり、ルール中の変数は遅延評価されて、かつ動的なスコーピングが行われます。以下の関数定義はこの性質のいくつかを端的に表しています。

CLibrary(name, files) =
    OFILES = $(addsuffix .o, $(files))

    $(name).a: $(OFILES)
        $(AR) cq $@ $(OFILES)

この関数は .o ファイルのリストから $(name) という名前のプログラムをビルドしています。引数のファイルは拡張子なしで指定されているので、まず最初の定義では各々のファイル名に拡張子 .o を加えた配列を、変数 OFILES に束縛しています。次のステップでは $(OFILES) からターゲットライブラリ $(name).a をビルドするためのルールを定義しています。 $(AR) は関数が呼び出されて、呼び出してるスコープから変数 AR が与えられた時点で評価されます(詳細はスコーピングのセクションを参照してください)。

8.1 暗黙のルール

ルールはまた暗黙的にすることもできます。これは、ファイルがワイルドカードパターンによって指定されることを示しています。ワイルドカードとしてOMakeでは % を使用します。例えば、以下のルールでは .o ファイルをビルドするための通常のルールを指定しています。

%.o: %.c
    $(CC) $(CFLAGS) -c -o $@ $*.c

このルールは任意の .c ファイルから .o ファイルをビルドするためのテンプレートとなっています。

通常、暗黙のルールはカレントディレクトリ中のターゲットのみに用いられます。しかしながら、 .SUBDIRS ルールを経由したものも含むサブディレクトリには、スコープ中にある暗黙のルールがすべて継承されます(詳細はスコーピングのセクションを参照してください)。

8.2 束縛された暗黙のルール

暗黙のルールのターゲットにはいくつかのファイルを指定することもできます。そのためには以下のような構文を用います。

<targets>: <pattern>: <dependencies>
    <commands>

例えば、以下のルールではファイル a.ob.o のみにルールを適用しています。

a.o b.o: %.o: %.c
     $(CC) $(CFLAGS) -DSPECIAL -c $*.c

8.3 section

ルールの内容に書かれているコマンドはシェルによって頻繁に評価されます。omakeはまた、omake自身も評価の対象に加えることもできます。

これらの『ファイルを作るためのルール』を表現するためには section 表現を使います。以下のルールではターゲットの hello.c を生成するためにomakeのIO関数を用いています。

hello.c:
    section
        FP = fopen(hello.c, w)
        fprintln($(FP), $""#include <stdio.h> int main() { printf("Hello world\n"); }"")
        close($(FP))

この例では出力するテキストをクオートするために、クオーテーション $""..."" を用いています(詳細はB.1.6のセクションを参照してください)。これらのクオートは出力されたファイルには含まれていません。 fopen , fprintln , close 関数はIOセクションで評価するようにファイルのIOを実行します。

加えて、関数の呼び出しや他の特殊なコマンドはその場で正しく評価されます。また、 fprintln 関数はファイルを直接出力することもできるので、上のルールは下のような形に省略できます。

hello.c:
   fprintln($@, $""#include <stdio.h> int main() { printf("Hello world\n"); }"")

8.4 section rule

また、ターゲットの依存関係がルールの内容で記述されるような場合、 section rule を使うことで計算することができます。以下のルールでは、ファイル a.c が存在していた場合は内容を hello.c にコピーし、そうでなければ hello.cdefault.c から生成されます。

hello.c:
    section rule
       if $(target-exists a.c)
          hello.c: a.c
             cat a.c > hello.c
       else
          hello.c: default.c
             cp default.c hello.c

8.5 特別な依存関係

8.5.1 :exists:

時々、依存しているファイルの内容ではなく、ファイルが存在しているのかどうかが重要となる場合もあるでしょう。 :exists: 修飾子はこのような依存関係を指定したい場合に用いられます。

foo.c: a.c :exists: .flag
   if $(test -e .flag)
       $(CP) a.c $@

8.5.2 :effects:

コマンドの中には副産物として別のファイルを生み出すものもあるでしょう。例えば、 latex(1) コマンドは .dvi ファイルを生成する際、副産物として .aux ファイルを生成します。このような場合、 :effects: 修飾子はこのような副産物のファイルを明示的に指定したい場合に用いられます。omakeはこのファイルを上書きしないように、平行してプログラムを実行させないようにします。

paper.dvi: paper.tex :effects: paper.aux
    latex paper

8.5.3 :value:

:value: 依存関係は、ルールの実行がある式の値に依存していることを指定するために用いられます。例えば、以下のルールを見てください。

a: b c :value: $(X)
    ...

上のルールでは、”a”が $(X) の値が変わった場合に再コンパイルされる必要があることを指定しています(Xがファイル名である必要はありません)。これはファイルや変数の依存関係をより精密にコントロールすることを意図しています。

加えて、これは他の依存関係の代わりに用いることができます。例えば、以下のルールは、その次のルールと等価です。

a: b :exists: c
    commands
a: b :value: $(target-exists c)
    commands

ノート

  • 任意の値をとることができます(指定する変数に制限はありません)。
  • 値はルールが展開されるときに評価されます。なので式には $@#^ のような変数も含めることができます。

8.6 .SCANNER ルール

スキャナルールでは、自動的に依存関係の解析を行う方法について定義します。 .SCANNER ルールは以下のような形となります。

.SCANNER: target: dependencies
    commands

ルールは、指定されたターゲットのソースファイルに定義されている、追加の依存関係を計算するのに用いられます。 .SCANNER コマンドの実行結果は、OMakeのフォーマットで依存関係のシーケンスが標準の出力先に出力される必要があります。例えばGNUシステムでは、 gcc -MM foo.c はファイル foo.c の依存関係を生成するコマンドです(これは #include の情報を元にしています)。

私たちはこれを用いて、 .c ファイルから .o ファイルを生成するため、スキャンされた依存関係を既存の依存関係に追加することができます。以下のスキャナではファイルの依存関係を指定しており、仮に foo.o が指定されたとすると、 gcc -MM foo.c を実行することによって依存関係の解析が行われます。さらには、 foo.c は依存されているので、 foo.c が変更された場合はいつでもスキャナは再計算されます。

.SCANNER: %.o: %.c
    gcc -MM $<

コマンド gcc -MM foo.c は以下の行を出力するものとします。

foo.o: foo.h /usr/include/stdio.h

以上から、ファイル foo.ofoo.h/usr/include/stdio.h に依存しているものと考えられます。これは、もしこれらのファイルのどれか一つが変更された場合、 foo.o はリビルドされるべきであることを示しています。

これはある程度ですがうまく動きます。この機能の利点の一つとしては、 foo.c が変更された場合、いつでもスキャナが再解析を行ってくれるというのが挙げられます。しかしながらこれには問題があります。Cの依存関係は 再帰的 なのです。すなわち、もしファイル foo.h が修正されたとしたら、そのファイルは他のファイルを含んでいることで、さらなる依存関係が生じているのかもしれません。必要なのは foo.h の変更に応じて、再びスキャナを実行することです。

私たちはこの問題を『依存関係を示す値(value dependency)』を用いて解決しました。変数 $& は任意の前回のスキャンから、依存関係の結果を返す変数として定義されています。私たちはこれらの依存関係を、ファイルのMD5による要約を計算して返す digest 関数を用いて追加しました。

.SCANNER: %.o: %.c :value: $(digest $&)
    gcc -MM $<

これで、ファイル foo.h が変更されたときには要約も変更されているのでスキャナは再計算されます。これは依存関係を示す値( $& には foo.h がインクルードされています)によるものです。

ですが、これはまだ完全に正しいというわけではありません。Cコンパイラはインクルードファイルのために 検索パス(search-path) を使用しているのです。ファイル foo.h には何通りものバージョンが存在しており、そのうちの選んでいる一つがインクルードパスに依存しているのかもしれません。必要なのは検索パスの依存関係も含めることです。

$(digest-in-path-optional ...) 関数は検索パスを元にした要約を計算し、この問題に解決案を提示します。

.SCANNER: %.o: %.c :value: $(digest-in-path-optional $(INCLUDES), $&)
   gcc -MM $(addprefix -I, $(INCLUDES)) $<

スキャナルールの標準出力はOMakeによって捉えられるので、OMakeが依存関係を解析できない内容を含むことは許されません。まだ関連付けられていない依存関係について出力することは許されますが、このような依存関係は無視されます。スキャナルールでは標準エラーの出力先に任意の内容を出力することが許されています。このような出力は通常のルールの出力と同様に扱われます。(言い換えれば、これは –output-… オプションを有効にすることで、ユーザーに見せることのできる出力です。)

.SCANNER ルールについての追加例は “3.4.3 依存関係の解析” で見つけることができます。

8.6.1 スキャナの命名と :scanner: 依存関係

スキャナがルール中で使われていることを明示的に指定するほうが有用である場合があります。例えば、私たちは .c ファイルを異なったオプションでコンパイルしたり、あるいは(天よ我らを助けて!) gcc と Microsoft Visual C++ コンパイラ cl の両方を使いたいとしましょう。通常、 .SCANNER のターゲットは特定のターゲットに結びついていないのですが、私たちはこれを好きなように命名することができます。

.SCANNER: scan-gcc-%.c: %.c :value: $(digest-in-path-optional $(INCLUDES), $&)
    gcc -MM $(addprefix -I, $(INCLUDES)) $<

.SCANNER: scan-cl-%.c: %.c :value: $(digest-in-path-optional $(INCLUDES), $&)
    cl --scan-dependencies-or-something $(addprefix /I, $(INCLUDES)) $<

次のステップはスキャナの依存関係を明示的に定義することです。 :scanner: 依存関係はこのために用いられます。この場合、スキャナの依存関係は明示的に指定されます。

$(GCC_FILES): %.o: %.c :scanner: scan-gcc-%c
    gcc ...

$(CL_FILES): %.obj: %.c :scanner: scan-cl-%c
    cl ...

明示的な :scanner: スキャナの指定は、単体の .SCANNER ルールを複数のターゲットに向けて依存関係を生成することにも用いられます。例えば以下の例を見てみましょう。

.SCANNER: scan-all-c: $(GCC_FILES) :value: $(digest-in-path-optional $(INCLUDES), $&)
    gcc -MM $(addprefix -I, $(INCLUDES)) $(GCC_FILES)

$(GCC_FILES): %.o: %.c :scanner: scan-all-c
    ...

上の例はgccが一回だけ呼び出されるという利点と、一つのソースファイルが変更された場合、すべてのファイルが再スキャンされてしまうという欠点の両方を持ち合わせています。

8.6.2 ノート

多くの場合において、あなたは自分の手でスキャナを定義する必要はありません。OMakeにはC, OCaml, LaTeXファイルのためのスキャナ(しかも暗黙的、明示的に命名されたスキャナの両方)が標準で用意されています。

SCANNER_MODE 変数は、暗黙のスキャナの依存関係の使用についてコントロールする変数です。

明示的に :scanner: 依存関係を指定することは、スキャナが間違って指定してしまう危険を減らします。巨大で複雑なプロジェクトでは SCANNER_MODEerror に設定することで、名前がある .SCANNER ルールと明示的な :scanner: 指定を使うようにしましょう。

8.7 .DEFAULT

.DEFAULT ターゲットは、omakeが明示的にターゲットを指定していないようなデフォルトの状態でビルドされるターゲットを指定するために用いられます。以下のルールでは hello プログラムがデフォルトの状態でビルドすることを指定しています。

.DEFAULT: hello

8.8 .SUBDIRS

.SUBDIRS ターゲットはプロジェクトの一部であるサブディレクトリの集合を指定するために用いられます。各々のサブディレクトリには、サブディレクトリの内容をその環境下で評価するための OMakefile をそれぞれ有しているべきです。

.SUBDIRS: src doc tests

このルールでは src , doc , test ディレクトリの中のOMakefileをそれぞれ読み込むことを指定しています。

いくつかの場合─特に OMakefile の内容が似ている大量のサブディレクトリを持っているような場合─各々のディレクトリに分割して OMakefile を持たせるのは不便でしょう。もし .SUBDIRS ルールの中に内容を記述したとすると、その内容が OMakefile の代わりとして使われます。

.SUBDIRS: src1 src2 src3
   println(Subdirectory $(CWD))
   .DEFAULT: lib.a

この場合、サブディレクトリ src1 , src2 , src3 には OMakefile が必要ありません。さらには、たとえ OMakefile が存在していた場合でも、OMakeはそれを無視します。以下の例では OMakefile が含まれていた場合、そのファイルをインクルードするよう指定しています。

.SUBDIRS: src1 src2 src3
    if $(file-exists OMakefile)
       include OMakefile
    .DEFAULT: lib.a

8.9 .INCLUDE

.INCLUDE ターゲットは include 文と似ていますが、 .INCLUDE ではたとえ指定されたファイルが存在していなくても、ビルドするためのルールを指定できます。

.INCLUDE: config
    echo "CONFIG_READ = true" > config

 echo CONFIG_READ is $(CONFIG_READ)

あなたはまた .INCLUDE ルールの依存関係を指定できます。

.INCLUDE: config: config.defaults
   cp config.defaults config

順番どおりにターゲットと依存関係が記述されています。通常の場合ですと、この記法はルールがいつ期限切れになったのかどうかを決定するために用いられます。 .INCLUDE 内のルールは、以下の場合のどれかに当てはまったときに実行されます。

  • ターゲットが存在しない場合
  • 以前からずっとこのルールが実行されていなかった場合
  • ルールが実行された最後の時間から現在までの間に、以下のうちどれかが変更されていた場合
    • ターゲット
    • 依存先
    • コマンド文

これは、たとえ既にターゲットファイルが存在していたとしても、ルールが実行される場合もあることを示しています。もしターゲットが、エディターなどで変更するような(それゆえ上書きされたくない)ファイルを指定する場合、あなたはルールの評価を、ターゲットが既に存在しているかどうかで条件分岐させるべきです。

.INCLUDE: config: config.defaults
    # わたしが注意深く手作業で変更したファイルを上書きさせません!
    if $(not $(file-exists config))
       cp config.defaults config

8.10 .PHONY

“phony” ターゲットは実際には存在しませんが、複数の依存関係を持っているようなターゲットです。Phony ターゲットは .PHONY ルールを用いて指定します。以下の例では、 install ターゲットは実際のファイルではありませんが、( omake install が実行されることによって) install ターゲットが生起された場合はいつでも、いくつかのコマンドが実行されます。

.PHONY: install

install: myprogram.exe
   cp myprogram.exe /usr/bin

8.11 スコープ規則

以前私たちが注意したように、omakeは スコープ化された 言語です。これは非常に融通のきく─プロジェクトの異なるパートは別のパートを気にすることなく、異なった設定を定義することができる─ものとなっています。例えば、プロジェクトのあるパートには CFLAGS=-O3 を持たせてコンパイルさせるが、別のパートには CFLAGS=-g を持たせるといった具合です。

しかし、どのようにしてターゲットファイルのスコープが選択されるのでしょうか?そこで、現在私たちはファイル dir/foo.o をビルドしているものとしましょう。omakeではスコープを決定するために、以下のルールを用います。

  • まず、もし fir/foo.o をビルドするための明示的なルールが指定されていた(そしてそのルールはワイルドカードを用いていない)場合、そのルールに従って、ターゲットをビルドするためのスコープが決定されます。
  • さもなければ、ディレクトリ dir/ はプロジェクトの一部であるので、普通に考えて設定ファイル dir/OMakefile が存在するはずです(あるいは、 OMakefile.SUBDIRS セクションを用いるなどのなにか別の方法で指定しているかもしれません)。この場合、ターゲットのスコープは dir/OMakefile の終わりのスコープです。

このスコープ規則を確かめるために、2つのファイルから “Hello world” プログラムを作る例へと戻ってみましょう。これは OMakefile のサンプルです。( CFLAGS の2つの定義がスコープ規則の確認に用いられます)

# 実行ファイルはデバッグオプションを用いてコンパイルされます
CFLAGS = -g
hello: hello_code.o hello_lib.o
   $(CC) $(CFLAGS) -o $@ $+

# CFLAGSの再定義
CFLAGS += -O3

このプロジェクトでは、ターゲット hello明示的 です。 hello ターゲットのスコープは hello: から始まる行なので、 CFLAGS の値は -g となります。別の2つのターゲット hello_code.ohello_lib.o は明示的にターゲットが指定されていないため、これらのスコープは OMakefile の終わりで決定しますので、 CFLAGS の値は -g -O3 となります。これは、 helloCFLAGS=-g でリンクされて、 .o ファイルが CFLAGS=-g -O3 でコンパイルされることを表しています。

私たちは明示的にターゲットを指定することで、任意のターゲットのふるまいを変えることができます。例えば、私たちは hello_lib.o を、プリプロセッサ変数 LIBRARY を用いてコンパイルしたいものとしましょう。

# 実行ファイルはデバッグオプションを用いてコンパイルされます
CFLAGS = -g
hello: hello_code.o hello_lib.o
   $(CC) $(CFLAGS) -o $@ $+

# hello_lib.o を CFLAGS = -g -DLIBRARY オプションでコンパイル
section
    CFLAGS += -DLIBRARY
    hello_lib.o:

# CFLAGSの再定義
CFLAGS += -O3

この場合、 hello_lib.o の暗黙のターゲットが、 CFLAGS=-g -DLIBRARY のスコープ中で指定されています。これはルールの内容が指定されていないため、 .o ファイルは、通常の暗黙のルールを使って( CFLAGS=-g -DLIBRARY のオプションで)コンパイルされます。

8.11.1 暗黙のルールのスコーピング

暗黙のルール(ワイルドパターンを含む)は グローバルではなく 、通常のスコープ規則に従っています。これによって、異なるプロジェクトのパートは、異なる暗黙のルールの集合を持つことができるようになりました。もしやってみたいのであれば、私たちは上のサンプルを、以下のような新しい暗黙のルールに修正することができます。

# 実行ファイルはデバッグオプションを用いてコンパイルされます
CFLAGS = -g
hello: hello_code.o hello_lib.o
   $(CC) $(CFLAGS) -o $@ $+

# hello_lib.o を CFLAGS = -g -DLIBRARY オプションでコンパイル
section
    %.o: %.c
        $(CC) $(CFLAGS) -DLIBRARY -c $<
    hello_lib.o:

# CFLAGSの再定義
CFLAGS += -O3

この場合、ターゲット hello_lib.o は、 %.o をビルドするための新しい暗黙のルールがあるスコープ中でビルドされます。この暗黙のルールでは -DLIBRARY オプションを加えています。この暗黙のルールはターゲット hello_lib.o だけに定義されるため、ターゲット hello_code.o は普通にビルドされます。

8.11.2 .SCANNER ルールのスコーピング

スキャナルールは通常のスコープ規則と同様にスコープされます。 .SCANNER ルールが明示的に指定されていた(ワイルドカードパターンを含まない)場合、スキャンターゲットのスコープはルールと同様に扱われます。 .SCANNER ルールが暗黙的に指定されていた場合、その環境は :scanner: 依存関係によって決定されます。

# 実行ファイルはデバッグオプションを用いてコンパイルされます
CFLAGS = -g
hello: hello_code.o hello_lib.o
   $(CC) $(CFLAGS) -o $@ $+

# .c ファイルのスキャナ
.SCANNER: scan-c-%.c: %.c
   $(CC) $(CFLAGS) -MM $<

# hello_lib.o を CFLAGS = -g -DLIBRARY オプションでコンパイル
section
    CFLAGS += -DLIBRARY
    hello_lib.o: hello_lib.c :scanner: scan-c-hello_lib.c
       $(CC) $(CFLAGS) -c $<

# hello_code.c を CFLAGS = -g -O3 オプションでコンパイル
section
    CFLAGS += -O3
    hello_code.o: hello_code.c :scanner: scan-c-hello_code.c
       $(CC) $(CFLAGS) -c $<

再びこの例が登場しましたーあなたは恐らく、このような複雑怪奇な設定を書きたいとは思っていないでしょう!この場合、 .SCANNER ルールでは、Cコンパイラが依存関係を計算するために -MM フラグを用いて呼び出すことを指定しています。ターゲット hello_lib.o には、スキャナは CFLAGS=-g -DLIBRARY が呼ばれ、 hello_code.o には、 CFLAGS=-g -O3 が呼ばれます。

8.11.3 .PHONY ターゲットのスコーピング

Phonyターゲット(実際のファイルに相当しないターゲット)は .PHONY ルールを用いて定義されます。Phonyターゲットは普通にスコープされます。以下の例はよくある間違いで、 .PHONY ターゲットはそれが使われている 後で 宣言されています。

# !!この例は正常に動きません!!
all: hello

hello: hello_code.o hello_lib.o
    $(CC) $(CFLAGS) -o $@ $+

.PHONY: all

.PHONY 宣言が非常に遅れてしまっているために、この例は期待している通りには動きません。正しくは .PHONY 宣言を最初に持っていきます。

# Phonyターゲットはそれが使われる前に宣言されなければなりません
.PHONY: all

all: hello

hello: hello_code.o hello_lib.o
    $(CC) $(CFLAGS) -o $@ $+

Phonyターゲットはサブディレクトリに渡されます。実用性から、すべての .PHONY ターゲットを .SUBDIRS が表れる前に、ルートの OMakefile に宣言することは賢い判断と言えるでしょう。これは以下の点を保証してくれます。

  1. 各々のサブディレクトリにPhonyターゲットが渡される
  2. プロジェクトのルートディレクトリからビルドすることができる
.PHONY: all install clean

.SUBDIRS: src lib clib

ノート

.SUBDIRS を経由したサブディレクトリによって .PHONY ターゲットが継承されたときに、全体の .PHONY ターゲット(これはグローバルの一部分です)の階層構造が作られます。詳細は下のセクション “8.12.2 .PHONYターゲットの階層構造” を参照してください。

8.12 サブディレクトリからOMakeを実行

omake foo を実行した場合、 全体 のプロジェクトの文脈から foo ファイルに関連する部分だけをビルドします。たとえそれがプロジェクトのサブディレクトリから実行したとしてもです。それゆえ、もし bar/baz が通常のターゲット( .PHONY ターゲットではない)であるとすると、 omake bar/baz を実行することは (cd bar; omake baz) を実行することと等価です。

上のルールには、2つの注目に値する例外が存在します。

  • サブディレクトリがプロジェクトの一部でなかった( .SUBDIRS に存在しない)場合、あなたがそのディレクトリから実行させようとすると、OMakeは文句を言うでしょう。
  • サブディレクトリ自身に OMakeroot が含まれている場合、OMakeはサブディレクトリを別のプロジェクトとして解釈します。これはあまり良い考えではないので推奨しません。

8.12.1 サブディレクトリのPhonyターゲット

.PHONY: clean がルートの OMakefile で宣言されており、さらにルートの OMakefile と、サブディレクトリのいくつかの OMakefileclean: ルールを含んでいるものとします。この場合、

  • omake clean をルートのディレクトリで実行したときには、(適切なディレクトリの中にある各々の)すべての clean: ルールが実行されます。
  • omake clean をサブディレクトリで実行したときには、カレントサブディレクトリの clean: ルールだけが実行されます。

上のルールは .DEFAULT を含んだ、ビルドインの .PHONY ターゲットに等しく適用されます。すなわち、もしOMakeがプロジェクトのルートディレクトリで(引数なしで)実行された場合、プロジェクト中のすべての .DEFAULT がビルドされます。一方で、サブディレクトリでOMakeが(引数なしで)実行された場合、サブディレクトリ中で定義された .DEFAULT ターゲットだけがビルドされます。

以下のセクションでは上のようなふるまいをもたらしている、基本的な概念について説明します。

8.12.2 .PHONYターゲットの階層構造

ルートの OMakefile.PHONY:clean を含んでいた場合、OMakeは以下を生成します。

  • “グローバル(global)”なPhonyターゲット /.PHONY/clean (先頭の “/” に注目してください)
  • カレントディレクトリに所属している、”関連している(relative)”Phonyターゲット .PHONY/clean (先頭の “/” が欠けていることに注目してください)
  • 依存関係 /.PHONY/clean: .PHONY/clean

ルートの OMakefile では、 .PHONY: clean 宣言の後にくるすべての clean: ... ルールは、 .PHONY/clean ターゲットのルールとして解釈されます。

それでは、次にOMakeは(上の .PHONY: clean 宣言のスコープ上で) .SUBDIRS: foo に遭遇したとしましょう。この場合、OMakeは以下の処理を行います。

  • 新しい .PHONY/foo/clean Phonyターゲットを生成します。このターゲットは”関連している”Phonyターゲットです。
  • 依存関係 .PHONY/clean: .PHONY/foo/clean を生成します。
  • .SUBDIRS: foo の内容を処理するか、もし内容が空であった場合は foo/OMakefile を読み込みます。処理している間、これらの指示は foo ディレクトリに関連しているものと解釈します。特に、すべての clean: ... ルールは .PHONY/foo/clean に適用されます。

あなたが omake clean をプロジェクトのルートディレクトリで実行した場合、これは omake .PHONY/clean として解釈され(通常のターゲットに関しても同様です)、 .PHONY/clean のルールと、その依存関係 .PHONY/foo/clean のルールの両方が実行されます。また、 (cd foo; omake clean) を実行することは、 omake .PHONY/foo/clean を実行することと等価であり、 .PHONY/foo/clean のルールだけが実行されます。これもまた、通常のターゲットに関して同様です。

8.13 ルール中でのパス名

ルール中では、ターゲットと依存先は最初に”ファイル”として変換されます(詳細は “10.1.1 file, dir” を参照してください)。これらはコマンドライン上で文字列として変換される値です。また、これによっていくつかの期待していないふるまいを起こすことがあります。以下の例では、 absname 関数はファイル a の絶対パスを返す関数ですが、それにも関わらずこのルールでは相対パスとして出力されています。

.PHONY: demo
demo: $(absname a)
    echo $<

# omakeのデモ
a

この振る舞いについては、議論の余地がある良い理由があります。Win32のシステム上では、 / という文字は『オプションの指定子』として判定されます。そして、パス名のセパレータには \ が用いられています。OMakeはファイル名を自動的に変換することで、両方のシステムで期待通りの動きをするようにしてくれます。

demo: a/b
    echo $<

# omakeのデモ(Unixのシステム上)
a/b
# omakeのデモ(Win32のシステム上)
a\b

ときどき、あなたはターゲット名を、ルール中のコマンドに文字通り渡したいと思うことがあるかもしれません。これを解決する一つの方法は、変数を指定してあげることです。

SRC = a/b $(absname c/d)
demo: $(SRC)
    echo $(SRC)

# omakeのデモ(Win32のシステム上)
a/b c:\...\c\d

ついでに、あなたはファイル名を自動的に絶対パスに展開してほしいと思うこともあるかもしれません。例えば、エラーを見るために、OMakeの出力を解析するような場合には有効でしょう。これを実現するために、あなたは --absname オプションを用いることができます(詳細は A.3.20 -absname を参照してください)。もしあなたが omake--absname オプションで呼び出した場合、すべてのファイル名は絶対パスとして展開されます。

# omake --absname のデモ(Unixのシステム上)
/home/.../a/b /home/.../c/d

ついでに、 --absname オプションはスコープ化されています。もしあなたがこれを限られたルールでのみ用いたい場合は、 OMakeFlags 関数を使うことで、 --absname オプションを適用するかどうかをコントロールすることができます。

section
   OMakeFlags(--absname)
   demo: a
       echo $<

# omakeデモ
/home/.../a

警告

--absname オプションは現在、実験的な機能として搭載しています。