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

8 テキストを変換する関数

 関数(function)を使うと、makefile の中でテキスト処理を行えます。これによって、処理対象とするファイルを計算で求めたり、レシピで使うコマンドを組み立てたりできます。関数は関数呼び出しという形で利用します。関数の名前と、その関数に処理させるテキスト(これを引数と呼びます)を与えると、関数が処理を行い、その結果が呼び出し位置に埋め込まれます。これは、変数が展開されて置き換わるのとちょうど同じ仕組みです。

8.1 関数呼び出しの構文

 関数呼び出しは、変数参照とよく似た形をしています。変数参照を書ける場所であればどこにでも書けますし、展開のルールも変数参照と同じです。関数呼び出しは、次のような形になります:

$(function arguments)

 あるいは、次のような形でも書けます:

${function arguments}

 ここで function は関数名で、make に組み込まれている数少ない関数名のうちのいずれかです。なお、組み込み関数 call を使えば、実質的に自分専用の関数を作ることもできます。

 arguments はその関数の引数です。引数は関数名から1個以上のスペースまたはタブで区切られ、引数が複数ある場合は引数同士をカンマで区切ります。こうした区切りの空白やカンマは、引数の値の一部にはなりません。関数呼び出しを囲む区切り文字は丸括弧でも波括弧でもかまいませんが、引数の中にその種類の括弧を書く場合は対になっていなければなりません。一方、もう片方の種類の区切り文字であれば、対になっていなくても単独で書けます。引数の中にさらに別の関数呼び出しや変数参照が含まれる場合は、すべての参照に同じ種類の区切り文字を使うのが賢明です。つまり「$(subst a,b,$(x))」のように書くべきで、「$(subst a,b,${x})」のように混在させるべきではありません。そのほうが見て分かりやすいですし、参照の終わりを探すときに照合されるのは1種類の区切り文字だけだからです。

 各引数は、関数が呼び出される前にそれぞれ展開されます(ただし以下で特に注記がある場合を除きます)。展開は引数が並んでいる順序で行われます。

特殊文字

 関数の引数として make にとって特別な意味を持つ文字を使いたいときは、それらを隠す必要が出てくることがあります。GNU make は、バックスラッシュやその他のエスケープシーケンスによる文字のエスケープをサポートしていません。しかし、引数は展開される前に分割されるため、特殊文字を変数に入れておくことで隠すことができます。

 隠す必要があるかもしれない文字には、次のようなものがあります:

 例えば、値がカンマ1文字・スペース1文字だけになる変数 commaspace を定義しておき、そうした文字が必要な箇所でこれらの変数を埋め込む、という方法があります。次のように書きます:

comma:= ,
empty:=
space:= $(empty) $(empty)
foo:= a b c
bar:= $(subst $(space),$(comma),$(foo))
# barの値はこれで 'a,b,c' になります。

 ここでは subst 関数が、foo の値の中の各スペースをカンマに置き換え、その結果を埋め込んでいます。

8.2 文字列の置換・解析のための関数

 ここでは、文字列を操作するいくつかの関数を紹介します:

$(subst from,to,text)

 テキスト text に対して文字列の置換を行います。すなわち、from が現れるたびにそれを to に置き換えます。その結果が関数呼び出しの位置に埋め込まれます。例えば、

$(subst ee,EE,feet on the street)

は「fEEt on the strEEt」という値を生みます。

$(patsubst pattern,replacement,text)

 text の中から、空白で区切られた単語のうち pattern に一致するものを探し、それらを replacement に置き換えます。ここで pattern には「%」を含めることができ、これはワイルドカードとして働いて、1単語の中の任意の個数の任意の文字に一致します。replacement にも「%」が含まれていれば、その「%」は、pattern の「%」に一致した部分のテキストに置き換えられます。パターンに一致しなかった単語は、変更されずにそのまま出力に残ります。このように特別扱いされるのは patternreplacement の最初の「%」だけで、2個目以降の「%」はそのまま扱われます。

 patsubst 関数の呼び出しで使う「%」文字は、直前にバックスラッシュ(「\」)を置くことでクオートできます。「%」をクオートする働きを持つバックスラッシュ自体は、さらにバックスラッシュを重ねることでクオートできます。「%」文字や他のバックスラッシュをクオートするバックスラッシュは、パターンがファイル名と照合されたり語幹(stem)が差し込まれたりする前に、パターンから取り除かれます。一方、「%」文字をクオートする恐れのないバックスラッシュは、そのまま手つかずで残ります。例えば、パターン the\%weird\\%pattern\\ では、実効的な「%」文字の前に「the%weird\」があり、その後ろに「pattern\\」が続きます。末尾の2個のバックスラッシュは、どの「%」文字にも影響を与えられないため、そのまま残されます。

 単語と単語の間の空白は、1個のスペース文字にまとめられます。また、先頭と末尾の空白は捨てられます。

 例えば、

$(patsubst %.c,%.o,x.c.c bar.c)

は「x.c.o bar.o」という値を生みます。

 置換参照(置換参照の項を参照してください)を使うと、patsubst 関数と同じ効果をもっと簡単に得られます:

$(var:pattern=replacement)

は、

$(patsubst pattern,replacement,$(var))

と等価です。2番目の短縮記法は、patsubst のもっとも一般的な使い方の一つ——ファイル名末尾のサフィックスを置き換える操作——を簡潔にします。

$(var:suffix=replacement)

は、

$(patsubst %suffix,%replacement,$(var))

と等価です。例えば、オブジェクトファイルの一覧があったとします:

objects = foo.o bar.o baz.o

 これに対応するソースファイルの一覧を得たいなら、一般形の、

$(patsubst %.o,%.c,$(objects))

を使う代わりに、単に次のように書けます:

$(objects:.o=.c)
$(strip string)

 string の先頭と末尾の空白を取り除き、内部にある1個以上連続した空白文字のまとまりをそれぞれ1個のスペースに置き換えます。したがって「$(strip a b c )」の結果は「a b c」になります。

 strip 関数は、条件分岐と組み合わせて使うととても便利です。ifeqifneq を使って何かを空文字列「」と比較するとき、空白だけからなる文字列を空文字列と一致させたい、という場面がよくあります(Makefileの条件分岐を参照してください)。

 したがって、次の例は思いどおりの結果にならないかもしれません:

.PHONY: all
ifneq   "$(needs_made)" ""
all: $(needs_made)
else
all:;@echo 'Nothing to make!'
endif

 ifneq ディレクティブの中の変数参照「$(needs_made)」を、関数呼び出し「$(strip $(needs_made))」に置き換えれば、もっと堅牢になります。

$(findstring find,in)

 in の中に find が現れるかどうかを探します。現れれば、値は find になります。現れなければ、値は空になります。この関数は、ある文字列の中に特定の部分文字列が含まれているかどうかを条件分岐の中で調べるのに使えます。したがって、次の2つの例、

$(findstring a,a b c)
$(findstring a,b c)

は、それぞれ「a」と「」(空文字列)という値を生みます。findstring の実践的な応用については、フラグを調べる条件分岐の項を参照してください。

$(filter pattern…,text)

 text の中で空白で区切られた単語のうち、pattern として与えた単語のいずれかに一致するものをすべて返し、一致しない単語は取り除きます。パターンは、先ほどの patsubst 関数で使ったのと同じく「%」を用いて書きます。

 filter 関数は、変数の中にある異なる種類の文字列(例えばファイル名)を選り分けるのに使えます。例えば:

sources := foo.c bar.c baz.s ugh.h
foo: $(sources)
        cc $(filter %.c %.s,$(sources)) -o foo

 この例は、foofoo.cbar.cbaz.sugh.h に依存しているが、コンパイラへのコマンドに渡すべきは foo.cbar.cbaz.s だけである、ということを表しています。

$(filter-out pattern…,text)

 text の中で空白で区切られた単語のうち、pattern として与えた単語のどれにも一致しないものをすべて返し、1つ以上に一致する単語は取り除きます。これは filter 関数のちょうど正反対の動作です。

 例えば、次のように与えられているとき:

objects=main1.o foo.o main2.o bar.o
mains=main1.o main2.o

次の式は、「mains」に含まれていないオブジェクトファイルだけからなる一覧を生成します:

$(filter-out $(mains),$(objects))
$(sort list)

 list の単語を辞書順にソートし、重複する単語を取り除きます。出力は、単語を1個のスペースで区切った一覧になります。したがって、

$(sort foo bar lose)

は「bar foo lose」という値を返します。

 ちなみに、sort は重複する単語を取り除くので、ソート順がどうでもよい場合でも、重複除去だけの目的で使うことができます。

$(word n,text)

 textn 番目の単語を返します。n として有効な値は1から始まります。ntext 内の単語数より大きい場合、値は空になります。例えば、

$(word 2, foo bar baz)

は「bar」を返します。

$(wordlist s,e,text)

 text の単語のうち、s 番目の単語から e 番目の単語まで(両端を含む)の一覧を返します。s として有効な値は1から始まり、e は0から始められます。stext 内の単語数より大きい場合、値は空になります。etext 内の単語数より大きい場合は、text の末尾までの単語が返されます。se より大きい場合は、何も返されません。例えば、

$(wordlist 2, 3, foo bar baz)

は「bar baz」を返します。

$(words text)

 text 内の単語数を返します。したがって、text の最後の単語は $(word $(words text),text) で得られます。

$(firstword names…)

 引数 names は、空白で区切られた名前の並びとみなされます。値は、その並びの最初の名前です。残りの名前は無視されます。

 例えば、

$(firstword foo bar)

は「foo」という結果を生みます。$(firstword text)$(word 1,text) と同じですが、簡潔さのために firstword 関数も残されています。

$(lastword names…)

 引数 names は、空白で区切られた名前の並びとみなされます。値は、その並びの最後の名前です。

 例えば、

$(lastword foo bar)

は「bar」という結果を生みます。$(lastword text)$(word $(words text),text) と同じですが、簡潔さとより良い性能のために lastword 関数が追加されました。

 ここで、substpatsubst の現実的な使用例を挙げます。ある makefile が、前提条件のファイルを make に探させるディレクトリの一覧を指定するために VPATH 変数を使っているとします(すべての前提条件に対する VPATH 探索パスを参照してください)。この例では、同じディレクトリの一覧の中をヘッダファイルについても探すよう、C コンパイラにどう指示するかを示します。

 VPATH の値は、コロンで区切られたディレクトリの一覧で、例えば「src:../headers」のようになっています。まず subst 関数を使って、コロンをスペースに変えます:

$(subst :, ,$(VPATH))

 これは「src ../headers」を生みます。次に patsubst を使って、各ディレクトリ名を「-I」フラグに変えます。こうしてできたものは、C コンパイラへ自動的に渡される変数 CFLAGS の値に、次のように追加できます:

override CFLAGS += $(patsubst %,-I%,$(subst :, ,$(VPATH)))

 その効果は、CFLAGS にもともと与えられていた値に、テキスト「-Isrc -I../headers」を追加することです。ここで override ディレクティブを使っているのは、CFLAGS の以前の値がコマンドライン引数で指定されていた場合でも、新しい値が確実に代入されるようにするためです(override ディレクティブを参照してください)。

8.3 ファイル名を扱う関数

 組み込みの展開関数のうちいくつかは、特にファイル名やファイル名の一覧を分解する処理に関係しています。

 以下の各関数は、ファイル名に対して特定の変換を行います。関数の引数は、空白で区切られたファイル名の並びとみなされます(先頭と末尾の空白は無視されます)。並びの中の各ファイル名が同じやり方で変換され、その結果が1個のスペースで連結されます。

$(dir names…)

 names の中の各ファイル名から、ディレクトリ部分を取り出します。ファイル名のディレクトリ部分とは、その中の最後のスラッシュまで(そのスラッシュ自体を含む)のすべてです。ファイル名にスラッシュが含まれていない場合、ディレクトリ部分は文字列「./」になります。例えば、

$(dir src/foo.c hacks)

は「src/ ./」という結果を生みます。

$(notdir names…)

 names の中の各ファイル名から、ディレクトリ部分を除いた残りを取り出します。ファイル名にスラッシュが含まれていない場合は、そのまま変更されずに残ります。スラッシュが含まれている場合は、最後のスラッシュまでがすべて取り除かれます。

 スラッシュで終わるファイル名は、空文字列になります。これは残念な点で、結果に含まれる空白区切りのファイル名の個数が、引数の個数と必ずしも一致しなくなることを意味します。しかし、他に妥当な選択肢が見当たりませんでした。

 例えば、

$(notdir src/foo.c hacks)

は「foo.c hacks」という結果を生みます。

$(suffix names…)

 names の中の各ファイル名から、サフィックスを取り出します。ファイル名にピリオドが含まれている場合、サフィックスは最後のピリオドから始まるすべてです。含まれていない場合、サフィックスは空文字列になります。このため、names が空でなくても結果が空になることがよくあり、また names に複数のファイル名が含まれていても、結果に含まれるファイル名の個数が少なくなることがあります。

 例えば、

$(suffix src/foo.c src-1.0/bar.c hacks)

は「.c .c」という結果を生みます。

$(basename names…)

 names の中の各ファイル名から、サフィックスを除いた残りを取り出します。ファイル名にピリオドが含まれている場合、basename は最後のピリオドの手前まで(そのピリオド自体は含まない)のすべてです。ディレクトリ部分にあるピリオドは無視されます。ピリオドが1つもない場合、basename はファイル名全体になります。例えば、

$(basename src/foo.c src-1.0/bar hacks)

は「src/foo src-1.0/bar hacks」という結果を生みます。

$(addsuffix suffix,names…)

 引数 names は、空白で区切られた名前の並びとみなされます。suffix は1つのまとまりとして扱われます。suffix の値が個々の名前の末尾に追加され、こうしてできた長くなった名前同士が1個のスペースで連結されます。例えば、

$(addsuffix .c,foo bar)

は「foo.c bar.c」という結果を生みます。

$(addprefix prefix,names…)

 引数 names は、空白で区切られた名前の並びとみなされます。prefix は1つのまとまりとして扱われます。prefix の値が個々の名前の先頭に付け加えられ、こうしてできた長くなった名前同士が1個のスペースで連結されます。例えば、

$(addprefix src/,foo bar)

は「src/foo src/bar」という結果を生みます。

$(join list1,list2)

 2つの引数を単語ごとに連結します。すなわち、最初の2つの単語(各引数から1つずつ)を連結したものが結果の最初の単語になり、2番目の2つの単語が結果の2番目の単語になる、というように続きます。つまり、結果の n 番目の単語は、各引数の n 番目の単語から作られます。一方の引数のほうが単語数が多い場合、余った単語はそのまま変更されずに結果へコピーされます。

 例えば、「$(join a b,.c .o)」は「a.c b.o」を生みます。

 一覧の単語間の空白は保たれず、1個のスペースに置き換えられます。

 この関数は、dir 関数と notdir 関数の結果を結合して、それらの関数に与えた元のファイル一覧を復元するのに使えます。

$(wildcard pattern)

 引数 pattern はファイル名のパターンで、典型的には(シェルのファイル名パターンと同じように)ワイルドカード文字を含みます。wildcard の結果は、そのパターンに一致する既存のファイルの名前を、スペースで区切って並べた一覧です。ファイル名でワイルドカード文字を使うの項を参照してください。

$(realpath names…)

 names の中の各ファイル名について、正規化された絶対名を返します。正規化された名前には、... という構成要素も、重複したパス区切り文字(/)も、シンボリックリンクも含まれません。失敗した場合は空文字列が返されます。失敗する可能性のある原因の一覧については、realpath(3) のドキュメントを参照してください。

$(abspath names…)

 names の中の各ファイル名について、... という構成要素も、重複したパス区切り文字(/)も含まない絶対名を返します。realpath 関数とは対照的に、abspath はシンボリックリンクを解決しませんし、ファイル名が既存のファイルやディレクトリを指している必要もない、という点に注意してください。存在するかどうかを調べたいときは、wildcard 関数を使ってください。

8.4 条件分岐のための関数

 条件付きの展開を行う関数が4つあります。これらの関数の重要な特徴は、すべての引数が最初から展開されるわけではない、という点です。展開が必要な引数だけが展開されます。

$(if condition,then-part[,else-part])

 if 関数は、(ifeq のような GNU make の makefile 条件分岐(条件分岐の構文を参照してください)とは対照的に)関数の形での条件付き展開をサポートします。

 最初の引数 condition は、まず先頭と末尾の空白がすべて取り除かれ、それから展開されます。展開結果が空でない文字列になれば、条件は真とみなされます。空文字列に展開されれば、条件は偽とみなされます。

 条件が真であれば、2番目の引数 then-part が評価され、それが if 関数全体の評価結果として使われます。

 条件が偽であれば、3番目の引数 else-part が評価され、それが if 関数の結果になります。3番目の引数がない場合、if 関数は何も生みません(空文字列になります)。

 then-partelse-part のどちらか一方だけが評価され、両方が評価されることは決してない、という点に注意してください。したがって、どちらにも副作用(shell 関数の呼び出しなど)を含めることができます。

$(or condition1[,condition2[,condition3…]])

 or 関数は「短絡(short-circuiting)」する OR 演算を提供します。各引数が順に展開されます。ある引数が空でない文字列に展開された時点で処理が止まり、その展開結果が返り値になります。すべての引数を展開し終えても、そのすべてが偽(空)であった場合は、展開結果は空文字列になります。

$(and condition1[,condition2[,condition3…]])

 and 関数は「短絡(short-circuiting)」する AND 演算を提供します。各引数が順に展開されます。ある引数が空文字列に展開された時点で処理が止まり、展開結果は空文字列になります。すべての引数が空でない文字列に展開された場合は、展開結果は最後の引数を展開したものになります。

$(intcmp lhs,rhs[,lt-part[,eq-part[,gt-part]]])

 intcmp 関数は、整数同士の数値比較をサポートします。この関数には、GNU make の makefile 条件分岐の中に対応するものがありません。

 左辺 lhs と右辺 rhs が展開され、10進の整数として解析されます。残りの引数の展開は、数値としての左辺が数値としての右辺とどう比較されるかによって制御されます。

 それ以上の引数がなければ、左辺と右辺が等しくない場合は空に展開され、等しい場合はそれらの数値に展開されます。

 そうでなく、左辺が右辺より厳密に小さい場合、intcmp 関数は3番目の引数 lt-part を展開したものに評価されます。両辺が等しい場合、intcmp 関数は4番目の引数 eq-part を展開したものに評価されます。左辺が右辺より厳密に大きい場合、intcmp 関数は5番目の引数 gt-part を展開したものに評価されます。

 gt-part が省略されている場合は、eq-part がその既定値になります。eq-part が省略されている場合は、空文字列がその既定値になります。したがって、「$(intcmp 9,7,hello)」も「$(intcmp 9,7,hello,world,)」も空文字列に評価されますが、「$(intcmp 9,7,hello,world)」(world の後ろにカンマがないことに注意してください)は「world」に評価されます。

8.5 let 関数

 let 関数は、変数のスコープを限定する手段を提供します。let 式の中で名前を付けた変数への代入は、その let 式が提供するテキストの中でだけ有効であり、この代入は外側のスコープにある同名の変数には影響を与えません。

 さらに let 関数では、未割り当ての値をすべて最後の名前の変数に割り当てることで、リストの「アンパック(取り出し)」を行えます。

 let 関数の構文は次のとおりです:

$(let var [var ...],[list],text)

 最初の2つの引数 varlist は、他の何よりも先に展開されます。ここで、最後の引数 text は同時には展開されないという点に注意してください。次に、展開された list の値の各単語が、変数名 var のそれぞれに順番に束縛され、最後の変数名には展開後の list の残り全体が束縛されます。言い換えれば、list の最初の単語が最初の変数 var に、2番目の単語が2番目の変数 var に、というように束縛されていきます。

 var の変数名の数が list の単語数より多い場合、余った var の変数名には空文字列が設定されます。var の数が list の単語数より少ない場合は、最後の varlist の残りの単語すべてが設定されます。

 var の各変数は、let の実行中、単純展開変数として代入されます。変数の2つの種別を参照してください。

 こうしてすべての変数が束縛された後、text が展開され、それが let 関数の結果になります。

 例えば、次のマクロは、第1引数として与えられたリストの単語の順序を逆にします:

reverse = $(let first rest,$1,\
            $(if $(rest),$(call reverse,$(rest)) )$(first))

all: ; @echo $(call reverse,d c b a)

 これは a b c d と表示します。最初に呼び出されると、let$1d c b a に展開します。次に firstd を割り当て、restc b a を割り当てます。それから if 文を展開しますが、ここで $(rest) は空ではないので、いまや c b a となった rest の値で reverse 関数を再帰的に呼び出します。再帰的に呼ばれた letfirstc を、restb a を割り当てます。この再帰は、let がただ1つの値 a だけで呼ばれるまで続きます。このとき firstarest は空なので、もう再帰せず、単に $(first)a に展開して戻り、そこに b が加わる、というように続いていきます。

 reverse の呼び出しが完了した後は、firstrest の変数はもう設定されていません。これらの名前の変数が事前に存在していたとしても、reverse マクロの展開によって影響を受けることはありません。

8.6 foreach 関数

 foreach 関数は let 関数に似ていますが、他の関数とは大きく異なります。これは1つのテキストを繰り返し使い、その都度そのテキストに異なる置換を施します。foreach 関数は、シェル shfor コマンドや、C シェル cshforeach コマンドによく似ています。

 foreach 関数の構文は次のとおりです:

$(foreach var,list,text)

 最初の2つの引数 varlist は、他の何よりも先に展開されます。ここで、最後の引数 text は同時には展開されないという点に注意してください。それから、展開された list の値の各単語について、展開された var の値が示す名前の変数にその単語が設定され、text が展開されます。text はおそらくその変数への参照を含んでいるでしょうから、展開結果は毎回異なるものになります。

 その結果、textlist 内の空白区切りの単語の数だけ展開されます。text を複数回展開したものが、間にスペースを挟んで連結され、foreach の結果になります。

 次の簡単な例は、リスト「dirs」内の各ディレクトリにあるすべてのファイルの一覧を、変数「files」に設定します:

dirs := a b c d
files := $(foreach dir,$(dirs),$(wildcard $(dir)/*))

 ここで text は「$(wildcard $(dir)/*)」です。1回目の繰り返しでは dir の値として「a」が見つかるので、「$(wildcard a/*)」と同じ結果を生みます。2回目の繰り返しは「$(wildcard b/*)」の結果を、3回目は「$(wildcard c/*)」の結果を生みます。

 この例は、(「dirs」を設定する点を除けば)次の例と同じ結果になります:

files := $(wildcard a/* b/* c/* d/*)

 text が複雑なときは、追加の変数を使ってそれに名前を付けることで、読みやすさを改善できます:

find_files = $(wildcard $(dir)/*)
dirs := a b c d
files := $(foreach dir,$(dirs),$(find_files))

 ここでは変数 find_files をこのように使っています。ここで素の「=」を使って再帰展開変数として定義しているのは、その値に、foreach の制御のもとで再展開されるべき実際の関数呼び出しが含まれるようにするためです。単純展開変数では駄目で、その場合 wildcardfind_files を定義した時点で一度だけ呼ばれてしまいます。

 let 関数と同様、foreach 関数は変数 var に恒久的な影響を与えません。foreach 関数の呼び出し後の var の値と種別は、呼び出し前と同じです。list から取られたそれ以外の値は、foreach の実行中だけ一時的に有効です。変数 var は、foreach の実行中は単純展開変数です。foreach 関数の呼び出し前に var が未定義であったなら、呼び出し後も未定義のままです。変数の2つの種別を参照してください。

 変数名を生み出すような複雑な変数式を使うときは、注意が必要です。というのも、奇妙な文字列の多くが有効な変数名になり得るからで、それはおそらく意図したものではないでしょう。例えば、

files := $(foreach Esta-escrito-en-espanol!,b c ch,$(find_files))

 もし find_files の値が「Esta-escrito-en-espanol!」という名前の変数を参照しているなら(es un nombre bastante largo, no?〔ずいぶん長い名前ですよね?〕)、これは役に立つかもしれません。しかし、たいていは間違いでしょう。

8.7 file 関数

 file 関数を使うと、makefile からファイルへの書き込みやファイルからの読み込みができます。書き込みには2つのモードがサポートされています。1つは「上書き」で、テキストがファイルの先頭から書き込まれ、既存の内容はすべて失われます。もう1つは「追記」で、テキストがファイルの末尾に書き込まれ、既存の内容は保たれます。どちらの場合も、ファイルが存在しなければ作成されます。書き込みのためにファイルを開けない場合や、書き込み操作が失敗した場合は、致命的なエラーになります。ファイルへ書き込むとき、file 関数は空文字列に展開されます。

 ファイルから読み込むとき、file 関数はそのファイルの内容そのまま(ただし最後の改行が1つあればそれは取り除かれます)に展開されます。存在しないファイルから読み込もうとした場合は、空文字列に展開されます。

 file 関数の構文は次のとおりです:

$(file op filename[,text])

 file 関数が評価されるとき、まずそのすべての引数が展開され、それから filename が示すファイルが、op が記述するモードで開かれます。

 演算子 op には、ファイルを新しい内容で上書きすることを示す >、ファイルの現在の内容に追記することを示す >>、ファイルの内容を読み込むことを示す < のいずれかを指定できます。filename は、書き込み先または読み込み元のファイルを指定します。演算子とファイル名の間には、空白を入れてもかまいません。

 ファイルを読み込むときに text の値を与えるのはエラーです。

 ファイルへ書き込むときは、text がファイルへ書き込まれます。text がまだ改行で終わっていなければ、最後に改行が1つ書き込まれます(text が空文字列であっても書き込まれます)。text 引数がまったく与えられなかった場合は、何も書き込まれません。

 例えば、file 関数は、ビルドシステムのコマンドラインの長さに制限があり、かつレシピで実行するコマンドが引数をファイルから受け取れる場合に役立ちます。多くのコマンドには、@ を前に付けた引数がさらなる引数を含むファイルを指す、という慣習があります。その場合、レシピを次のように書けます:

program: $(OBJECTS)
        $(file >$@.in,$^)
        $(CMD) $(CMDFLAGS) @$@.in
        @rm $@.in

 もしコマンドが、各引数を入力ファイルの別々の行に置くことを要求するなら、レシピを次のように書けます:

program: $(OBJECTS)
        $(file >$@.in) $(foreach O,$^,$(file >>$@.in,$O))
        $(CMD) $(CMDFLAGS) @$@.in
        @rm $@.in

8.8 call 関数

 call 関数は、新しいパラメータ付き関数を作るために使えるという点で、他に類を見ない関数です。複雑な式を変数の値として書いておき、call を使って、それを異なる値で展開できます。

 call 関数の構文は次のとおりです:

$(call variable,param,param,…)

 make がこの関数を展開するとき、各 param を一時変数 $(1)$(2) などに割り当てます。変数 $(0) には variable が入ります。パラメータ引数の個数に上限はありません。下限もありませんが、パラメータなしで call を使っても意味がありません。

 そして variable が、これらの一時的な割り当てのもとで make 変数として展開されます。したがって、variable の値の中の $(1) への参照は、その call 呼び出しにおける最初の param に解決されます。

 variable はその変数の名前であって、その変数への参照ではない、という点に注意してください。したがって、それを書くときに「$」や括弧は通常使いません(ただし、名前を定数にしたくない場合は、名前の中で変数参照を使ってもかまいません)。

 variable が組み込み関数の名前である場合は、同名の make 変数が存在していても、常に組み込み関数のほうが呼び出されます。

 call 関数は、param 引数を一時変数に割り当てる前に展開します。このため、foreachif のように特別な展開ルールを持つ組み込み関数への参照を含む variable の値は、期待どおりに動かないことがあります。

 いくつか例を挙げると分かりやすいでしょう。

 次のマクロは、単に引数を逆順にします:

reverse = $(2) $(1)

foo = $(call reverse,a,b)

 ここで foo には「b a」が入ります。

 こちらは少し興味深いものです。PATH の中から、あるプログラムが最初に見つかる場所を探すマクロを定義します:

pathsearch = $(firstword $(wildcard $(addsuffix /$(1),$(subst :, ,$(PATH)))))

LS := $(call pathsearch,ls)

 これで変数 LS には /bin/ls やそれに類するものが入ります。

 call 関数は入れ子にできます。再帰的な呼び出しはそれぞれ、より上位の call の値を覆い隠す、自分専用のローカルな $(1) などの値を持ちます。例えば、次は map 関数の実装です:

map = $(foreach a,$(2),$(call $(1),$(a)))

 これで、通常は引数を1つしか取らない関数(例えば origin)を、複数の値に一度に map できます:

o = $(call map,origin,o map MAKE)

 その結果、o には「file file default」のようなものが入ります。

 最後に一つ注意を。call の引数に空白を加えるときは気をつけてください。他の関数と同じく、2番目以降の引数に含まれる空白は保たれるので、これが奇妙な結果を引き起こすことがあります。call にパラメータを渡すときは、余計な空白をすべて取り除くのが一般に最も安全です。

8.9 value 関数

 value 関数は、変数の値を展開せずに使うための手段を提供します。ただし、これはすでに行われた展開を取り消すものではない、という点に注意してください。例えば単純展開変数を作った場合、その値は定義時に展開されてしまっているので、その場合 value 関数は変数を直接使ったのと同じ結果を返します。

 value 関数の構文は次のとおりです:

$(value variable)

 variable はその変数の名前であって、その変数への参照ではない、という点に注意してください。したがって、それを書くときに「$」や括弧は通常使いません(ただし、名前を定数にしたくない場合は、名前の中で変数参照を使ってもかまいません)。

 この関数の結果は、variable の値を、何の展開も行わずにそのまま含む文字列です。例えば、次の makefile では:

FOO = $PATH

all:
        @echo $(FOO)
        @echo $(value FOO)

 1行目の出力は ATH になります。これは「$P」が make 変数として展開されるためです。一方、2行目の出力は、あなたの環境変数 $PATH の現在の値になります。これは value 関数が展開を回避したためです。

 value 関数は、eval 関数(eval 関数を参照してください)と組み合わせて使われることが最も多いです。

8.10 eval 関数

 eval 関数はとても特別です。これを使うと、定数ではない新しい makefile の構文要素——他の変数や関数を評価した結果としてできるもの——を定義できます。eval 関数の引数はまず展開され、その展開結果が makefile の構文として解析されます。展開結果は、新しい make 変数、ターゲット、暗黙のルールや明示的なルールなどを定義できます。

 eval 関数の結果は常に空文字列です。したがって、makefile のほぼどこに置いても、構文エラーを引き起こすことはありません。

 ここで大切なのは、eval の引数が2回展開されることを理解しておくことです。1回目は eval 関数によって、そして2回目はその展開結果が makefile の構文として解析されるときに、再び展開されます。つまり、eval を使うときには、「$」文字のためにエスケープの段階をさらに増やす必要が出てくることがあります。こうした状況では、望まない展開を回避するために value 関数(value 関数を参照してください)が役立つことがあります。

 ここに、eval をどう使えるかの例を挙げます。この例は、いくつもの概念や他の関数を組み合わせています。この例で、ルールをそのまま書き出さずに eval を使うのは大げさに見えるかもしれませんが、2つのことを考えてみてください。第一に、テンプレート定義(PROGRAM_template の中)は、ここで示したものよりずっと複雑になる必要があるかもしれません。第二に、この例の複雑で「汎用的な」部分を別の makefile に入れておき、個々の makefile すべてでそれをインクルードする、という使い方ができます。そうすれば、個々の makefile はとてもすっきりしたものになります。

PROGRAMS    = server client

server_OBJS = server.o server_priv.o server_access.o
server_LIBS = priv protocol

client_OBJS = client.o client_api.o client_mem.o
client_LIBS = protocol

# これ以降はすべて汎用的な部分です

.PHONY: all
all: $(PROGRAMS)

define PROGRAM_template =
 $(1): $$($(1)_OBJS) $$($(1)_LIBS:%=-l%)
 ALL_OBJS   += $$($(1)_OBJS)
endef

$(foreach prog,$(PROGRAMS),$(eval $(call PROGRAM_template,$(prog))))

$(PROGRAMS):
        $(LINK.o) $^ $(LDLIBS) -o $@

clean:
        rm -f $(ALL_OBJS) $(PROGRAMS)

8.11 origin 関数

 origin 関数は、他のほとんどの関数とは違って、変数の値を操作しません。代わりに、変数についての情報を教えてくれます。具体的には、その変数がどこから来たのかを教えてくれます。

 origin 関数の構文は次のとおりです:

$(origin variable)

 variable は、問い合わせる対象の変数の名前であって、その変数への参照ではない、という点に注意してください。したがって、それを書くときに「$」や括弧は通常使いません(ただし、名前を定数にしたくない場合は、名前の中で変数参照を使ってもかまいません)。

 この関数の結果は、変数 variable がどのように定義されたかを教える文字列です:

undefined

 variable が一度も定義されていない場合。

default

 variable が、CC などのようにデフォルトの定義を持つ場合。暗黙のルールで使われる変数を参照してください。なお、デフォルト変数を再定義した場合、origin 関数は後の定義のほうの出自を返します。

environment

 variable が、make に与えられた環境から継承されたものである場合。

environment override

 variablemake に与えられた環境から継承されたもので、かつ「-e」オプション(オプションの概要を参照してください)の結果として makefile 内の variable の設定をオーバーライドしている場合。

file

 variable が makefile の中で定義された場合。

command line

 variable がコマンドラインで定義された場合。

override

 variable が makefile 内の override ディレクティブで定義された場合(override ディレクティブを参照してください)。

automatic

 variable が、各ルールのレシピを実行するために定義される自動変数である場合(自動変数を参照してください)。

 この情報は、(単なる好奇心を別にすれば)主に、ある変数の値を信用してよいかどうかを判断するのに役立ちます。例えば、別の makefile bar をインクルードする makefile foo があるとします。「make -f bar」というコマンドを実行したとき、環境に bletch の定義が含まれていても、変数 bletchbar の中で定義してほしいとします。ただし、foobar をインクルードする前にすでに bletch を定義していた場合は、その定義をオーバーライドしてほしくありません。これは、foo の中で override ディレクティブを使い、その定義に bar の後の定義より優先権を持たせることでも実現できます。しかし残念ながら、override ディレクティブはコマンドラインの定義までもオーバーライドしてしまいます。そこで、bar に次の内容を含めるとよいでしょう:

ifdef bletch
ifeq "$(origin bletch)" "environment"
bletch = barf, gag, etc.
endif
endif

 bletch が環境から定義されていた場合、これはそれを再定義します。

 もし bletch の以前の定義が環境から来たものであるなら、「-e」の下であってもオーバーライドしたい、というときは、代わりに次のように書けます:

ifneq "$(findstring environment,$(origin bletch))" ""
bletch = barf, gag, etc.
endif

 ここでは、「$(origin bletch)」が「environment」または「environment override」のどちらかを返した場合に再定義が行われます。文字列の置換・解析のための関数を参照してください。

8.12 flavor 関数

 flavor 関数は、origin 関数と同じく、変数の値を操作するのではなく、変数についての情報を教えてくれます。具体的には、変数の種別(flavor)を教えてくれます(変数の2つの種別を参照してください)。

 flavor 関数の構文は次のとおりです:

$(flavor variable)

 variable は、問い合わせる対象の変数の名前であって、その変数への参照ではない、という点に注意してください。したがって、それを書くときに「$」や括弧は通常使いません(ただし、名前を定数にしたくない場合は、名前の中で変数参照を使ってもかまいません)。

 この関数の結果は、変数 variable の種別を示す文字列です:

undefined

 variable が一度も定義されていない場合。

recursive

 variable が再帰展開変数である場合。

simple

 variable が単純展開変数である場合。

8.13 make を制御する関数

 これらの関数は、make の動き方を制御します。一般には、makefile の利用者に情報を提供したり、何らかの環境上のエラーが検出されたときに make を停止させたりするために使われます。

$(error text…)

 メッセージを text とする致命的なエラーを生成します。なお、エラーはこの関数が評価されたときに必ず生成される、という点に注意してください。したがって、これをレシピの中や、再帰展開変数の代入の右辺に置いた場合、評価されるのは後になってからです。text はエラーが生成される前に展開されます。

 例えば、

ifdef ERROR1
$(error error is $(ERROR1))
endif

は、make 変数 ERROR1 が定義されている場合、makefile の読み込み中に致命的なエラーを生成します。あるいは、

ERR = $(error found an error!)

.PHONY: err
err: ; $(ERR)

は、err ターゲットが呼び出された場合、make の実行中に致命的なエラーを生成します。

$(warning text…)

 この関数は、上の error 関数と同じように働きますが、make が終了しない点が異なります。代わりに、text が展開され、その結果のメッセージが表示されますが、makefile の処理は続行されます。

 この関数の展開結果は空文字列です。

$(info text…)

 この関数は、その(展開済みの)引数を標準出力に表示する以外、何もしません。makefile 名や行番号は付加されません。この関数の展開結果は空文字列です。

8.14 shell 関数

 shell 関数は、wildcard 関数(関数 wildcardを参照してください)を除けば、他のどの関数とも異なります。これは make の外の世界とやり取りをするからです。

 shell 関数は、ほとんどのシェルにおけるバッククオート(「`」)と同じ機能を make に提供します。すなわちコマンド展開を行います。これは、引数としてシェルコマンドを取り、そのコマンドの出力に展開する、という意味です。make がその結果に対して行う処理は、各改行(またはキャリッジリターンと改行の組)を1個のスペースに変換することだけです。末尾に(キャリッジリターンと)改行があれば、それは単に取り除かれます。

 shell 関数の呼び出しによって実行されるコマンドは、その関数呼び出しが展開されるときに実行されます(make が Makefile を読み込む仕組みを参照してください)。この関数は新しいシェルの起動を伴うため、shell 関数を再帰展開変数の中で使う場合と単純展開変数の中で使う場合とで、性能への影響をよく検討しておくべきです(変数の2つの種別を参照してください)。

 shell 関数の代替として「!=」代入演算子があります。これは似た動作をしますが、微妙な違いがあります(変数の設定を参照してください)。「!=」代入演算子は、より新しい POSIX 標準に含まれています。

 shell 関数または「!=」代入演算子が使われた後、その終了ステータスが .SHELLSTATUS 変数に格納されます。

 ここに shell 関数の使用例をいくつか挙げます:

contents := $(shell cat foo)

 これは、ファイル foo の内容を、各行を(改行ではなく)スペースで区切った形で、contents に設定します。

files := $(shell echo *.c)

 これは「*.c」の展開結果を files に設定します。make がよほど風変わりなシェルを使っているのでない限り、これは(少なくとも1つの「.c」ファイルが存在する限り)「$(wildcard *.c)」と同じ結果になります。

 export の対象として印が付けられたすべての変数は、shell 関数が起動するシェルにも渡されます。これによって、変数展開のループが生じる可能性があります。次の makefile を考えてみてください:

export HI = $(shell echo hi)
all: ; @echo $$HI

 make がレシピを実行しようとするとき、変数 HI を環境に加えなければなりません。そのためには HI を展開する必要があります。この変数の値は shell 関数の呼び出しを必要とし、それを呼び出すにはその環境を作らなければなりません。HI はエクスポートされているので、その環境を作るには HI を展開する必要があります。そしてこれが延々と続きます。この分かりにくいケースでは、make はループに陥ったりエラーを出したりするのではなく、make に与えられた環境にあるその変数の値を使い、なければ空文字列を使います。これはたいてい、あなたが望んでいることでしょう。例えば:

export PATH = $(shell echo /usr/local/bin:$$PATH)

 とはいえ、この場合は最初から単純展開変数(「:=」)を使うほうが、より簡単で効率的でしょう。

8.15 guile 関数

 GNU make が、組み込みの拡張言語として GNU Guile のサポート付きでビルドされている場合、guile 関数が利用できます。guile 関数は引数を1つ取り、まず make によって通常どおり展開され、それから GNU Guile の評価器に渡されます。評価器の結果は文字列に変換され、makefile 内での guile 関数の展開結果として使われます。Guile で make の拡張を書く方法の詳細については、GNU Guile との統合を参照してください。

 GNU Guile のサポートが利用できるかどうかは、.FEATURES 変数の中に「guile」という単語があるかどうかを調べることで判定できます。


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