Makefileでビルドを管理する場合にソースディレクトリを分けていると同じ記述を何度もするのが煩わしかったが、 まとめる方法があることを知った。
前口上
自分の好みとしてソースを格納するディレクトリとビルドで生成するオブジェクトファイルは別のディレクトリに入れるのが好きなんだけど、ソースをさらにいくつかのサブディレクトリに分けている場合に%
を使ったパターンルールを内容は同じでもディレクトリごとに書く必要があるのが面倒だった。
例えばsrc
以下にfoo
とbar
というディレクトリがあったとして:
$ tree |
Makefileで
# 同じ内容でもディレクトリごとにパターンルールを書く必要がある |
コンパイルしてオブジェクトファイルを生成するルールが「$(SRC_DIR)
以下のすべて」とか書ければいいんだけどその方法を知らなくて、ソースディレクトリごとに書く必要があって面倒だった。
(src/**/%.c
で書ける、と一瞬喜んだが第2階層だけだった)。
define, foreach, call, eval
Makeにはマクロという機能があるとのことで、define
で定義できる:
define DEFINE_OBJ_TARGET |
endef
までが内容で、パラメータが$1
以降に渡される。
注意点としては$
の変数参照がマクロ展開時に行われるので、ルール内で行わせたい場合にはエスケープして二重の$$
にする必要がある。
定義したマクロを使うにはeval
-call
を、そして配列で複数に対して行うためにforeach
を使う:
SRC_DIRS:=$(foo_DIR) $(bar_DIR) |
foreach
の第2引数の各要素を第1引数(D
)の変数にセットして第3引数の内容が実行される。
call
でマクロを呼び出した結果をeval
することで要素ごとのルールが定義される。
追加の情報で処理したい場合
ちょっと変えて例えば実行ファイルfoo
とbar
をそれぞれxxx_OBJS
変数のオブジェクトファイル群からビルドしたい、という場合にどうするか。
foreach
に渡す配列をターゲット名とオブジェクトファイル群の二重配列にして、とか文字列を連結しといたものを分割して、とか考えたがうまくいかず。
がなんのことはない、変数の展開を重ねて、
define DEFINE_EXE_TARGET |
とするだけでできる
(内容としては$(1)
がマクロcall
時に展開され、$$
はcall
で$
になったものがeval
時に処理される。
が$$
を$
にしてもちゃんと動く、重ねてもちゃんと展開できる)。
「このディレクトリには特別にコンパイルオプションを足したい」、とかも変数名で引けるようにしておけばできるでしょう
(未定義変数の場合は単に空白に置換される)。
Makefileの関数wildcard
やnotdir
、addprefix
などを使えば、ファイル構成と命名に沿ってルールを生成することもできると思う。
参照
- Makefile で {単数 | 複数} の {依存項目 | ターゲット} を指定するルールの書き方 - Qiita
- GNU make GNU Makeのマニュアル
- The foreach Function, The eval Function, The call Function
- Makeではマクロ・関数といっても変数と同じもので、
call
で引数が文字列置換されたものをeval
で反映するというだけっぽい 3.7 How make Reads a Makefile - Variable Assignment