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

<img width="128" alt="スクリーンショット 2020-03-25 15.42.32.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81825/bc582042-ca2d-76b3-573d-a3f2e196ae92.png">

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

通常の Makefile ルール。

## single → multiple [foreach]

<img width="132" alt="スクリーンショット 2020-03-25 15.42.40.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81825/4a51cc59-a851-0b19-9240-4aa2464ae8bc.png">

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

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

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

は

```make
target1: prereq
	echo target1 prereq > target1

target2: prereq
	echo target2 prereq > target2
```

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

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

target2: prereq
	echo huga $^ > $@
```

## single → multiple [bulk]

<img width="160" alt="スクリーンショット 2020-03-25 15.42.47.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81825/3ade17fe-35ea-8d3f-a2fb-5d84d8549a60.png">

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

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


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

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

<img width="158" alt="スクリーンショット 2020-03-25 15.42.57.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81825/d1cd9ac4-6a0a-ed11-492c-c6b28c102fd7.png">

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

もしくは

<img width="163" alt="スクリーンショット 2020-03-25 15.43.02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81825/ab6b6cb3-28b3-f71b-ba42-ca1fbf6079f3.png">

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

もしくは

<img width="192" alt="スクリーンショット 2020-03-25 15.43.09.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81825/6c594675-a547-0d11-92e5-8c756a186bdf.png">

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

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

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

## multiple [foreach] → multiple [foreach]

<img width="140" alt="スクリーンショット 2020-03-25 15.43.15.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81825/c3117f44-6c0f-7d25-d77d-064d0f3c75dd.png">

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

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

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

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

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

```make: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` をそれぞれカレントディレクトリにコピーするルールを生成する。読みにくいので、ファイル名に規則性があり上記のパターンルールで書ける場合はなるべくそちらで書く。