【make/Makefile】「なにそれ？おいしいの？」状態からの脱却

この記事では，超基本的な`Makefile`の作り方を解説していきます．

##make/Makefileとは？##
`make`は主にコンパイラ型言語のプログラムをビルドするために使用されるコマンドです．

```bash:bash
$ make
```
とするだけで勝手にソースコードから実行ファイルが作成されます．
もちろん，ソースコードだけ作成して`make`コマンドを実行すれば勝手に実行ファイルが作成されるような魔法のコマンドではありません．
どのようにソースファイルをビルドするのかを記述したファイルが必要です．
それが`Makefile`と呼ばれるファイルです．

##makeを使うメリット##
`Makefile`を記述する必要があるのなら，`make`コマンドを使用する利点はどこにあるのでしょうか？
利点を説明する前に少しc言語のビルドについて考えてみましょう．
ソースファイルから実行可能なファイルを作るまでにはいくつかの手順があります．
簡単に説明すると，以下のようになります．

手順①プリプロセス
`#define`で宣言されたマクロの展開など，プロプロセッサ命令を処理します．機械語に変換するための前処理ですね．

手順②コンパイル
ソースファイルをアセンブリ言語に変換します．`gcc`では，`-S`オプションを指定すると，アセンブリで書かれたファイルが生成されます．

手順③アセンブル
アセンブリで書かれたファイルをコンピュータが理解できる機械語に変換します．`gcc`では，`-c`オプションを指定すると，機械語で書かれたオブジェクトファイルが生成されます．

手順④リンク
複数のオブジェクトファイルを連結し，実行可能ファイルを作成します．

これで`make`を使う利点を説明する準備ができました．
複数のソースファイルを扱うプロジェクトを想像してください．
一部のファイルに変更を加えただけで，変更していないファイルについても再度オブジェクトファイルを生成してからリンクしていたのではリソースの無駄となります．かと言って，変更を加えるたびにそのファイルを控えておいて手動でビルドするのは手間ですし，ミスが起こる原因ともなります．
そこで，`Makefile`に一度ビルドのルールを記述しておけばその後，`make`を実行するだけで変更を加えたファイルのみオブジェクトファイルを作り直し，リンクして，実行可能ファイルを自動で作成してくれます．
大きいプロジェクトになればなるほど，`make`のメリットは大きくなるでしょう．

##Makefileを書いてみよう##
まずは，一番基本的な`Makefile`を作ってみましょう．
今回は以下のプログラムをビルドします．

```c:hello_world.c
#include <stdio.h>

int main(void){
    printf("Hello, World!\n");
    return 0;
}
```

これをビルドして，`hello_world`という名前の実行ファイルを作成する`Makefile`は以下のようになります．

```makefile:Makefile
hello_world: hello_world.c
	gcc hello_world.c -o hello_world
```
ソースファイルと`Makefile`を同じディレクトに置いて，そのディレクトリで`make`コマンドを使って見ましょう．

```bash:bash
$ make
gcc hello_world.c -o hello_world
```

上のようになり，実行ファイル`hello_world`が作成されていると思います．
ファイルの中身を変更せずにもう一度`make`コマンドを実行するとどうなるでしょうか？

```bash:bash
$ make
make: 'hello_world' is up to date.
```
ファイルに変更がないため，`hello_world`という実行ファイルがもう一度作られることはありません．
次に，ソースファイルの中身を書き換えてから，`make`コマンドを実行してみましょう．

```bash:bash
$ make
gcc hello_world.c -o hello_world
```
すると，今度は`gcc`コマンドが実行され，`hello_world`が作り直されています．
このように`make`コマンドは，実行ファイルが作成された後にソースファイルが変更されたかを確認してビルドするかどうかを決めていることがわかります．


次に，`Makefile`の書き方を解説します．

```makefile:Makefile
ターゲット名: 依存関係
[　Tab　]コマンド
```
`ターゲット名`にはこれから作成するファイル名を書きます．
`依存関係`にはターゲットを作成するために必要なファイルを書きます．
`コマンド`にはターゲットを作成するためのコマンドを記述します．
注意したいのは，コマンド行の先頭にはタブを入れなければならないことです．
そんなに難しくはないですね．

##複数ファイルのMakefile##
次は，ソースファイルが複数ある場合の`Makefile`の書き方です．
ディレクトリ構成は以下のようになっているとします．

```:ディレクトリ構成
______________ test1.c
       |______ test2.c
       |______ test3.c
       |______ Makefile
```

この場合の`Makefile`は以下のようになります．

```makefile:Makefile
test: test1.o test2.o test3.o
    gcc test1.o test2.o test3.o -o test

test1.o: test1.c
    gcc -c test1.c

test2.o: test2.c
    gcc -c test2.c

test3.o: test3.c
    gcc -c test3.c
```

`test`という実行ファイルを作成するルールの他に，`test`を作成するための依存ファイルになっているオブジェクトファイルを作成するルールも記述されています．
`make`コマンドを実行すると，`make`はまず，`test`を作成するために`test1.o`, `test2.o`, `test3.o`を探します．
見つからない場合，それらを作成するルールが`Makefile`内に記述されていないかを探します．
今回の場合，オブジェクトファイルを作成するルールが存在するので，そのルールに従って，オブジェクトファイルが作成されます．
その後，オブジェクトファイルを用いて，目的であった`test`を作成します．

実際に`make`コマンドを実行してみましょう．

```bash:bash
$ make
gcc -c test1.c
gcc -c test2.c
gcc -c test3.c
gcc test1.o test2.o test3.o -o test
```
`test`が作成されました．
`test2.c`だけを編集して，もう一度`make`を実行すると以下のようになります．

```bash:bash
$ make
gcc -c test2.c
gcc test1.o test2.o test3.o -o test
```
`test2.c`に関数オブジェクトファイルだけが作り直されているがわかったと思います．


##おわりに##
このように，複数ファイルがある場合でもルールを追加していけば`Makefile`を作ることが出来ます．
しかし，もっとファイルが多くなった場合に`Makefile`を作成するのが面倒に思えてくるでしょう．
実は，`Makefile`には変数やマクロを使用したもっと効率的な書き方があります．
しかし，それを解説すると長くなるので，追々書いていきたいと思います．