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