打倒Makefile!

# Makefileとはなんぞや
MakefileはC言語などで面倒なコンパイルや実行、ファイル操作などを自動化してくれます。

例えば、a.c b.c c.cというファイルをまとめてコンパイルして、その実行ファイル名をhogeにして実行し、その後実行ファイルを削除する、という一連の流れを行おうとすると、
```zsh
gcc a.c b.c c.c -o hoge
./hoge
rm -rf hoge
```

のように毎回3回もコマンドを打つ必要が出てきます。
面倒ですね...

ところがMakefileを使うと、
```Makefile
run: 
    gcc a.c b.c c.c -o hoge
    ./hoge
    rm -rf hoge
```
とMakefileに記述し、`make run`を実行すると、上のコマンドと同様なことを実行してくれます。

### Makefileのメリット
- コンパイルや実行、ファイル操作などのコマンドを自動化してくれる。
- 他の人がコンパイルする際に、必要なファイルやオプションなどを知る必要がなく実行できる。

# Makefileの書き方
## 初級編
### Makefileの基本構造
Makefileは主に二つの構造から成り立っています。
一つは実行ファイル名やコンパイルしたいファイルをまとめた変数などを設定する部分。
```Makefile
SRCS=a.c b.c c.c
NAME=hoge
```
もう一方は、コンパイルや実行、ファイル操作などをまとめるルールを記述する部分です。
```Makefile
run:
    gcc a.c b.c c.c -o hoge
    ./hoge
    rm -rf hoge
```

### ルールの記述
ルールの基本構造は以下のようになっています。
```Makefile
[ターゲット]: [依存ファイル]
         [実行したいコマンド]
```
`依存ターゲット`については中級編でお話しするため一旦忘れてください。
`ターゲット`は生成したいファイル名や、動作名(.oファイルを消したかったらcleanなど)を書くことが多いです。実際にそのターゲットのルールを実行したい場合には`make [ターゲット]`で実行できます。
`実行したいコマンド`にはターゲットを実行した際に行ってほしい、コンパイルや実行、ファイル操作などのコマンドを記述します。`Tab`で開けて記述することに注意してください。

### 変数の設定
ここでは、実行ファイル名やコンパイルしたいファイル、フラグなどを変数として設定することができます。
基本的にな構造としては左辺が`変数名`、右辺が`変数の定義`となっています。
```Makefile
[変数名]: [変数の定義]
```
例えば以下のようなMakefileがあったとします。
```Makefile
run:
    gcc a.c b.c c.c -o hoge -Wall
    ./hoge
    rm -rf hoge
```
このMakefileに対して変数を設定すると以下のようになります。
```Makefile
CC=gcc
SRCS=a.c b.c c.c
Name=hoge
CFLAGS=-Wall
RM=rm -rf

run:
    $(CC) $(SRCS) -o $(NAME) $(CFLAGS)
    ./$(NAME)
    $(RM) $(NAME)
```
あれ？見づらくなった...？
僕も初めて変数を使ったMakefileを見た時同じことを思いました。
結論から言ってしまうと慣れます。
Makefileには暗黙のルールというのが存在しているため、ソースファイルは`SRCS`という変数名にして定義するという慣習があります。
なのでMakefileを何度も見ていくうちに変数名から大体何をやっているかわかるようになります。
また、42個ものソースファイルがあったとして、それをルールに全て書かれると、どこまでがソースファイルなのかわかりにくくなってしまいます。
慣れてくるとコンパイルされているファイル名を知ることなく、このターゲットで何が行われているかが一眼でわかるのでとても良いです。

## 中級編
### 依存ファイル
依存ファイルは初級編の部分で少しお話しした、ターゲットの隣に記述するものです。
```Makefile
[ターゲット]: [依存ファイル]
         [実行したいコマンド]
```
依存ファイルはターゲットを実行する際に必要なファイルを記述します。
例えば以下のような例では`hoge`という実行ファイルを作るためには、`hoge.o`, `hoge2.o`という二つのファイルが必要であるため、`make hoge`を実行すると、hogeのコマンドを実行する前に`hoge.o`, `hoge2.o`のターゲットが実行されます。
```Makefile
hoge: hoge.o hoge2.o
    gcc -o hoge hoge.o hoge2.o

hoge.o:
    gcc -c hoge.c -o hoge.o

hoge2.o:
    gcc -c hoge2.c -o hoge2.o
```

```bash
$ make hoge
gcc    -c -o hoge.o hoge.c
gcc    -c -o hoge2.o hoge2.c
gcc -o hoge hoge.o hoge2.o
```

### 動的マクロ
- `$@` 現在のターゲットの名前
- `$?` ターゲットよりも新しい依存関係のリスト
- `$<` make が暗黙の規則を使用するときの依存ファイル名
- `$*` 接尾辞を除いたターゲット名
- `$%` 処理されるライブラリメンバーの名前
- `$^` 依存ファイルのリスト

具体的な使い方についてはこちらの記事がよくまとまっていました。

https://zenn.dev/keitean/articles/aaef913b433677#%E3%83%AB%E3%83%BC%E3%83%AB%E5%86%85%E3%81%A7%E6%A9%9F%E8%83%BD%E3%81%99%E3%82%8B%E8%87%AA%E5%8B%95%E5%A4%89%E6%95%B0

### パターンルール
c言語ではソースファイル(.c)をコンパイルしてオブジェクト(.o)を出力させるのが一般的です。
```c
gcc -c a.c => a.oというオブジェクトファイルを生成
gcc -c b.c
gcc -c c.c
cc -o hello a.o b.o c.o => helloというプログラムを生成
```
これを42個ものファイルに対して書くのは大変です...
そこでMakefileではオブジェクトファイル(.o)を同一名のソースファイル(.c)から生成する際に、`%.o: %.c`というターゲットを用意することでこれらの処理をまとめて行うことができます。

### 特殊変数
変数を設定することができることはお伝えしましたが、デフォルトで変数が用意されているものもあります。それらは自分で変数の定義を明記することなく、デフォルトの値で呼び出すことができます。以下に代表的な4つの変数です。
- `$(CC)` Cのコンパイルを行う。(デフォルトは`cc`)
- `$(RM)` ファイルの削除を行う。(デフォルトは`rm -f`) 
- `$(AR)` アーカイブの作成、更新、および、アーカイブからの抽出を行う。(デフォルトは`ar`)
- `$(CFLAGS)` Cのコンパイラに与えるフラグ(デフォルトは無し)

### 暗黙のルール
```Makefile
hoge: hoge.o hoge2.o
    cc -o hoge hoge.o hoge2.o
```
というようなMakefileがあった場合に、hoge.oは依存ターゲットとして記述されていますが、このMakefile上にはそのターゲットが存在していません。このような場合に、Makefileでは暗黙のルールを参照します。
C言語の場合だと、`$(CC) -c $(CPPFLAGS) $(CFLAGS)`が暗黙のルールとして適用され、.cから.oを生成します。
つまり以下のように記述されていることと同義だと言えます。
```Makefile
hoge: hoge.o hoge2.o
    cc -o hoge hoge.o hoge2.o

hoge.o:
    $(CC) -c $(CPPFLAGS) $(CFLAGS) hoge.c -o hoge.o

hoge2.o:
    $(CC) -c $(CPPFLAGS) $(CFLAGS) hoge2.c -o hoge2.o
```

### その他
- ターゲットを指定せずにmakeを実行すると、一番上のターゲットが実行される。
```Makefile
run:
   gcc a.c b.c c.c -o hoge
hoge2:
   echo "これはでない"
```
```bash
$ make
gcc a.c b.c c.c -o hoge
```

## 上級編
### relink
relinkとは、実行ファイルなど生成したいファイルが既に存在しているにも関わらず、コンパイルやアーカイブファイルの作成を実行の度に行ってしまうことです。
```Makefile
run:
   gcc a.c b.c c.c -o hoge
```

```bash
$ make
gcc a.c b.c c.c -o hoge
$ make
gcc a.c b.c c.c -o hoge
```
上記のような場合ですと、1回目のmakeでhogeという実行ファイルが作成されたにも関わらず、2回目でも全く同様に実行ファイルを生成してしまっています。
これはいけないことなのでしょうか？
答えはもちろんYesです。例えば420個もの大量ななソースファイルがあるとして、変更差分がないにも関わらず、それを毎回コンパイルするのは効率がいいとはとても言えません。
Makefileではターゲット名と`生成ファイル名を同一にする`ことで、relinkを起こさないように対策をすることができます。

```Makefile
hoge:
    gcc a.c b.c c.c -o hoge
```
```bash
$ make
gcc a.c b.c c.c -o hoge
$ make
make: `hoge` is up to date.
```
上記のような場合ですと、1回目のmakeでhogeという実行ファイルが作成されるのは同様ですが、2回目は既にhogeが存在しているため、コンパイルが実行されませんでした。
ソースファイルに変更があった場合には、再度生成が行われます。
```bash
$ make
gcc a.c b.c c.c -o hoge
$ make
make: `hoge` is up to date.
$ touch a.c
$ make
gcc a.c b.c c.c -o hoge
```

# 最後に
ざっくりとMakefileについてお伝えしてきましたが、この記事だとMakefileの半分も説明できてないぐらいMakefileが奥が深いです。
Makefileは知れば知るほど便利になってくるので、これを機に皆さんも色々と勉強して見ましょう！

## 参考文献
https://tex2e.github.io/blog/makefile/automatic-variables

https://docs.oracle.com/cd/E19620-01/805-5827/6j5gfranf/index.html

https://zenn.dev/keitean/articles/aaef913b433677