変数(variable)とは、makefile の中で定義する名前で、値(value)と呼ばれる文字列を表すものです。これらの値は、明示的に要求することで、ターゲットや前提条件、レシピ、その他 makefile のさまざまな箇所に展開・差し込まれます。(make の他のバージョンの中には、変数をマクロ(macro)と呼ぶものもあります。)
makefile 中の変数や関数は、原則として読み込まれた時点で展開されます。ただし例外があり、レシピの中、‘=’ を使った変数定義の右辺、そして define ディレクティブを使った変数定義の本体については、その時点では展開されません。変数を展開したときに得られる値は、展開する時点での最新の定義のものです。言い換えれば、変数は動的にスコープが決まる(動的スコープ)ということです。
変数は、ファイル名のリスト、コンパイラに渡すオプション、実行するプログラム、ソースファイルを探すディレクトリ、出力を書き込むディレクトリなど、思いつくかぎりあらゆるものを表すことができます。
変数名には、‘:’、‘#’、‘=’、空白を含まない任意の文字列を使えます。ただし、英字・数字・アンダースコア以外の文字を含む変数名は慎重に扱うべきです。シェルによっては、そうした名前の変数を環境経由でサブmakeに渡せないことがあるからです(サブmakeへ変数を伝えるを参照してください)。‘.’ と大文字で始まる変数名は、make の将来のバージョンで特別な意味を与えられる可能性があります。
変数名は大文字と小文字を区別します。‘foo’、‘FOO’、‘Foo’ はすべて別々の変数を指します。
変数名には大文字を使うのが慣例ですが、本書では、makefile 内部の用途に使う変数には小文字を使い、暗黙のルールを制御するパラメータや、ユーザーがコマンドオプションで上書きすべきパラメータには大文字を予約しておくことをおすすめします(変数のオーバーライドを参照してください)。
ごく一部の変数は、1 個の記号文字や数文字だけの名前を持ちます。これらは自動変数(automatic variable)と呼ばれ、それぞれ特定の専門的な用途があります。自動変数を参照してください。
override ディレクティブ
変数の値を差し込むには、ドル記号に続けて、丸括弧または波括弧で囲んだ変数名を書きます。すなわち、‘$(foo)’ でも ‘${foo}’ でも、どちらも変数 foo への正しい参照になります。‘$’ にこのような特別な意味があるからこそ、ファイル名やレシピの中で 1 個のドル記号そのものを表したいときには ‘$$’ と書かなければならないのです。
変数参照は、どんな文脈でも使えます。ターゲット、前提条件、レシピ、ほとんどのディレクティブ、新しい変数の値などです。次に、よくある例として、あるプログラムを構成するすべてのオブジェクトファイルの名前を変数に持たせた場合を示します:
objects = program.o foo.o utils.o
program : $(objects)
cc -o program $(objects)
$(objects) : defs.h
変数参照は、厳密な文字どおりの置換によって機能します。したがって、次のルール
foo = c
prog.o : prog.$(foo)
$(foo)$(foo) -$(foo) prog.$(foo)
は、C プログラム prog.c をコンパイルするのに使えます。変数への代入では値の前にある空白は無視されるので、foo の値はちょうど ‘c’ になります。(実際の makefile をこんな書き方で書いてはいけませんよ!)
ドル記号の後ろに、ドル記号でも開き丸括弧でも開き波括弧でもない 1 文字が続く場合、その 1 文字が変数名として扱われます。したがって、変数 x は ‘$x’ で参照できます。ただし、この書き方は混乱を招くことがあります(たとえば ‘$foo’ は、変数 f の後ろに文字列 oo が続いたものと解釈されます)。そのため、省略することで読みやすさが大きく向上する場合を除き、1 文字の変数であってもすべての変数を丸括弧か波括弧で囲むことをおすすめします。括弧を省略すると読みやすくなることが多いのは、自動変数の場合です(自動変数を参照してください)。
GNU make では、変数が値を得る方法にいくつかの違いがあります。これを変数の種別(flavor)と呼びます。種別は、makefile 内で代入された値をどのように扱うか、そしてその後その変数が使われ展開されるときに値がどのように管理されるかによって区別されます。
変数の最初の種別は再帰展開(recursively expanded)変数です。この種類の変数は、‘=’ を使った行(変数を設定するを参照してください)、または define ディレクティブ(複数行にわたる変数を定義するを参照してください)で定義されます。指定した値はそのまま文字どおりに格納されます。もしその値が他の変数への参照を含んでいると、それらの参照は、この変数が(他の文字列を展開する過程で)差し込まれるたびに展開されます。これが起きることを再帰展開(recursive expansion)と呼びます。
例えば、
foo = $(bar) bar = $(ugh) ugh = Huh? all:;echo $(foo)
は ‘Huh?’ を出力します。‘$(foo)’ が ‘$(bar)’ に展開され、それが ‘$(ugh)’ に展開され、最終的に ‘Huh?’ に展開されるからです。
この種別の変数は、make の他のほとんどのバージョンが唯一サポートしている種類です。これには利点も欠点もあります。利点(多くの人がそう言うでしょう)は、次のような点です:
CFLAGS = $(include_dirs) -O include_dirs = -Ifoo -Ibar
これは意図したとおりに動きます。‘CFLAGS’ がレシピの中で展開されるとき、‘-Ifoo -Ibar -O’ に展開されるのです。大きな欠点は、次のように変数の末尾に何かを追加することができない点です。
CFLAGS = $(CFLAGS) -O
なぜなら、変数の展開が無限ループに陥ってしまうからです。(実際には make が無限ループを検出してエラーを報告します。)
もう一つの欠点は、定義の中で参照されている関数(テキストを変換する関数を参照してください)が、変数が展開されるたびに実行されてしまうことです。これにより make の動作が遅くなります。さらに悪いことに、wildcard 関数や shell 関数の結果が予測不能になります。これらの関数がいつ呼ばれるか、あるいは何回呼ばれるかすら、簡単には制御できないからです。
再帰展開変数の問題点や不便さを避けるために、もう一つの種別があります。それが単純展開変数です。
単純展開変数(simply expanded variable)は、‘:=’ または ‘::=’ を使った行で定義されます(変数を設定するを参照してください)。GNU make では両者は同等ですが、POSIX 標準で規定されているのは ‘::=’ の形式だけです(‘::=’ のサポートは POSIX Issue 8 で POSIX 標準に追加されました)。
単純展開変数の値は、変数が定義されるときに一度だけ走査され、他の変数や関数への参照があればそこで展開されます。その展開が完了すると、変数の値は二度と展開されません。変数が使われるときには、値はそのまま文字どおりにコピーされ、それが展開結果となります。もし値に変数参照が含まれていれば、展開結果にはこの変数が定義された時点でのそれらの値が入ります。したがって、
x := foo y := $(x) bar x := later
は、次と同等です。
y := foo bar x := later
次は、もう少し込み入った例で、‘:=’ を shell 関数と組み合わせて使う様子を示しています。(関数 shellを参照してください。)この例では、レベルからレベルへ渡されるときに変化する MAKELEVEL 変数の使い方も示しています。(MAKELEVEL の詳細はサブmakeへ変数を伝えるを参照してください。)
ifeq (0,${MAKELEVEL})
whoami := $(shell whoami)
host-type := $(shell arch)
MAKE := ${MAKE} host-type=${host-type} whoami=${whoami}
endif
この ‘:=’ の使い方の利点は、典型的な「ディレクトリへ降りていく」レシピが次のように書けることです:
${subdirs}:
${MAKE} -C $@ all
単純展開変数は、ほとんどのプログラミング言語の変数のように振る舞うため、複雑な makefile プログラミングをより予測しやすくしてくれます。単純展開変数を使えば、変数を自分自身の値(あるいは展開関数のいずれかで何らかの加工を施した自身の値)を使って再定義できますし、展開関数をはるかに効率よく使えます(テキストを変換する関数を参照してください)。
また、単純展開変数を使えば、変数値の先頭に制御された空白を入れることもできます。入力された値の先頭にある空白文字は、変数参照や関数呼び出しの置換が行われる前に取り除かれます。つまり、変数値の先頭に空白を含めたい場合は、次のように変数参照で守ってやればよいのです。
nullstring := space := $(nullstring) # end of the line
ここで変数 space の値は、ちょうど空白 1 個になります。コメント ‘# end of the line’ は、ここでは分かりやすさのために付けてあるだけです。変数値の末尾の空白文字は取り除かれないので、行末に空白を 1 個置くだけでも同じ効果が得られます(ただし、それでは非常に読みにくくなります)。変数値の末尾に空白を置くときは、このように行末にコメントを付けて意図を明確にしておくのがよいでしょう。逆に、変数値の末尾に空白文字を入れたくない場合は、次のように空白の後に何気ないコメントを行末に付けてしまわないよう気をつけなければなりません。
dir := /foo/bar # directory to put the frobs in
ここでは、変数 dir の値は ‘/foo/bar ’(末尾に空白 4 個)となってしまい、これはおそらく意図したものではありません。(この定義のまま ‘$(dir)/file’ のように使う場面を想像してみてください!)
もう一つの代入形式は即時展開を行いますが、単純代入とは違い、結果としてできる変数は再帰展開変数になります。すなわち、使われるたびに再展開されるのです。予期しない結果を避けるために、値が即時に展開された後は自動的にクォートされます。展開後の値に含まれるすべての $ は $$ に変換されます。この種類の代入には ‘:::=’ 演算子を使います。例えば、
var = first OUT :::= $(var) var = second
とすると、OUT 変数にはテキスト ‘first’ が入ります。一方、次の場合は、
var = one$$two OUT :::= $(var) var = three$$four
OUT 変数にはテキスト ‘one$$two’ が入ります。値は変数が代入されるときに展開されるので、結果はまず var の最初の値の展開、すなわち ‘one$two’ となります。それから、代入が完了する前に値が再エスケープされ、最終的な結果 ‘one$$two’ が得られます。
この後、OUT 変数は再帰展開変数として扱われるので、使われるときに再展開されます。
これは ‘:=’ / ‘::=’ 演算子と機能的には同等に見えますが、いくつかの違いがあります:
第一に、代入後の変数は通常の再帰展開変数です。‘+=’ で追加する場合、右辺の値は即時には展開されません。‘+=’ 演算子で右辺を即時に展開してほしい場合は、代わりに ‘:=’ / ‘::=’ 代入を使うべきです。
第二に、これらの変数は単純展開変数よりわずかに効率が劣ります。使われるときに単にコピーされるのではなく、再展開が必要だからです。とはいえ、すべての変数参照はエスケープされているので、この展開は単に値のエスケープを解除するだけで、変数を展開したり関数を実行したりはしません。
次にもう一つ例を示します:
var = one$$two OUT :::= $(var) OUT += $(var) var = three$$four
この後、OUT の値はテキスト ‘one$$two $(var)’ になります。この変数が使われると展開され、結果は ‘one$two three$four’ になります。
この代入スタイルは、従来の BSD make の ‘:=’ 演算子と同等です。ご覧のとおり、GNU make の ‘:=’ 演算子とはわずかに動作が異なります。:::= 演算子は、移植性を提供するために POSIX 仕様の Issue 8 で追加されました。
変数用のもう一つの代入演算子に ‘?=’ があります。これは条件付き変数代入演算子と呼ばれ、変数がまだ定義されていない場合にのみ効果を持ちます。次の文、
FOO ?= bar
は、次と完全に同等です(関数 originを参照してください):
ifeq ($(origin FOO), undefined) FOO = bar endif
空の値が設定された変数も「定義済み」とみなされる点に注意してください。そのため、‘?=’ はその変数を設定しません。
この節では、変数をより柔軟に参照するために使える、いくつかの高度な機能を説明します。
置換参照(substitution reference)は、変数の値に、指定した変更を加えて差し込みます。その形式は ‘$(var:a=b)’(または ‘${var:a=b}’)で、その意味は、変数 var の値を取り、その値の中で各単語の末尾にある a をすべて b に置き換えて、結果の文字列を差し込む、というものです。
ここで「単語の末尾にある」というのは、置き換えられるためには a が、空白文字に続かれているか、値の末尾にあるかのいずれかでなければならない、という意味です。値の中のそれ以外の a の出現は変更されません。例えば、
foo := a.o b.o l.a c.o bar := $(foo:.o=.c)
は ‘bar’ を ‘a.c b.c l.a c.c’ に設定します。変数を設定するを参照してください。
置換参照は、patsubst 展開関数(文字列の置換と解析を行う関数を参照してください)の簡略表記です。‘$(var:a=b)’ は ‘$(patsubst %a,%b,var)’ と同等です。make の他の実装との互換性のために、patsubst に加えて置換参照も提供しています。
もう一つの種類の置換参照では、patsubst 関数の力を余すところなく使えます。形式は上で述べた ‘$(var:a=b)’ と同じですが、今度は a が 1 個の ‘%’ 文字を含んでいなければなりません。この場合は ‘$(patsubst a,b,$(var))’ と同等です。patsubst 関数の説明については文字列の置換と解析を行う関数を参照してください。例えば、
foo := a.o b.o l.a c.o bar := $(foo:%.o=%.c)
は ‘bar’ を ‘a.c b.c l.a c.c’ に設定します。
計算で求める変数名は高度な概念で、より高度な makefile プログラミングで非常に役立ちます。単純な状況では考慮する必要はありませんが、きわめて便利になることがあります。
変数は、変数名の中で参照することができます。これを計算で求める変数名(computed variable name)、またはネストした変数参照(nested variable reference)と呼びます。例えば、
x = y y = z a := $($(x))
は、a を ‘z’ と定義します。‘$($(x))’ の内側の ‘$(x)’ が ‘y’ に展開されるので、‘$($(x))’ は ‘$(y)’ に展開され、それがさらに ‘z’ に展開されるのです。ここでは、参照する変数の名前が明示的に書かれているのではなく、‘$(x)’ の展開によって計算されます。ここでの参照 ‘$(x)’ は、外側の変数参照の中にネストされています。
前の例は 2 段のネストを示していますが、ネストは何段でも可能です。例えば、次は 3 段の例です:
x = y y = z z = u a := $($($(x)))
ここでは、最も内側の ‘$(x)’ が ‘y’ に展開されるので、‘$($(x))’ は ‘$(y)’ に展開され、それがさらに ‘z’ に展開されます。ここで ‘$(z)’ が残り、これが ‘u’ になります。
変数名の中にある再帰展開変数への参照は、通常どおり再展開されます。例えば、
x = $(y) y = z z = Hello a := $($(x))
は、a を ‘Hello’ と定義します。‘$($(x))’ は ‘$($(y))’ になり、それが ‘$(z)’ になり、‘Hello’ になります。
ネストした変数参照には、他の参照と同じように、修飾付きの参照や関数呼び出し(テキストを変換する関数を参照してください)を含めることもできます。例えば、subst 関数(文字列の置換と解析を行う関数を参照してください)を使うと、
x = variable1 variable2 := Hello y = $(subst 1,2,$(x)) z = y a := $($($(z)))
は、最終的に a を ‘Hello’ と定義します。これほど入り組んだネスト参照をわざわざ書きたい人がいるとは思えませんが、ちゃんと動きます。‘$($($(z)))’ は ‘$($(y))’ に展開され、それが ‘$($(subst 1,2,$(x)))’ になります。これは x から値 ‘variable1’ を取り、置換によってそれを ‘variable2’ に変えるので、文字列全体が ‘$(variable2)’ という単純な変数参照になり、その値は ‘Hello’ となります。
計算で求める変数名は、必ずしも 1 個の変数参照だけで構成されている必要はありません。複数の変数参照に加えて、変化しないテキストを含めることもできます。例えば、
a_dirs := dira dirb 1_dirs := dir1 dir2
a_files := filea fileb 1_files := file1 file2
ifeq "$(use_a)" "yes" a1 := a else a1 := 1 endif
ifeq "$(use_dirs)" "yes" df := dirs else df := files endif dirs := $($(a1)_$(df))
は、use_a と use_dirs の設定に応じて、dirs に a_dirs、1_dirs、a_files、1_files のいずれかと同じ値を与えます。
計算で求める変数名は、置換参照の中でも使えます:
a_objects := a.o b.o c.o 1_objects := 1.o 2.o 3.o sources := $($(a1)_objects:.o=.c)
は、a1 の値に応じて、sources を ‘a.c b.c c.c’ または ‘1.c 2.c 3.c’ と定義します。
このようなネストした変数参照の使い方に関する唯一の制限は、呼び出す関数名の一部を指定するのには使えない、という点です。これは、認識される関数名かどうかの判定が、ネストした参照の展開よりも前に行われるためです。例えば、
ifdef do_sort func := sort else func := strip endif
bar := a d b g q c
foo := $($(func) $(bar))
は、‘a d b g q c’ を sort 関数や strip 関数への引数として与えるのではなく、‘foo’ に変数 ‘sort a d b g q c’ または ‘strip a d b g q c’ の値を与えようとしてしまいます。この制限は、変更がよいアイデアだと示されれば、将来取り除かれるかもしれません。
計算で求める変数名は、変数代入の左辺や、define ディレクティブの中でも使えます。例えば、次のとおりです:
dir = foo $(dir)_sources := $(wildcard $(dir)/*.c) define $(dir)_print = lpr $($(dir)_sources) endef
この例は、‘dir’、‘foo_sources’、‘foo_print’ という変数を定義します。
ネストした変数参照は再帰展開変数(変数の2つの種別を参照してください)とはかなり異なるものである点に注意してください。ただし、makefile プログラミングを行う際には、この両者が複雑に組み合わさって一緒に使われることがあります。
変数は、いくつかの異なる方法で値を得ることができます:
make を実行するときに、オーバーライドする値を指定する。変数のオーバーライドを参照してください。
let 関数(関数 letを参照してください)や foreach 関数(関数 foreachを参照してください)で、一時的な値を指定する。
make の変数になる。環境から取り込まれる変数を参照してください。
makefile から変数を設定するには、変数名で始まる行を書き、その後に代入演算子 ‘=’、‘:=’、‘::=’、‘:::=’ のいずれかを続けます。演算子に続くもの、および行頭の空白を除いたものが値になります。例えば、
objects = main.o foo.o bar.o utils.o
は、objects という名前の変数を定義し、値 ‘main.o foo.o bar.o utils.o’ を持たせます。変数名の前後、および ‘=’ の直後にある空白は無視されます。
‘=’ で定義された変数は再帰展開変数です。‘:=’ または ‘::=’ で定義された変数は単純展開変数で、これらの定義に変数参照を含めることができ、定義が行われる前に展開されます。‘:::=’ で定義された変数は即時展開変数です。それぞれの代入演算子については変数の2つの種別で説明しています。
変数名には関数参照や変数参照を含めることができ、実際に使う変数名を求めるために、行が読み込まれるときに展開されます。
変数の値の長さには、コンピュータのメモリ量を除けば制限はありません。読みやすさのために、変数の値を複数の物理行に分割することもできます(長い行を分割するを参照してください)。
ほとんどの変数名は、一度も設定されていなければ、空文字列を値として持つとみなされます。いくつかの変数は空でない組み込みの初期値を持ちますが、通常の方法で設定することもできます(暗黙のルールで使われる変数を参照してください)。いくつかの特別な変数は、ルールごとに自動的に新しい値が設定されます。これらは自動変数と呼ばれます(自動変数を参照してください)。
変数がまだ設定されていない場合にのみ値を設定したいときは、‘=’ の代わりに簡略演算子 ‘?=’ を使えます。次の変数 ‘FOO’ の 2 つの設定は同一です(関数 originを参照してください):
FOO ?= bar
と
ifeq ($(origin FOO), undefined) FOO = bar endif
シェル代入演算子 ‘!=’ を使うと、シェルスクリプトを実行してその出力を変数に設定できます。この演算子はまず右辺を評価し、その結果を実行のためにシェルに渡します。実行結果が改行で終わっている場合、その 1 個の改行は取り除かれます。それ以外の改行はすべて空白に置き換えられます。こうしてできた文字列が、指定された再帰展開変数に格納されます。例えば、
hash != printf '\043' file_list != find . -name '*.c'
実行結果に $ が含まれる可能性があり、その後に続くものを make の変数参照や関数参照として解釈してほしくない場合は、実行の一部として各 $ を $$ に置き換えなければなりません。あるいは、shell 関数呼び出しを使ってプログラムの実行結果を単純展開変数に設定することもできます。関数 shellを参照してください。例えば、
hash := $(shell printf '\043') var := $(shell find . -name "*.c")
shell 関数の場合と同様に、いま起動したシェルスクリプトの終了ステータスは .SHELLSTATUS 変数に格納されます。
すでに定義されている変数の値に、さらにテキストを追加できると便利なことがよくあります。これには、次のように ‘+=’ を含む行を使います:
objects += another.o
これは、変数 objects の値を取り、そこにテキスト ‘another.o’ を追加します(すでに値がある場合は、空白 1 個を前に付けて追加します)。したがって、
objects = main.o foo.o bar.o utils.o objects += another.o
は、objects を ‘main.o foo.o bar.o utils.o another.o’ に設定します。
‘+=’ を使うことは、次と似ています:
objects = main.o foo.o bar.o utils.o objects := $(objects) another.o
しかし、もっと複雑な値を使うときに重要になる点で異なります。
問題の変数がそれ以前に定義されていない場合、‘+=’ は通常の ‘=’ とまったく同じように振る舞います。すなわち、再帰展開変数を定義します。しかし、以前に定義がある場合、‘+=’ が正確に何をするかは、最初にどの種別の変数として定義したかによって決まります。2 つの種別の変数の説明については変数の2つの種別を参照してください。
‘+=’ で変数の値に追加するとき、make は本質的に、その追加テキストを変数の最初の定義に含めていたかのように振る舞います。最初に ‘:=’ または ‘::=’ で定義して単純展開変数にしていた場合、‘+=’ はその単純展開の定義に追加し、‘:=’ がそうするのと同じように、新しいテキストを古い値に追記する前に展開します(‘:=’ や ‘::=’ の完全な説明については変数を設定するを参照してください)。実際、
variable := value variable += more
は、次と完全に同等です:
variable := value variable := $(variable) more
一方、最初に素の ‘=’ または ‘:::=’ を使って再帰展開変数として定義した変数に ‘+=’ を使う場合、make は既存の値が何であれ、そこに展開していないテキストを追加します。つまり、
variable = value variable += more
は、おおよそ次と同等です:
temp = value variable = $(temp) more
もちろん、temp という変数を実際に定義することはありませんが。このことが重要になるのは、変数の古い値に変数参照が含まれている場合です。次のよくある例を見てみましょう:
CFLAGS = $(includes) -O … CFLAGS += -pg # enable profiling
最初の行は、別の変数 includes への参照を伴って CFLAGS 変数を定義しています。(CFLAGS は C コンパイルのルールで使われます。組み込みルールのカタログを参照してください。)定義に ‘=’ を使うことで CFLAGS は再帰展開変数になり、make が CFLAGS の定義を処理するときには ‘$(includes) -O’ が展開されないという意味になります。したがって、その値が効力を持つために includes をまだ定義しておく必要はありません。CFLAGS への参照より前に定義されてさえいればよいのです。もし ‘+=’ を使わずに CFLAGS の値に追加しようとしたら、次のようにするかもしれません:
CFLAGS := $(CFLAGS) -pg # enable profiling
これはかなり近いですが、望みどおりではありません。‘:=’ を使うと CFLAGS を単純展開変数として再定義することになります。これは、変数を設定する前に make がテキスト ‘$(CFLAGS) -pg’ を展開することを意味します。もし includes がまだ定義されていなければ、‘ -O -pg’ が得られ、後から includes を定義しても効果はありません。逆に ‘+=’ を使えば、CFLAGS を展開していない値 ‘$(includes) -O -pg’ に設定します。こうして includes への参照を保持できるので、その変数が後のどこかの時点で定義されれば、‘$(CFLAGS)’ のような参照は依然としてその値を使います。
override ディレクティブ
変数がコマンド引数で設定されている場合(変数のオーバーライドを参照してください)、makefile 内の通常の代入は無視されます。コマンド引数で設定されていても makefile の中で変数を設定したい場合は、override ディレクティブを使えます。これは次のような形の行です:
override variable = value
または
override variable := value
コマンドラインで定義された変数にさらにテキストを追加するには、次のようにします:
override variable += more text
変数にテキストを追加するを参照してください。
override フラグが付いた変数代入は、別の override を除く他のすべての代入よりも高い優先度を持ちます。override が付いていない、この変数へのその後の代入や追加は無視されます。
override ディレクティブは、makefile とコマンド引数の間の争いをエスカレートさせるために考案されたわけではありません。これは、ユーザーがコマンド引数で指定する値を変更したり、それに付け加えたりできるように考案されたものです。
例えば、C コンパイラを実行するときには常に ‘-g’ スイッチを付けたいけれども、その他のスイッチについてはいつもどおりユーザーがコマンド引数で指定できるようにしたい、としましょう。次の override ディレクティブを使えばよいのです:
override CFLAGS += -g
override ディレクティブは define ディレクティブと一緒に使うこともできます。これは予想どおりのやり方で行います:
override define foo = bar endef
複数行にわたる変数を定義するを参照してください。
変数の値を設定するもう一つの方法は、define ディレクティブを使うことです。このディレクティブは、値に改行文字を含めることができる独特の構文を持ち、定型化したコマンド列(定型レシピを定義するを参照してください)を定義したり、eval で使う makefile 構文の断片(関数 evalを参照してください)を定義したりするのに便利です。
define ディレクティブの後ろには、同じ行に、定義する変数名と(任意の)代入演算子が続き、それ以外は何も書きません。変数に与える値は、続く行に現れます。値の終わりは、endef という語だけからなる行で示します。
この構文の違いを除けば、define は他の変数定義とまったく同じように動作します。変数名には関数参照や変数参照を含めることができ、実際に使う変数名を求めるために、ディレクティブが読み込まれるときに展開されます。
endef の直前の最後の改行は値に含まれません。値に末尾の改行を含めたい場合は、空行を入れる必要があります。例えば、改行文字を含む変数を定義するには、空行を 1 行ではなく2 行使わなければなりません:
define newline endef
お好みで変数代入演算子を省略してもかまいません。省略した場合、make はそれを ‘=’ とみなし、再帰展開変数を作成します(変数の2つの種別を参照してください)。‘+=’ 演算子を使う場合、他の追加操作と同様に、値は古い値と新しい値を空白 1 個で区切って、前の値に追加されます。
define ディレクティブはネストできます。make はネストしたディレクティブを追跡し、それらがすべて endef で正しく閉じられていなければエラーを報告します。レシピ接頭文字で始まる行はレシピの一部とみなされるので、そのような行に現れる define や endef という文字列は make のディレクティブとはみなされない点に注意してください。
define two-lines echo foo echo $(bar) endef
レシピの中で使う場合、前の例は次と機能的に同等です:
two-lines = echo foo; echo $(bar)
セミコロンで区切られた 2 つのコマンドは、2 つの別々のシェルコマンドとほぼ同じように振る舞うからです。ただし、2 つの別々の行を使うと、make がシェルを 2 回起動し、各行ごとに独立したサブシェルを実行する点に注意してください。レシピの実行を参照してください。
define で行う変数定義を、コマンドラインの変数定義より優先させたい場合は、override ディレクティブを define と一緒に使えます:
override define two-lines = foo $(bar) endef
override ディレクティブを参照してください。
変数をクリアしたい場合は、その値を空に設定すれば通常はそれで十分です。そのような変数を展開すると、設定されていたかどうかにかかわらず同じ結果(空文字列)が得られます。しかし、flavor 関数(関数 flavorを参照してください)や origin 関数(関数 originを参照してください)を使っている場合は、一度も設定されたことのない変数と、空の値を持つ変数とでは違いがあります。そのような場合には、undefine ディレクティブを使って、変数が一度も設定されなかったかのように見せたいことがあるでしょう。例えば、
foo := foo bar = bar undefine foo undefine bar $(info $(origin foo)) $(info $(flavor bar))
この例は、両方の変数について「undefined」と表示します。
コマンドラインの変数定義を取り消したい場合は、変数定義のときと同じように、override ディレクティブを undefine と一緒に使えます:
override undefine CFLAGS
make の変数は、make が実行される環境から取り込むことができます。make が起動時に見るすべての環境変数は、同じ名前と値を持つ make の変数に変換されます。ただし、makefile 内の明示的な代入や、コマンド引数での代入は、環境を上書きします。(‘-e’ フラグが指定された場合は、環境の値が makefile 内の代入を上書きします。オプションの一覧を参照してください。ただし、これは推奨される使い方ではありません。)
したがって、環境で変数 CFLAGS を設定しておけば、ほとんどの makefile で、すべての C コンパイルにあなたの好みのコンパイラスイッチを使わせることができます。これは、標準的あるいは慣例的な意味を持つ変数については安全です。どの makefile もそれらを別の目的に使わないと分かっているからです。(ただし、これは完全に当てにできるわけではありません。一部の makefile は CFLAGS を明示的に設定しており、その場合は環境の値の影響を受けません。)
make がレシピを実行するとき、makefile 内で定義された一部の変数が、make が呼び出す各コマンドの環境に置かれます。デフォルトでは、make の環境から来た変数か、コマンドラインで設定された変数だけが、コマンドの環境に置かれます。その他の変数を渡すには、export ディレクティブを使えます。詳細はすべてサブmakeへ変数を伝えるを参照してください。
環境から取り込んだ変数のそれ以外の使い方は推奨されません。makefile が、自分の制御の及ばないところで設定された環境変数に動作を依存するのは賢明ではありません。それでは、同じ makefile を使っても、ユーザーごとに異なる結果が得られてしまうからです。これは、ほとんどの makefile の本来の目的に反します。
このような問題は、変数 SHELL でとりわけ起こりやすいでしょう。SHELL は、ユーザーが選んだ対話シェルを指定するために、通常は環境に存在します。この選択が make に影響を及ぼすのは非常に望ましくありません。そのため、make は SHELL 環境変数を特別なやり方で扱います。シェルを選ぶを参照してください。
make の変数値は、通常はグローバルです。すなわち、(もちろん再設定されないかぎり)どこで評価されても同じです。例外となるのは、let 関数(関数 letを参照してください)や foreach 関数(関数 foreachを参照してください)で定義された変数、そして自動変数(自動変数を参照してください)です。
もう一つの例外がターゲット固有の変数値(target-specific variable value)です。この機能を使うと、make が現在ビルドしているターゲットに応じて、同じ変数に異なる値を定義できます。自動変数と同様に、これらの値はターゲットのレシピの文脈(および他のターゲット固有の代入)の中でのみ利用できます。
ターゲット固有の変数値は、次のように設定します:
target … : variable-assignment
ターゲット固有の変数代入には、特別なキーワード export、unexport、override、private のいずれか、またはすべてを接頭辞として付けられます。これらは、この変数のこのインスタンスにのみ、それぞれの通常の振る舞いを適用します。
複数の target を指定すると、ターゲットリストの各メンバーに対して個別にターゲット固有の変数値が作られます。
variable-assignment には、有効な代入形式であれば何でも使えます。再帰(‘=’)、単純(‘:=’ または ‘::=’)、即時(‘::=’)、追加(‘+=’)、条件付き(‘?=’)です。variable-assignment の中に現れるすべての変数は、ターゲットの文脈の中で評価されます。したがって、それ以前に定義されたターゲット固有の変数値があれば、それが効力を持ちます。この変数は、実際にはどんな「グローバル」な値とも別物である点に注意してください。2 つの変数は同じ種別(再帰か単純か)である必要はありません。
ターゲット固有の変数は、他の makefile 変数と同じ優先度を持ちます。コマンドラインで提供された変数(および ‘-e’ オプションが有効な場合は環境の変数)が優先されます。override ディレクティブを指定すると、ターゲット固有の変数値の方を優先させることができます。
ターゲット固有の変数には、もう一つ特別な機能があります。ターゲット固有の変数を定義すると、その変数値はこのターゲットのすべての前提条件、さらにそれらの前提条件、というように再帰的に効力を持ちます(ただし、それらの前提条件が自分自身のターゲット固有の変数値で上書きしている場合は別です)。例えば、次のような文、
prog : CFLAGS = -g prog : prog.o foo.o bar.o
は、prog のレシピで CFLAGS を ‘-g’ に設定しますが、それだけでなく、prog.o、foo.o、bar.o を作るレシピ、およびそれらの前提条件を作るレシピでも、CFLAGS を ‘-g’ に設定します。
ただし、ある前提条件は make の 1 回の起動につき、多くても 1 度しかビルドされない点に注意してください。同じファイルが複数のターゲットの前提条件になっていて、それらのターゲットがそれぞれ同じターゲット固有の変数に異なる値を持っている場合、最初にビルドされたターゲットがその前提条件をビルドさせ、前提条件は最初のターゲットからターゲット固有の値を継承します。他のターゲットからのターゲット固有の値は無視されます。
ターゲット固有の変数値(ターゲット固有の変数値を参照してください)に加えて、GNU make はパターン固有の変数値もサポートしています。この形式では、変数は指定されたパターンにマッチする任意のターゲットに対して定義されます。
パターン固有の変数値は、次のように設定します:
pattern … : variable-assignment
ここで pattern は %-パターンです。ターゲット固有の変数値と同様に、複数の pattern を指定すると、各パターンに対して個別にパターン固有の変数値が作られます。variable-assignment には、有効な代入形式であれば何でも使えます。コマンドラインでの変数設定は、override が指定されないかぎり優先されます。
例えば、
%.o : CFLAGS = -O
は、パターン %.o にマッチするすべてのターゲットに対して、CFLAGS に値 ‘-O’ を割り当てます。
あるターゲットが複数のパターンにマッチする場合、マッチするパターン固有の変数のうち、語幹(stem)が長いものから先に解釈されます。これにより、より汎用的なものよりも、より具体的な変数が優先されます。例えば、
%.o: %.c
$(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@
lib/%.o: CFLAGS := -fPIC -g
%.o: CFLAGS := -g
all: foo.o lib/bar.o
この例では、2 番目の定義も lib/bar.o というターゲットに当てはまりますが、CFLAGS 変数の最初の定義の方が lib/bar.o の更新に使われます。語幹の長さが同じになるパターン固有の変数は、makefile に定義された順序で考慮されます。
パターン固有の変数は、そのターゲットに対して明示的に定義されたターゲット固有の変数の後、かつ親ターゲットに対して定義されたターゲット固有の変数の前に探索されます。
これまでの節で述べたように、make の変数は前提条件に継承されます。この機能を使うと、ある前提条件をどのターゲットが再ビルドさせたかに応じて、その前提条件の振る舞いを変えられます。例えば、debug ターゲットにターゲット固有の変数を設定しておけば、‘make debug’ を実行したときにその変数が debug のすべての前提条件に継承されます。一方、(例えば)単に ‘make all’ を実行しただけでは、その代入は行われません。
しかし、変数を継承させたくないこともあるでしょう。そうした状況のために、make は private 修飾子を提供しています。この修飾子はどんな変数代入にも使えますが、最も意味があるのはターゲット固有変数とパターン固有変数の場合です。private が付いた変数は、そのローカルなターゲットからは見えますが、そのターゲットの前提条件には継承されません。private が付いたグローバル変数は、グローバルスコープでは見えますが、どのターゲットにも継承されないので、どのレシピからも見えません。
例として、次の makefile を考えてみましょう:
EXTRA_CFLAGS = prog: private EXTRA_CFLAGS = -L/usr/local/lib prog: a.o b.o
private 修飾子のおかげで、a.o と b.o は prog ターゲットからの EXTRA_CFLAGS 変数代入を継承しません。
GNU make は、特別な性質を持つ変数をいくつかサポートしています。
MAKEFILE_LIST
make が解析した各 makefile の名前を、解析された順序で保持します。名前は、make がその makefile の解析を始める直前に追記されます。したがって、makefile が最初に行うことがこの変数の最後の語を調べることであれば、それは現在の makefile の名前になります。ただし、現在の makefile が include を使った後は、最後の語はちょうど読み込まれた makefile になります。
Makefile という名前の makefile が次の内容を持つとします:
name1 := $(lastword $(MAKEFILE_LIST))
include inc.mk
name2 := $(lastword $(MAKEFILE_LIST))
all:
@echo name1 = $(name1)
@echo name2 = $(name2)
すると、次の出力が得られると予想できます:
name1 = Makefile name2 = inc.mk
.DEFAULT_GOAL
コマンドラインでターゲットが何も指定されなかった場合に使われるデフォルトゴールを設定します(ゴールを指定する引数を参照してください)。.DEFAULT_GOAL 変数を使うと、現在のデフォルトゴールを調べたり、その値をクリアしてデフォルトゴール選択アルゴリズムを再起動したり、デフォルトゴールを明示的に設定したりできます。次の例は、これらの場合を示しています:
# Query the default goal. ifeq ($(.DEFAULT_GOAL),) $(warning no default goal is set) endif .PHONY: foo foo: ; @echo $@ $(warning default goal is $(.DEFAULT_GOAL)) # Reset the default goal. .DEFAULT_GOAL := .PHONY: bar bar: ; @echo $@ $(warning default goal is $(.DEFAULT_GOAL)) # Set our own. .DEFAULT_GOAL := foo
この makefile は次のように出力します:
no default goal is set default goal is foo default goal is bar foo
.DEFAULT_GOAL に 2 つ以上のターゲット名を割り当てるのは無効で、エラーになる点に注意してください。
MAKE_RESTARTS
この変数は、この make のインスタンスが再起動した場合にのみ設定されます(Makefileはどう再生成されるかを参照してください)。このインスタンスが再起動した回数が入ります。これは再帰(MAKELEVEL 変数で数えられるもの)とは別物である点に注意してください。この変数を設定したり、変更したり、エクスポートしたりしてはいけません。
MAKE_TERMOUTMAKE_TERMERR
make は起動時に、標準出力と標準エラー出力がそれぞれ端末に出力を表示するかどうかを確認します。もしそうであれば、それぞれ MAKE_TERMOUT と MAKE_TERMERR に、端末デバイスの名前(判定できない場合は true)を設定します。設定された場合、これらの変数はエクスポート対象としてマークされます。これらの変数は make によって変更されることはなく、すでに設定されていれば変更されません。
これらの値は、(特に出力同期(並列実行時の出力を参照してください)と組み合わせて)make 自身が端末に書き込んでいるかどうかを判定するのに使えます。例えば、レシピのコマンドに色付きの出力を強制するかどうかを決めるためにテストできます。
サブmakeを呼び出してその標準出力や標準エラー出力をリダイレクトする場合、makefile がこれらの変数に依存しているなら、これらの変数もリセットまたはアンエクスポートするのはあなたの責任です。
.RECIPEPREFIXこの変数の値の最初の文字が、make がレシピ行の始まりを示すとみなす文字として使われます。変数が空(デフォルトではそうです)の場合、その文字は標準のタブ文字になります。例えば、次は有効な makefile です:
.RECIPEPREFIX = > all: > @echo Hello, world
.RECIPEPREFIX の値は何度でも変更できます。いったん設定されると、変更されるまで、それ以降に解析されるすべてのルールに対して効力を持ち続けます。
.VARIABLESここまでに定義されたすべてのグローバル変数の名前のリストに展開されます。これには、空の値を持つ変数や、組み込み変数(暗黙のルールで使われる変数を参照してください)も含まれますが、ターゲット固有の文脈でのみ定義された変数は含まれません。この変数に何か値を割り当てても無視される点に注意してください。常にこの特別な値を返します。
.FEATURES
このバージョンの make がサポートする特別な機能のリストに展開されます。とりうる値には、以下のものが含まれます(ただしこれらに限りません):
特別なファイル名構文を使った ar(アーカイブ)ファイルをサポートします。makeでアーカイブファイルを更新するを参照してください。
-L(--check-symlink-times)フラグをサポートします。オプションの一覧を参照してください。
ネストしない「else if」条件分岐をサポートします。条件分岐の構文を参照してください。
.EXTRA_PREREQS 特別なターゲットをサポートします。
明示的なルールのグループ化ターゲット構文をサポートします。1つのルールに複数のターゲットを参照してください。
組み込みの拡張言語として GNU Guile が利用できます。GNU Guileの統合を参照してください。
「ジョブサーバ」による強化された並列ビルドをサポートします。並列実行を参照してください。
名前付きパイプを使った「ジョブサーバ」による強化された並列ビルドをサポートします。GNU makeを組み込むを参照してください。
カスタム拡張を作るための動的にロード可能なオブジェクトをサポートします。動的オブジェクトのロードを参照してください。
.NOTINTERMEDIATE 特別なターゲットをサポートします。GNU makeを組み込むを参照してください。
.ONESHELL 特別なターゲットをサポートします。1つのシェルを使うを参照してください。
順序のみの前提条件をサポートします。前提条件の種類を参照してください。
--output-sync コマンドラインオプションをサポートします。オプションの一覧を参照してください。
前提条件リストの二次展開をサポートします。
make 変数を shell 関数へエクスポートすることをサポートします。
当てはまる複数の選択肢のうち、どのパターンを使うかを選ぶのに「最短の語幹」方式を使います。パターンはどうマッチするかを参照してください。
ターゲット固有およびパターン固有の変数代入をサポートします。ターゲット固有の変数値を参照してください。
undefine ディレクティブをサポートします。変数の定義を取り消すを参照してください。
.INCLUDE_DIRS
make が、インクルードされる makefile を探索するディレクトリのリストに展開されます(他のMakefileをインクルードするを参照してください)。この変数の値を変更しても、探索されるディレクトリのリストは変わらない点に注意してください。
.EXTRA_PREREQSこの変数の各語は、それが設定されているターゲットに追加される新しい前提条件です。これらの前提条件が通常の前提条件と異なるのは、自動変数(自動変数を参照してください)のいずれにも現れないという点です。これにより、レシピに影響を与えない前提条件を定義できます。
プログラムをリンクするルールを考えてみましょう:
myprog: myprog.o file1.o file2.o
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LDLIBS)
ここで、コンパイラの更新があったらプログラムを再リンクするよう、この makefile を改良したいとしましょう。コンパイラを前提条件として追加できますが、それがリンクコマンドの引数として渡されないようにしなければなりません。次のようなものが必要になります:
myprog: myprog.o file1.o file2.o $(CC)
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ \
$(filter-out $(CC),$^) $(LDLIBS)
次に、追加の前提条件が複数ある場合を考えてみましょう。それらをすべて除外しなければなりません。.EXTRA_PREREQS とターゲット固有変数を使えば、よりシンプルな解決策が得られます:
myprog: myprog.o file1.o file2.o
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LDLIBS)
myprog: .EXTRA_PREREQS = $(CC)
この機能は、簡単には変更できない makefile に前提条件を追加したいときにも役立ちます。extra.mk のような新しいファイルを作り:
myprog: .EXTRA_PREREQS = $(CC)
それから make -f extra.mk -f Makefile を呼び出せばよいのです。
.EXTRA_PREREQS をグローバルに設定すると、それらの前提条件がすべてのターゲット(ターゲット固有の値で自分自身で上書きしなかったもの)に追加されます。make は賢いので、.EXTRA_PREREQS に挙げられた前提条件を、その前提条件自身の前提条件としては追加しない点に注意してください。