「ほとんど忘れた、Makefile」 にて：

「忘れている」ってよりは、僕の知識じゃ古すぎて、改めて勉強しないとダメでした*1。

なにしろ、makeだけじゃ機能が貧弱なんで、cpp（Cプリプロセッサ）やm4（マクロプロセッサ）と組み合わせて使っていた頃しか知らんからね（古すぎ！）。今じゃGNU Makeを（使おうと思えば）どこでも使えるから、GNU Makeを習えばそれでいいじゃないかな。僕は、Windows上のMSYS（MinGW - Minimal SYStem）でGNU Makeを動かしました。

というわけで、GNU Makeの手習いをしたからメモしておきます。以下、名前がMakefileじゃなくても、GNU Makeへの指示を書いたファイルは何でもMakefileと呼びます。

[追記]id:paellaさんのブックマークコメントに曰く：

はい、そういうことです。ターゲットとかルールについては、別な資料をあたってください。

[/追記]

内容：

includeとif 変数と関数 空白に注意！ 続きがあると思う

●includeとif

GNU Makeは、cppのお世話になんかなりませんね。includeとifがあります。if文は、ifeq, ifneq, ifdef, ifndefで、文字列の等値性と変数の定義状況を調べることができます。等号以外の比較演算／論理演算は使えないから、その点ではcppのほうが高機能((cppだと、 #if (defined(debug) || defined(DEBUG)) && !defined(NO_DEBUG) のような書き方ができます。))かもしれないけど、これだけでも間に合うでしょう。

Makefile実例：



# file: t1.mk

include ../common.mk ifeq ($(OS_TYPE),win32)

include ../win32.mk

endif -include optional.mk ifdef debug

DEBUG_FLAG = -DDEBUG

else

DEBUG_FLAG =

endif print_vars:

@echo "OS_TYPE='$(OS_TYPE)'"

@echo "DEBUG_FLAG='$(DEBUG_FLAG)'"

念のため説明：

ファイル../common.mkをインクルードします。僕は、 include "../common.mk" と書いてハマってました。cppじゃないって。 変数OS_TYPEの値がwin32のときは、ファイル../win32.mkをインクルードします。変数の値は、環境変数やmakeコマンド起動時オプションにより指定できます。 includeディレクティブの前にマイナスを付けると、ファイルがなくてもエラーになりません。 -include optional.mk は、ファイルoptional.mkがあれば読み込み、なければ何もしません。 変数debugが定義されていれば（値はなんでもいい）、変数DEBUG_FLAGの値を-DDEBUGに設定します。そうでないなら、DEBUG_FLAGの値は空（長さ0の文字列、未定義と同じ）です。 確認のためのターゲットprint_varsが実行されると、変数値を表示します。

実行例：



$ make -f t1.mk

OS_TYPE=''

DEBUG_FLAG='' $ make OS_TYPE=win32 -f t1.mk

OS_TYPE='win32'

DEBUG_FLAG='' $ make OS_TYPE=win32 debug=1 -f t1.mk

OS_TYPE='win32'

DEBUG_FLAG='-DDEBUG'

●変数と関数

Makefileの構文はどうも一貫性がなく、オマジナイのような記号が色々あって、なじみにくいものでした。GNU Makeは、構文と意味論を整理して合理化しようと試みています。互換性から古い構文も残っていますが、新しい一貫した構文を使ったほうがいいと思います。

構文の大原則として、変数参照は$(FOO)のようにドル記号（「$」）を使います。丸括弧（例：$(FOO)）または波括弧（例：${FOO}）は必須だと思ってください。例外は変数名が1文字のときで、$FOOは$(F)OOと同じです。$@、$< のような変な記号も、名前が「@」、「

ドル記号は、変数参照だけでなく、関数呼び出しにも使えます。例えば、$(subst ge,GA,$(FOO)) は関数substに3つの引数を渡して呼び出しています。substは文字列置換関数で、$(FOO)の値である文字列に出現するgeをGAに置き換えます。



# file: t2.mk

FOO=hoge hoge fuga hage

BAR=$(subst ge,GA,$(FOO)) print_vars:

@echo "FOO='$(FOO)'"

@echo "BAR='$(BAR)'"

このMakefileを実行すると、



$ make -f t2.mk

FOO='hoge hoge fuga hage'

BAR='hoGA hoGA fuga haGA'

Perlなら、次ようになりますかね。



$FOO = "hoge hoge fuga hage";

($BAR = $FOO) =~ s/ge/GA/g;

print "FOO='$FOO'

";

print "BAR='$BAR'

";

実は、関数呼び出しを使うときは、代入に「=」を使うより「:=」のほうが適切かつ効率的なときが多いのですが、その話は次の機会にします。（続きに書きました。）

もちろん、ドル記号を使った変数参照／関数呼び出しの出現は、その値／戻り値に展開（置換）されます。ドル記号そのものを書きたいなら$$とします。

●空白に注意！

Makefileの変数には、値として文字列が入ります。しかし、プログラミング言語とは違って、引用符（「"」や「'」）をまったく使わないので、文字列の開始／終了が分かりにくいですねー。

次のMakefileを実行するとどうなるか、予測できますか？



# file: t3.mk

X1=a

X2= a

X2 = a

X3= a # with trailing spaces

X4= a b

X5= a b print_vars:

@echo "X1='$(X1)'"

@echo "X2='$(X2)'"

@echo "X3='$(X3)'"

@echo "X4='$(X4)'"

@echo "X5='$(X5)'"

やってみましょう。



$ make -f t3.mk

X1='a'

X2='a'

X3='a '

X4='a b'

X5='a b'

イコールの左右の空白は無視されます。したがって、値の先頭空白が無視されることになります。しかし、値の末尾や中間にある空白はそのまま残ります。

次はどうでしょう？



# file: t4.mk

X1= hello world

X2=$(subst h,H,$(X1))

X3=$(subst h,H,$(X1))

X4=$(subst h, H,$(X1))

X5=$(subst h, H ,$(X1))

X6=$(subst h, H, $(X1))

X7=$(subst h ,H ,$(X1)) print_vars:

@echo "X1='$(X1)'"

@echo "X2='$(X2)'"

@echo "X3='$(X3)'"

@echo "X4='$(X4)'"

@echo "X5='$(X5)'"

@echo "X6='$(X6)'"

@echo "X7='$(X7)'"

こうなります。

関数呼び出しにおける、関数名と引数並びのあいだの空白はいくつあっても同じですが、カンマの前後の空白とお尻の空白はそのまま保存されます。

ifeqはもっと奇妙な挙動をします。「余分な空白があるとヤバいな」と思ったら、前後の空白を削るstrip関数を使ってください。なお、strip関数は中間の空白も圧縮します*2。



# file: t5.mk

A=foo

B=foo # with trailing space ifeq ($(A),$(A))

X1 = A=A

endif ifeq ($(A), $(A))

X2 = A=_A

endif ifeq ( $(A),$(A))

X3 = _A=A

endif ifeq ($(A),$(A) )

X4 = A=A_

endif ifeq ( $(A), $(A))

X5 = _A=_A

endif ifeq ($(A),$(B))

X6 = A=B

endif ifeq ($(B),$(A) )

X7 = B=A_

endif ifeq ($(A),$(strip $(B)))

X8 = A=stripB

endif print_vars:

@echo "X1='$(X1)'"

@echo "X2='$(X2)'"

@echo "X3='$(X3)'"

@echo "X4='$(X4)'"

@echo "X5='$(X5)'"

@echo "X6='$(X6)'"

@echo "X7='$(X7)'"

@echo "X8='$(X8)'"

実行結果はこんなです。



$ make -f t5.mk

X1='A=A'

X2='A=_A'

X3=''

X4=''

X5=''

X6=''

X7='B=A_'

X8='A=stripB'

X2の'A=_A'ってのがなんか不思議ですねー、どうやらifeqではカンマの左右の空白は削除されるようです(苦笑)。ともかくトラブルを未然に防ぐには、

余分な空白を入れない！

のが一番です。

では、空白から始まる変数値はどうやって設定するのでしょうか？ 次の裏技があります。（これも「=」より「:=」を使うのが望ましい。）



# file: t6.mk

EMPTY=

SPACE=$(EMPTY) $(EMPTY) print_vars:

@echo "EMPTY='$(EMPTY)'"

@echo "SPACE='$(SPACE)'"



$ make -f t6.mk

EMPTY=''

SPACE=' '

SPACEを定義するときの二番目の$(EMPTY)は不要ですが、書いてないと分かりにくいですよね。

●続きがあると思う

まだ、関数を使ってソースやターゲットを生成する方法とかパターン規則の説明をしてないので、続きを書くと思います。調べているうちに、GNU Makeの構文（の一部）はある種のプログラミング言語だという気がしてきました；そのことも書きたい気がしてます。いつものことながら、保証はしませんけどね。

[追記]続きを書きました。

[/追記]