これを書いていたらMakefileに関する知識がついてしまったので、ここにまとめておきます。

基本的な構文

makefileの基本的な構文は以下のようなものである:

target1 target2 ... targetN: dep1 dep2 ... depM
	cmd1
	cmd2
	...
	cmdK

target1, …, targetNをそれぞれターゲットという。ターゲットとは、基本的にはファイル名を表す。例外として、ファイル名ではないターゲットを作ることもできる(後述)。

dep1, …, depMをそれぞれ前提条件という。前提条件も基本的にはファイル名を表す。これらは、ターゲットを生成するために必要となるファイル(等)の一覧である。

cmd1, …, cmdKからなる部分をレシピという。レシピはシェルに渡されるコマンドからなり、これらのコマンドを順に実行することでターゲットを生成することができることを表す。コマンドの前にあるのはタブ文字でなければならない。

このような、ターゲットを生成する方法の定義のことを、ルールという。makefileの基本的な構造は、このようにして定義されるルールの繰り返しである。

この状態で、Makefileのあるディレクトリ上でmake target1を実行すると、dep1, …, depMに関連するルールが実行された上で、cmd1, …, cmdKが実行され、ファイルtarget1が生成される。 target2, …, targetNについても同様。

実行方法

out-file: file-1 file-2
	cat file-1 > out-file
	cat file-2 >> out-file

file-1:
	echo 'hogehogehoge' > file-1

file-2:
	echo 'fugafugafuga' > file-2

このようなMakefileがある状態でmake out-fileというコマンドを実行すると、ターゲットout-fileと、その前提条件に関するルールが順に実行される。

$ ls
Makefile
$ make out-file
echo 'hogehogehoge' > file-1
echo 'fugafugafuga' > file-2
cat file-1 > out-file
cat file-2 >> out-file
$ cat out-file
hogehogehoge
fugafugafuga

なお、makeは賢いので、ターゲットと前提条件にあるファイルの更新時刻を比較し、必要な場合にのみルールの実行を行う。例えば、先ほどの環境でもう一度make out-fileを実行した場合、以下のようなメッセージが表示され、ルールは実行されない。

$ make out-file
make: `out-file' is up to date.

この状態でターゲットや前提条件にあるファイルを削除したり、前提条件にあるファイルを更新したりした場合は、再度ルールが実行される。

$ echo foobarfoobar >> file-2
$ make out-file
cat file-1 > out-file
cat file-2 >> out-file
$ cat out-file
hogehogehoge
fugafugafuga
foobarfoobar

なお、引数なしでmakeを実行した場合、Makefileに書かれている一番最初のルールが実行される。

$ make   # この場合は make out-file と同じ意味
make: `out-file' is up to date.

ターゲット

Phony Target

特定のファイルを生成したいわけではないが、何らかのコマンドを実行したい場合がある。 そのような場合は、phony target(偽のターゲットという意味) を用いる。あるターゲットtargetがphony targetであることを指定するには、.PHONY: targetと書く。

.PHONY: clean
clean:
	rm *.o

このとき、cleanというファイルが実際に存在するかどうかにかかわらず、make cleanと打てばルールcleanが実行される。

変数

変数は、var = valueもしくはvar := valueという形式で定義する。これらの違いについては後述。また、変数展開を行うときは$varもしくは$(var)のように書く。

name = John Doe

.PHONY: greet
greet:
	echo Hello, $(name).
$ make greet
echo Hello, John Doe.
Hello, John Doe.

変数の味

変数には2つの種類(これをという)がある。var = valueで定義される変数は再帰展開変数といい、var := valueで定義される変数は単純展開変数という。

単純展開変数は、シェルスクリプトの変数に近い挙動をする。代入の右辺に変数や後述する関数呼び出しなどがあった場合、単純展開変数ではこれらを代入のタイミングで展開する。例えば、

fruit := orange
msg := I like $(fruit)

.PHONY: greet
greet:
	echo $(msg)

これは以下と全く同様である。

fruit := orange
msg := I like orange

.PHONY: greet
greet:
	echo $(msg)

一方、再帰展開変数の場合は、実際に変数が展開されるまで、右辺の変数や関数呼び出し(後述)の展開を行わない。従って、後で右辺で使われている変数の値が書き換わった場合、それを使用している再帰展開変数の値も書き換わることになる。例えば、以下のmakefileを用いてmake greetを行うと、I like appleが表示される。

fruit = orange
msg = I like $(fruit)

fruit = apple

.PHONY: greet
greet:
	echo $(msg)

また、+=演算子を用いることによって変数の末尾に文字列を追加することができる。この際、変数の味は変わらない。

複数行からなる変数

展開した結果が複数行になるような変数は、以下のようにdefineを用いて定義する:

define CMDS
echo hello
echo goodbye
endef

defineを用いて定義された変数は再帰展開変数となる。

なお、GNU Make 3.82以上では、define VARの後に代入演算子=, :=を置くことが許される。この場合、変数VARの味は置かれた演算子に対応するものとなる(未確認)。

特殊変数

いくつかの変数は自動で定義され、特殊な意味を持つ。以下はその一例である:

  • $@: ターゲット名
  • $<: 前提条件のうち、最初のもの
  • $^: 前提条件すべて

すべての特殊変数の一覧は、公式のマニュアルを参照。

ルール

複数のルールを定義する

同一のターゲットに対して、複数のルールを定義してよい。

1つのターゲットに対しコマンドが書かれたルールが2つ以上あった場合は、それらの前提条件はすべて連結され、一番最後に定義されたルールのコマンドのみが実行される。

例えば、

file1 file2: dep1 dep2

file1: dep3
	cat dep1 dep2 dep3 > file1

file2: dep4
	cat dep1 dep2 dep4 > file2

このmakefileは以下と等価である:

file1: dep1 dep2 dep3
	cat dep1 dep2 dep3 > file1

file2: dep1 dep2 dep4
	cat dep1 dep2 dep4 > file2

デフォルトのルールを定義する

特定のパターンにマッチするターゲットに対して、デフォルトのルールを定義することができる。例えば、以下で定義されている先頭のルールは、*.oファイルを*.cから生成するルールである:

%.o: %.c
	gcc -o $@ $<

hello.o: hello.h

goodbye.o: goodbye.h

hello.o, goodbye.oには明示的にコマンドが与えられていないため、デフォルトのルールに従ってgcc -o hello.o hello.cというようにコマンドが実行される。

ここで定義したデフォルトのルールに含まれるような%を含む文字列はパターンと呼ばれ、%の部分に任意の1文字以上の文字列がマッチする。また、パターンを用いて定義されたルールをパターンルールという。

レシピ

シェルの変更

コマンドは各行ごとにシェルに送られる。

使用されるシェルは変数$(SHELL)で指定できる(デフォルトはsh)。例えばfishを使いたい場合は、以下のようにすればよい。

SHELL := fish

.PHONY: hoge
hoge:
	if true; echo ok; else; echo error; end

実行するコマンドを出力しない

コマンドの先頭に@をつけると、実行するコマンド自体は表示せずに、シェルに@以下の文字列が送られる。例えば、以下のようなMakefileを用意してmake helloを実行すると、実行するコマンドであるecho hello, worldが表示され、その後に実行結果であるhello, worldが表示される。

.PHONY: hello
hello:
	echo hello, world
$ make hello
echo hello, world
hello, world

一方、コマンドの先頭に@を付けた場合、コマンド自体は表示されない。

.PHONY: hello
hello:
	@echo hello, world
$ make hello
hello, world

コマンドのエラーを無視する

通常、makeは実行しているコマンドのうち一つがエラー(exit codeが0以外)となった場合、その時点で残りのコマンドの実行を中断する。

.PHONY: fail
fail:
	false
	echo "OK!"
$ make fail
false
make: *** [fail] Error 1

特定のコマンドの返すエラーを無視したい場合は、そのコマンドの頭に-を付ける。その場合、対象のコマンドがエラーとなった場合は警告のみが表示され、makeの処理は続行される。

.PHONY: fail
fail:
	-false
	echo "OK!"
$ make fail
false
make: [fail] Error 1 (ignored)
echo "OK!"
OK!

関数

GNU Makeには、いくつかの関数が用意されている。関数は$(func-name arg1,arg2,...,argN)の形で呼び出す。以下は代表的な関数の例である:

  • $(subst from,to,text): text内のすべての文字列fromtoに置換する。
  • $(patsubst fromp,top,text): text内の、パターンfrompにマッチする部分をパターンtopに置換する。
  • $(firstword words): スペース区切りの単語列wordの先頭の単語を返す。
  • $(dir paths): スペース区切りのパスの列pathsの各要素から、末尾のファイル名を取り除いたものを返す。
  • $(wildcard pat): pat内のワイルドカードを展開する。
  • $(forearch var,list,text): 単語列listの各単語を先頭から順にvarに束縛した状態でlistを評価したものを返す。
  • $(if cond,then,else): condが空文字列ならelseを、そうでないならthenを返す。

すべての関数の一覧は公式のマニュアルを参照。

ユーザー定義関数

ユーザー定義関数とは、実際にはただの変数である。$(call USERFUNC,arg1,...,argN)を評価すると、$1,…,$Nにそれぞれarg1,…,argNが束縛された状態で変数USERFUNCが展開される。

HELLO = Hello, $(1)!

.PHONY: hoge
hoge:
	echo $(call HELLO,World)
$ make hoge
echo Hello, World!
Hello, World!

その他

コメント

#から行末まではコメント

# 必要なオブジェクトファイル
OBJS = hoge.o fuga.o foo.o bar.o

行継続

行末にバックスラッシュ(\)を置くと、そこから次の行までが一つの行とみなされる。