前へ | 次へ | 目次 | 英語原版(gnu.org)

7 Makefileの条件分岐

 条件分岐のディレクティブを使うと、変数の値に応じて、makefile の一部を実行させたり無視させたりすることができます。条件分岐では、ある変数の値を別の変数の値と比べたり、変数の値を固定の文字列と比べたりできます。条件分岐が制御するのは、make が makefile の中で実際に「目にする」内容です。そのため、レシピが実行される時点での動きを条件分岐で制御することはできません

7.1 条件分岐の例

 次に挙げる条件分岐の例は、変数 CC が「gcc」であればある一群のライブラリを使い、そうでなければ別の一群のライブラリを使うように make に指示するものです。これは、ルールに用いる 2 行のレシピのうち、どちらを使うかを制御することで実現しています。その結果、make の引数として「CC=gcc」を与えると、使われるコンパイラだけでなく、リンクされるライブラリも切り替わります。

libs_for_gcc = -lgnu
normal_libs =

foo: $(objects)
ifeq ($(CC),gcc)
        $(CC) -o foo $(objects) $(libs_for_gcc)
else
        $(CC) -o foo $(objects) $(normal_libs)
endif

 この条件分岐では、ifeqelseendif という 3 つのディレクティブを使っています。

 ifeq ディレクティブは条件分岐の始まりを示し、条件を指定します。これは 2 つの引数を取り、引数同士はカンマで区切られ、全体は括弧で囲まれます。両方の引数に対して変数の置換が行われたうえで、それらが比較されます。ifeq に続く makefile の各行は、2 つの引数が一致すれば実行され、一致しなければ無視されます。

 else ディレクティブは、直前の条件が成立しなかったときに、それに続く各行を実行させます。上の例では、これは最初のリンクコマンドが使われなかったときには必ず、2 つ目の代わりのリンクコマンドが使われる、ということを意味します。条件分岐の中に else を置くかどうかは任意です。

 endif ディレクティブは条件分岐を終わらせます。すべての条件分岐は endif で終わらなければなりません。その後ろには、条件に関係のない通常の makefile のテキストが続きます。

 この例が示すように、条件分岐はテキストのレベルで働きます。すなわち、条件分岐に含まれる行は、条件に応じて、makefile の一部として扱われたり、無視されたりします。これが、ルールのような makefile のより大きな構文単位が、条件分岐の始まりや終わりをまたいでもかまわない理由です。

 変数 CC が「gcc」という値を持つとき、上の例は次のような効果を持ちます:

foo: $(objects)
        $(CC) -o foo $(objects) $(libs_for_gcc)

 変数 CC がそれ以外のどんな値を持つときは、次のような効果になります:

foo: $(objects)
        $(CC) -o foo $(objects) $(normal_libs)

 これと同じ結果は、別のやり方でも得られます。すなわち、変数への代入そのものを条件分岐の対象とし、その変数は条件なしで使う、という方法です:

libs_for_gcc = -lgnu
normal_libs =

ifeq ($(CC),gcc)
  libs=$(libs_for_gcc)
else
  libs=$(normal_libs)
endif

foo: $(objects)
        $(CC) -o foo $(objects) $(libs)

7.2 条件分岐の書き方

 else を持たない単純な条件分岐の書き方は、以下のとおりです:

conditional-directive
text-if-true
endif

 text-if-true には任意の行を書くことができ、条件が真であれば makefile の一部として扱われます。条件が偽であれば、代わりに何のテキストも使われません。

 複雑な条件分岐の書き方は、以下のとおりです:

conditional-directive
text-if-true
else
text-if-false
endif

 あるいは:

conditional-directive-one
text-if-one-is-true
else conditional-directive-two
text-if-two-is-true
else
text-if-one-and-two-are-false
endif

 「else conditional-directive」という形の節は、必要なだけいくつでも置くことができます。いずれかの条件がひとたび真になると、その text-if-true が使われ、それ以外の節は使われません。どの条件も真にならなければ、text-if-false が使われます。text-if-truetext-if-false は、何行のテキストであってもかまいません。

 conditional-directive の書き方は、単純な条件分岐でも複雑な条件分岐でも、また else の後であってもなくても、同じです。異なる条件を調べる 4 種類のディレクティブがあります。それらを表にまとめると、次のようになります:

ifeq (arg1, arg2)
ifeq 'arg1' 'arg2'
ifeq "arg1" "arg2"
ifeq "arg1" 'arg2'
ifeq 'arg1' "arg2"

 arg1arg2 に含まれるすべての変数参照を展開し、それらを比較します。両者が同一であれば text-if-true が有効になり、そうでなければ text-if-false が(もしあれば)有効になります。

 変数が空でない値を持っているかどうかを調べたい、という場面はよくあります。値が変数や関数の複雑な展開の結果として得られる場合、あなたが空だと考えている展開結果が、実は空白文字を含んでいて、空とは見なされないことがあります。しかし、strip 関数(文字列の置換と解析を行う関数を参照してください)を使えば、空白を空でない値と解釈してしまうのを避けられます。例えば、次のように書けます:

ifeq ($(strip $(foo)),)
text-if-empty
endif

 こう書けば、$(foo) の展開結果が空白文字を含んでいても、text-if-empty が評価されます。

ifneq (arg1, arg2)
ifneq 'arg1' 'arg2'
ifneq "arg1" "arg2"
ifneq "arg1" 'arg2'
ifneq 'arg1' "arg2"

 arg1arg2 に含まれるすべての変数参照を展開し、それらを比較します。両者が異なっていれば text-if-true が有効になり、そうでなければ text-if-false が(もしあれば)有効になります。

ifdef variable-name

 ifdef の形式は、変数への参照ではなく、変数の名前を引数として取ります。その変数の値が空でない値であれば text-if-true が有効になり、そうでなければ text-if-false が(もしあれば)有効になります。一度も定義されたことのない変数は、空の値を持ちます。variable-name のテキストは展開されるので、これは変数名へと展開される変数や関数であってもかまいません。例えば、次のとおりです:

bar = true
foo = bar
ifdef $(foo)
frobozz = yes
endif

 変数参照 $(foo) が展開されて bar となり、これが変数名として扱われます。変数 bar 自体は展開されませんが、その値が空でないかどうかを判定するために調べられます。

 ifdef は変数が値を持っているかどうかを調べるだけ、という点に注意してください。その値が空でないかを確かめるために変数を展開することはしません。したがって、ifdef を使った判定は、foo = のような定義を除き、すべての定義に対して真を返します。空の値かどうかを調べたいときは、ifeq ($(foo),) を使ってください。例えば、次のように書くと:

bar =
foo = $(bar)
ifdef foo
frobozz = yes
else
frobozz = no
endif

 「frobozz」には「yes」が設定されます。一方、次のように書くと:

foo =
ifdef foo
frobozz = yes
else
frobozz = no
endif

 「frobozz」には「no」が設定されます。

ifndef variable-name

 変数 variable-name が空の値であれば text-if-true が有効になり、そうでなければ text-if-false が(もしあれば)有効になります。variable-name の展開と判定の規則は、ifdef ディレクティブと同一です。

 条件分岐ディレクティブの行頭には、余分なスペースを置いても無視されますが、タブを置くことは許されません。(行がタブで始まっていると、それはあるルールのレシピの一部と見なされてしまいます。)この点を除けば、ディレクティブ名の中や引数の中を別とすれば、余分なスペースやタブをどこに挿入しても何の影響もありません。行末には「#」で始まるコメントを置くことができます。

 条件分岐で役割を果たすもう 2 つのディレクティブが elseendif です。これらはいずれも 1 語だけで書き、引数は取りません。行頭に余分なスペースを置いても無視され、行末にスペースやタブを置くこともできます。行末には「#」で始まるコメントを置くことができます。

 条件分岐は、make が makefile のどの行を使うかに影響します。条件が真であれば、maketext-if-true の行を makefile の一部として読み込みます。条件が偽であれば、make はそれらの行を完全に無視します。このことから、ルールのような makefile の構文単位は、条件分岐の始まりや終わりをまたいで分割しても安全だ、ということになります。

 make は makefile を読み込むときに条件分岐を評価します。したがって、条件分岐の判定の中で自動変数を使うことはできません。自動変数はレシピが実行されるまで定義されないからです(自動変数を参照してください)。

 収拾のつかない混乱を防ぐため、条件分岐をある makefile で始めて、別の makefile で終わらせることは許されていません。ただし、条件分岐の中に include ディレクティブを書くことはできます。その場合、取り込まれたファイルの中でその条件分岐を終わらせようとしてはいけません。

7.3 フラグを調べる条件分岐

 変数 MAKEFLAGSfindstring 関数(文字列の置換と解析を行う関数を参照してください)と組み合わせて使うと、「-t」のような make のコマンドフラグを調べる条件分岐を書くことができます。これは、ファイルを最新と見せかけるのに touch だけでは足りない場合に役立ちます。

 思い出してほしいのですが、MAKEFLAGS は(「-t」のような)1 文字のオプションをすべて先頭の単語にまとめます。そして、1 文字のオプションが一つも与えられなかった場合、その単語は空になります。これに対処するには、先頭に何か値を足して必ず単語が存在するようにしておくと便利です。例えば「-$(MAKEFLAGS)」のようにします。

 findstring 関数は、ある文字列が別の文字列の部分文字列として現れるかどうかを判定します。「-t」フラグを調べたいなら、1 つ目の文字列に「t」を、もう一方に MAKEFLAGS の先頭の単語を指定します。

 例えば、アーカイブファイルを最新と印付ける仕上げに「ranlib -t」を使うよう手配するには、次のようにします:

archive.a: …
ifneq (,$(findstring t,$(firstword -$(MAKEFLAGS))))
        +touch archive.a
        +ranlib -t archive.a
else
        ranlib archive.a
endif

 「+」という接頭辞は、それらのレシピ行を「再帰的」なものとして印付けます。これにより、「-t」フラグが使われていても、それらの行は実行されます。makeの再帰的な使い方を参照してください。


前へ | 次へ | 目次 | 英語原版(gnu.org)