Makefile で {単数 | 複数} の {依存項目 | ターゲット} を指定するルールの書き方


Makefile で複数の依存項目 (prerequisite; 以降 prereq と表記) や複数のターゲット (target) を扱う場合のまとめ。複数の場合でも基本的に単数と同様に書けるが、中身を個別に扱う (multiple [foreach] と表記) 場合にはまとめて扱う (multiple [bulk] と表記) 場合と異なる書き方をしないといけない、という話。

自動変数 ($@, $^, $< など)、組み込み関数、VPATH 等の詳細についてはここでは触れない。

prereq↓ \ target→ single multiple [bulk] multiple [foreach]
single 通常のルール 複数ターゲット 複数ターゲットで\$@使用
multiple [bulk] 複数依存項目 複数依存項目/ターゲット 複数依存項目/ターゲットで\$@使用
multiple [foreach] (用途なし) (用途なし) パターンルール or 組み込み関数

以下、各項目の具体的な説明。

single → single

single→single
target: prereq
    command $^ > $@

通常の Makefile ルール。

single → multiple [foreach]

同じコマンドを同じ依存項目に適用して複数のターゲットを生成するのは無意味(同じファイルを複数出力することになるから)なので、ターゲットに応じてコマンドが変わる場合についてのみ述べる。

各ターゲット名とコマンドに関連がある場合は、自動変数$@を使えばよい。例えば

single→multiple[foreach]_case1
target1 target2: prereq
    echo $@ $^ > $@

target1: prereq
    echo target1 prereq > target1

target2: prereq
    echo target2 prereq > target2

と同じになる。各ターゲット名とコマンドに関連がない場合は、仕方ないので最初からそれぞれ別のルールを書く(これはターゲットが multiple [foreach] であるような他のすべてのケースでも同じなので、以降は省略する)。

single→multiple[foreach]_case2
target1: prereq
    echo hoge $^ > $@

target2: prereq
    echo huga $^ > $@

single → multiple [bulk]

1 回のコマンド実行で複数のファイルが生成されるような場合。これは単に通常のルールのターゲットを複数にすればよい (ただし、自動変数 $@ を使うと上記の single → multiple [foreach] で解釈されてしまうので注意)。

[コメントにて内容の誤りを指摘していただいたのでそちらを参照。]

multiple [bulk] → single もしくは multiple [foreach] もしくは multiple [bulk]

複数の依存項目を一度にコマンドに与えるケースは自然によく起こりうるものであり、これらについては依存項目が 1 つだけ (single) だったものをそのまま複数にすればよい。自動変数 $^ を使える場合はコマンドは single の場合と同じになる:

multiple[bulk]→single
target: prereq1 prereq2
    echo $^ > $@

もしくは

multiple[bulk]→multiple[foreach]
target1 target2: prereq1 prereq2
    echo $@ $^ > $@

もしくは

multiple[bulk]→multiple[bulk]
target1 target2: prereq1 prereq2
    command $^

multiple [foreach] → single もしくは multiple [bulk]

複数の依存項目を別々に扱うのにターゲットが 1 つに固定されてしまっており、ナンセンスなのでこれらは用途なし。

multiple [foreach] → multiple [foreach]

複数の依存項目に対して別々に、同一のもしくはパターン化可能なコマンドを適用したい場合。

「ある拡張子を持つような全てのファイルに対して同一の処理を行いたい」というような場合にはパターンルール (型ルール) が便利。例えば .c ファイルから .o ファイルを生成する

multiple[foreach]→multiple[foreach]_case1
%.o: %.c
    $(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@

は GNU Make のビルトイン型ルールとなっていて、最初から使用できる。

そうではなく、「特定のいくつかのファイルに対して同一の処理を行いたい」場合には、foreach 組み込み関数とマクロを使用する。例えば、

multiple[foreach]→multiple[foreach]_case2
ORIGINAL_DIR = from/
OBJS = a b c

define COPY
$(1): $(2)
    cp $(2) $(1)
endef

$(foreach OBJ, $(OBJS), $(eval $(call FUNC, $(OBJ), $(addprefix $(ORIGINAL_DIR), $(OBJ)))))

というルールでは、from/ ディレクトリにある 3 つのファイル a, b, c をそれぞれカレントディレクトリにコピーするルールを生成する。読みにくいので、ファイル名に規則性があり上記のパターンルールで書ける場合はなるべくそちらで書く。