関数を使えばmakefileで処理する文字列で利用するコマンドやその実行対象のファイルをコンピュータに計算させることができるようになります。関数を使うには、関数の名前と関数に与える(引数という)付随文字列からなるファンクション・コール(関数の呼び出し; function call)を使います。関数の処理結果は変数の場合と全く同じように、makefileの関数を呼び出した部分に置き換えられます。
ファンクションコールは変数の参照に似ていて、このようになっています。
$(関数 引数)
またはこうなります。
${関数 引数}
この関数というのが関数名になり、make
に用意された数少ない名前のリストのうちの一つを使います。新しい関数は定義できません。
引数というのが関数の引数です。引数は関数名と一つ以上のスペースかタブで区切られており、複数の引数がある場合は引数同士をコンマで区切ります。このような空白やコンマは引数の値の一部にはなりません。ファンクションコールを囲むデリミタ(区切り文字)として括弧と大括弧のどちらを使ったにしろ、その文字自身を引数として一対になっている場合は使う事ができ、使ってないほうのデリミタは対になってなくても構いません。引数自身に別のファンクションコールや変数参照が含まれる場合、全ての参照に同じデリミタを使うのが賢い方法です。つまり`$(subst a,b,${x})'と書くのではなくて `$(subst a,b,$(x))'として下さい。そのほうがきれいですし、参照の終端を探すのに一つのデリミタを一致させるだけで済むからです。
それぞれの引数にかかれた文字列は、関数に付随する引数の値を変数の代入とファンクションコールで作成してから処理されます。代入作業は引数が並ぶ順番で行います。
コンマと、一致しない括弧と大括弧は引数の文字列として書くことができず、最初の引数の文字列として前置きの空白は書けません。これらの文字を引数の値に入力するには変数の代入を使います。次のように、最初にコンマと空白文字を隔離させて値としてcomma
とspace
というように定義した変数を使ってそういう文字が必要な場所でその変数を代用してください。
comma:= , empty:= space:= $(empty) $(empty) foo:= a b c bar:= $(subst $(space),$(comma),$(foo)) # bar is now `a,b,c'.
ここではsubst
関数はfoo
の値の全ての空白をコンマに置換し、結果を関数のある場所に代入します。
ここに文字列を操作する関数を示します。
$(subst from,to,text)
$(subst ee,EE,feet on the street)…とすると、`fEEt on the strEEt'という文字列が代入されます。
$(patsubst pattern,replacement,text)
patsubst
関数実行における`%'はバックスラッシュ(`\')で引用化できます。`%'文字を引用させる目的ではないバックスラッシュはもう一つのバックスラッシュでそれ自身を引用できます。`%'や自身を引用させるためのバックスラッシュはファイル名の比較や代入させる語幹(stem)に使われる前に型から削除されます。`%'の引用とは関係ないバックスラッシュはこのことに関係なく動作します。例えば`the\%weird\\%pattern\\'という型では、`the%weird\'のあとに有効な`%'文字があり、その後に`pattern\\'が続きます。最後の二つのバックスラッシュは`%'に影響しないのでそのまま残ります。単語間の空白部分は一つの空白文字に収縮され、前後の空白部分は廃棄されます。
例えば、
$(patsubst %.c,%.o,x.c.c bar.c)…とすると、`x.c.o bar.o'という値が生成されます。
patsubst
関数の効果を得るには代用参照(代用参照の項を参照)を使うほうが簡単です。
$(var:pattern=replacement)…は、下と同等です。
$(patsubst pattern,replacement,$(var))もう一つの手っ取り早い方法を使えば
patsubst
の最も一般的な利用法の一つであるファイル名の最後にある語尾(サフィックス;suffix)の置換という作業を簡単に行えます。
$(var:suffix=replacement)…は、下のものと同等です。
$(patsubst %suffix,%replacement,$(var))例えば、次のようなオブジェクトファイルのリストがあるとします。
objects = foo.o bar.o baz.o相当するソースファイルのリストを取得するにはこうやれば間単に取得できます。
$(objects:.o=.c)…は、一般形である次のものの代わりです。
$(patsubst %.o,%.c,$(objects))
$(strip string)
strip
という関数は条件分岐と一緒に使うととても便利です。`'という空っぽの文字列をifeq
かifneq
を使って何かと比較させる場合、普通は空白文字からなる文字列も空っぽの文字列として扱わせたいはずだからです(Makefilesの条件分岐部分の項を参照)。
だから、以下は望んだ結果にはならないかもしれません。
.PHONY: all ifneq "$(needs_made)" "" all: $(needs_made) else all:;@echo 'Nothing to make!' endif
ifneq
ディレクティヴの`$(needs_made)'という変数参照を`$(strip $(needs_made))'というファンクションコールに置き換えればもっと信用できるスクリプトになります。
$(findstring find,in)
$(findstring a,a b c) $(findstring a,b c)`a'という値と(空っぽの文字列である)`'をそれぞれ生成します。
findstring
の実用法についてはフラグを調べるための条件分岐の項を見てください。
$(filter pattern...,text)
patsubst
関数で使った型と同様に`%'を使って書きます。filter
関数は変数中にあるタイプの違う(ファイル名のような)文字列を締め出すのに使えます。
例えば、
sources := foo.c bar.c baz.s ugh.h foo: $(sources) cc $(filter %.c %.s,$(sources)) -o foo…とすると、`foo'は`foo.c'、`bar.c'、`baz.s'、`ugh.h'に依存して構成されるが、`foo.c'、`bar.c'、`baz.s'だけがコンパイラへのコマンドに指定されなければならない、という意味になります。
$(filter-out pattern...,text)
filter
関数の正反対になります。例えば、
objects=main1.o foo.o main2.o bar.o mains=main1.o main2.o…と与えておくと、以下のものは`mains'にない全てのオブジェクトファイルを含めたリストを生成します。
$(filter-out $(mains),$(objects))
$(sort list)
$(sort foo bar lose)…とすると`bar foo lose'という値を返します。
sort
はついでに重複文字列を削除してくれるので、順番をソートするのに関係ない場合でもこの目的で使うこともできます。
次に示すのはsubst
とpatsubst
の実用例です。make
に依存関係を探させるディレクトリのリストを指定するのにmakefileでVPATH
変数を使うと仮定します(VPATH
: 全依存関係をPATHから探すの項を参照)。この例ではディレクトリのものと同じリストの中からCコンパイラにヘッダファイルを探させる方法を示しています。
VPATH
の値は`src:../headers'のようにコロンで区切られたディレクトリのリストです。はじめにコロンをスペースに変更するのにsubst
関数を使います。
$(subst :, ,$(VPATH))
これで`src ../headers'が生成されます。それから、各ディレクトリ名に`-I'フラグをつけるのにpatsubst
を使います。この結果は次のようにCFLAGS
という変数の値に追加して、自動的にCコンパイラに渡させることができます。
override CFLAGS += $(patsubst %,-I%,$(subst :, ,$(VPATH)))
`-Isrc -I../headers'という文字列がこの前にCFLAGS
に与えた値に追加されます。CFLAGS
がコマンド引数で指定された場合も新しい値を使わせるのにoverride
ディレクティヴを利用しています(override
ディレクティヴの項を参照)。
ビルトイン拡張関数のいくつかはファイル名の分析やファイル名のリストに特に関係しています。
以下に示す関数はそれぞれ特定のファイル名変形を提供します。どの関数の引数も空白で区切られたファイル名の連続したものとして扱います。(前後の空白部分は無視されます。)連続するどのファイル名も同じ方法で変形されて、結果はファイル名の間にスペースを一つ挟んで鎖のように順番に並べたものになります。
$(dir names...)
$(dir src/foo.c hacks)…とすると結果は`src/ ./'になります。
$(notdir names...)
$(notdir src/foo.c hacks)…とすると結果は`foo.c hacks'になります。
$(suffix names...)
$(suffix src/foo.c src-1.0/bar.c hacks)…とすると、結果は`.c .c'になります。
$(basename names...)
$(basename src/foo.c src-1.0/bar hacks)…とすると結果は`src/foo src-1.0/bar hacks'になります。
$(addsuffix suffix,names...)
$(addsuffix .c,foo bar)…とすると結果は`foo.c bar.c'になります。
$(addprefix prefix,names...)
$(addprefix src/,foo bar)…とすると結果は`src/foo src/bar'になります。
$(join list1,list2)
dir
関数とnotdir
関数の結果をマージ(同化)して、二つの関数から作られたオリジナルのファイルリストを作成するのに使えます。
$(word n,text)
$(word 2, foo bar baz)…とすると`bar'を返します。
$(wordlist s,e,text)
make
がこの二つを交換してくれます。例えば、
$(wordlist 2, 3, foo bar baz)…とすると`bar baz'を返します。
$(words text)
$(word $(words text),text)
になります。
$(firstword names...)
$(firstword foo bar)…とすると結果は`foo'になります。
$(firstword text)
は$(word 1,text)
と同じですが、簡便性からfirstword
関数を保持しています。
$(wildcard pattern)
wildcard
の結果は型に一致する存在するファイル名が空白で分けられたリストになります。
これについてはファイル名にワイルドカードを利用するの項を見てください。
foreach
関数
foreach
関数は他の関数とは大きく違います。この関数は毎回代入が実行されるたびにテキストの一部分を繰り返し利用します。この機能はsh
シェルのfor
コマンドやCシェルcsh
のforeach
コマンドと似ています。
foreach
関数の構文は次に示すとおりです。
$(foreach var,list,text)
varとlistという最初の二つの引数は他の動作の前に展開されます。ここで注意しておいてほしいのは最後の引数textが同時に展開されないという事です。それで(変数参照が含まれていれば展開して変数名として)varという名前の変数にlistの値として展開された各単語を一つづつセットし、そのたびにtextを展開します。多分textにはその変数への参照を含めるので毎回展開内容が違います。
textは結果としてlistの空白で区切られた単語の量と同じだけ展開されます。textの複数回の展開はスペースでつなげられ、それがforeach
の実行結果になります。
次の簡単な例では`files'という変数に`dirs'のリストにあるディレクトリの全ファイルのリストをセットします。
dirs := a b c d files := $(foreach dir,$(dirs),$(wildcard $(dir)/*))
ここでは`$(wildcard $(dir)/*)'がtextです。繰り返しの一巡目に`a'という値を見つけてそれをdir
に与えて`$(wildcard a/*)'と同じ結果を生成し、二巡目には`$(wildcard b/*)'とした結果を、三巡目は`$(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
の制御下で再展開されるべき実際のファンクションコールを含んでいるので再帰展開変数を定義するために`='を使います。単純展開変数ではfind_files
の定義時にwildcard
を一度呼び出すだけになってしまうからです。
foreach
関数は永久にvarという変数に効果を及ぼしません。このためファンクションコール以後のその変数の値と味は前のものと同じになります。もう一つのlistから取り出すのに使われたものはforeach
関数の実行中の一定時間だけ効力を持つだけです。foreach
の実行中のvarという変数は単純展開変数です。foreach
のファンクションコール以前にvarが定義されていなかった場合、コール後も定義されていない状態になります。
変数の二つの味の項を見てください。
変数名に複雑な変数の表現を使うことになった場合、多くの変なものが有効な変数名になって、意図したものと違うことになりうるので気をつけておかねばなりません。例えば、
files := $(foreach Esta escrito en espanol!,b c ch,$(find_files))
…とするのはfind_files
の値が`Esta escrito en espanol!' (es un nombre bastante largo,no?)という名前の変数を参照しているときは便利かもしれませんが、失敗を起こしやすくなります。
origin
関数
origin
(起源)関数は変数の値を扱うものではない、という点で他の多くの関数と違っています。つまり変数について何らかのことを知るためのものです。具体的にはどこから生じたかを教えてくれます。
origin
関数の構文は次のものです。
$(origin variable)
variableは調べる変数の参照ではなくて変数の名前であることに注意しておいてください。だから通常は書くときに`$'とか括弧を使わないでしょう。(とはいえ対象とする名前を一定にさせない場合は変数参照を使っても構いません。)
variableという変数がどういう風に定義されたかを知らせる文字列がこの関数の結果になります。次のようなものです。
CC
などのようにvariableがデフォルト定義だった場合。これについては暗黙ルールで使われる変数の項を見てください。origin
関数は後の定義の起源を調べて返します。
override
ディレクティヴで定義されていた場合(override
ディレクティヴの項を参照)。
この情報は(好奇心目的以外には)主に変数の値が信用に足るものかどうかを決めるのに便利です。例えば仮に`foo'というmakefileがあってその中で`bar'という別のmakefileをインクルードしているとします。そして、`make -f bar'と実行した場合、環境変数にbletch
という変数があっても`bar'でその変数を定義させたい。でも`foo'で`bar'をインクルードする前にbletch
が定義された場合はそれを`bar'で上書きさせたくない。こういう希望は`foo'でoverride
ディレクティヴを使って、その定義が後の`bar'での定義よりも優位であると定めれば実現できます。しかし不幸にもoverride
ディレクティヴはコマンドライン上の定義をも上書きしてしまいます。だから`bar'に次のものをインクルードします。
ifdef bletch ifeq "$(origin bletch)" "environment" bletch = barf, gag, etc. endif endif
bletch
が環境で定義されていれば、再定義し直します。
`-e'の影響下において環境で定義されていても前のbletch
の定義を上書きさせたい、という場合は、代わりにこう書くこともできます。
ifneq "$(findstring environment,$(origin bletch))" "" bletch = barf, gag, etc. endif
この場合`$(origin bletch)'が`environment'か`environment override'を返せば再定義が行われます。 文字列を代入・分析する関数の項を見て下さい。
shell
関数
shell
関数はmake
の外界と接触するという点でwildcard
関数(wildcard
という関数の項を参照)だけは似ていますが、他の関数とは毛色が違います。
shell
関数はバッククォート(``')が多くのシェルで果たすコマンドの拡張(command expansion)と同じ機能を持ちます。つまり、引数を一つとってシェルコマンドとし、そのコマンドの出力を返します。このとき、関数のあった部分に代入させる前にmake
が結果に対して行う事は、ただキャリッジリターン(行の先頭に移動)とニューライン(改行)のペア、またはニューライン単体、を、空白文字一つに変換する事と、ついでに(キャリッジリターンと)ニューラインが結果の最後についていればそれを削除するという事だけです。
shell
関数を使って呼び出されるコマンドは、ファンクションコール展開時に実行され、それは多くの場合makefileが読み込まれた時です。例外として、ルールのコマンド内のファンクションコールはそのコマンドの実行時に展開され、これは他のものと同様にshell
のファンクションコールにも適用されることです。
ここにshell
関数の利用例をいくつか示します。
contents := $(shell cat foo)
…とすると、contents
に`foo'の中身の各行を(ニューラインではなく)空白で区切ったものをセットします。
files := $(shell echo *.c)
…とすると、files
に`*.c'の展開結果をセットします。make
がとんでもなくおかしなシェルを使っているのでなければこの結果は`$(wildcard *.c)'と同じになります。(訳注:MS-DOS/MS-Windowsのcommand.com/cmd.exeをシェルにした場合はこうなりません。)