Makeのことはじめ


# Makeとは
Makefileと呼ばれるテキストファイルに依存関係を書き、`make`というコマンドでMakefileに書かれた内容を実行してくれる。
主にC言語やC++の様にコンパイルが必要な言語で利用する場面の多いビルドツール。

差分コンパイルをしてくれるので、コンパイルが必要な言語だととても助かる。
またワイルドカードも使えるというのもいい所。

# makefileの書き方


## makeをとりあえず試してみる
Makefileのフォーマットは

```
.PHONY ターゲット
ターゲット: 依存ファイル
	[実行コマンド]
```
この様な形になっている。
[実行コマンド]行の手前にもタブ1文字を入れることに注意!!

そしてこのルールを実行する場合ターゲットの部分がmakeコマンドを実行する時に指定をするだけです。

```shell
% make ターゲット
```

**.PHONY**はタスクターゲットを宣言するためのもので、書かなくても問題はないです。
ただ、ターゲット名と同名のファイルが存在する場合`Makefile`のターゲットではなくターゲットファイルの指定されたと認識されルールの実行をしてくれません。
なので`Makefile`だけで完結するターゲットを提供する場合 **必ず.PHONY**を書くぐらいの認識でいたほうが良いかもしれないです。
※.PHONYを書いたほうがパフォーマンスも良いらしい


例えばただ`echo`をしたいだけのルールを提供するなら

```Makefile
.PHONY output
output:
	echo "test"
```

この様に記載を行うだけで良い。
このファイルを用意したディレクトリで`make output`と実行すると

```
echo "test"	# makeの出力
test		　# echoの出力
```
この様な出力が行われる

## C++のビルドをしてみる
ざっくりとした`makefile`の記述はわかったのでこれで試しにC++のビルドをしてみる

```main.cpp
#include <iostream>

int main(int argc, char const* argv[])
{
    std::cout << "make test" << std::endl;
    return 0;
}
```
こんな簡単なC++プログラムを用意してこのビルドを行うターゲットを提供したい場合

```Makefile
.PHONY: debug
debug: main.cpp
	clang++ -std=c++14 -O3 -g main.cpp

.PHONY: release
release: main.cpp
	clang++ -std=c++14 -O0 -DNDEBUG main.cpp
```
この様な`Makefile`を提供すればdebugビルド、releaseビルド両方を提供できる。

## 依存関係のあるビルド
まずはファイル分割されたソースを用意

```main.cpp
#include <iostream>

#include "sub1.hpp"

int main(int argc, char const* argv[])
{
    std::cout << "make test" << std::endl;
    sub1();
#ifdef NDEBUG
    std::cout << "NO DEBUG" << std::endl;   
#else
    std::cout << "DEBUG" << std::endl;   
#endif
    return 0;
}
```

```sub1.cpp
#include <iostream>

#include "sub1.hpp"

void sub1()
{
    std::cout << "sub1" << std::endl;
}
```

```sub1.hpp
#pragma once

void sub1();
```
cppファイル１つのビルドはとても簡単でしたけど、これがファイル分割されていたプログラムの場合コマンドに全てのファイルのビルドをしてリンクするコマンドをかけばいいわけなんですけど

```Makefile
.PHONY: debug
debug: main.cpp sub1.cpp
	clang++ -std=c++14 -O3 -g main.cpp
	clang++ -std=c++14 -O3 -g sub1.cpp
	clang++ -o main main.o sub1.o
```
まだ２つで終わりなら良いけどここからsub2が増えたとかなったらビルドの行を追加して、実行ファイルのリンクの所編集してととても面倒！！

そこで **依存ファイルの指定** と **サフィックスルール** を使用してファイルが増える度に編集する箇所を減らしてやる。

### 依存ファイル指定
依存ファイル指定は`debug: main.cpp sub1.cpp`このターゲットの後に書かれた内容のことです。
要はビルドを行う為には**に依存するという意味になります。
この部分に別のターゲットを書くことで別のターゲットを呼び出すことも可能です。
正直`.cpp`ならば書く必要はないです。

C言語、C++で分割コンパイルをする場合必ず.oファイルを作ってそれをリンクして実行ファイルを作るという流れなので実行ファイルを作るためこの指定には`.o`を指定します。

### サフィックスルール
C言語、C++では必ず.cppから.oファイルが作られる，ということを利用し、ルール化したのがサフィックスルールです。
特殊なターゲットで

```
.cpp.o:
	コマンド
```
というのを用意します。
`.cpp.o:`というターゲットは，.oというファイルが必要になれば、これを.cppからつくる というルールである。


### 分割コンパイル
この２つを特性を利用してやると先ほどのルール記載がこの様に変わります。

```Makefile
.PHONY: debug
debug: main.o sub1.o
	clang++ main.o sub1.o

.cpp.o:
	clang++ -c -std=c++14 -O3 -g $< 
```

`debug`ターゲットは`main.o sub1.o`に依存する。
`.o`依存のターゲットが実行されるためmakeでは`.o`を生成するというフローに認識され`.cpp.o`を実行して`.o`を作るという流れになります。
`$<`は自動変数で`サフィックス.cpp`つまりコンパイル対象の１ファイル名になります。
これでビルドしてやることで実行ファイルが出来ますがまだこれだとファイルが増えた時に面倒ですね。
なので変数等を利用してdebug、releaseビルドの切り替えやファイル名の追加を楽にしてやります。


```Makefile
CC=clang++
SRCS=main.cpp sub1.cpp
EXE_FILE=exe_
CXXFLAGS=-c -std=c++14
OBJS=$(SRCS:.cpp=.o)
DEFINE=NDEBUG

.PHONY: debug
debug: EXE_FILE=exe_debug
debug:CXXFLAGS+=-g -O3 -Wall -W
debug: build

.PHONY: release
release: EXE_FILE=exe_release
release: CXXFLAGS+=-O0 -DNDEBUG
release: build

.PHONY: build
build: $(OBJS)
	$(CC) -o $(EXE_FILE) $(OBJS)

.cpp.o:
	$(CC) -c -std=c++14 $(CXXFLAGS) $< 

.PHONY: clean
clean:
	rm -f $(EXE_FILE)* $(OBJS)
```
最終的にはこの形になりました。
`SRCS`のラインにファイル名を追加してやるだけで追加できます。(ワイルドカード使ってやることでこの作業も省略可能)

##追記
上の最終的な形だとヘッダーを編集してもインクルードを行っているファイルでコンパイルしてくれないことがわかりました。
そのため分割コンパイルをする場合ヘッダーを編集した場合にもインクルード先のファイルをコンパイルしてくれる設定を追加する必要があります。

`-MMD` `-MP`オプションを使うことでこの解決は可能です。

・`-MMD`
依存関係のファイルリストを拡張子.dのファイルに保存してくれて、コンパイルを行ってくれる。

```
clang++ -MMD -std=c++11 -c main.cpp
```
先ほどのコードに`-MMD`を指定してコンパイルした場合

```main.d
main.o: main.cpp ../include/sub1.hpp
```

この様に`main.o`が依存しているファイルを列挙してくれます。

・`-MP`
依存するヘッダーファイルに偽のターゲットを追加してくれる。

これだけだとなんの意味があるのかわからないが、このオプションが効果を発揮するのはヘッダーファイルを消した時
`-MMD`オプションだけを指定した場合の`.d`ファイルを見ると`.o`オブジェクトが特定のヘッダーに依存するという形になる。
この依存ファイルをmakeが読み込むのが実行時に行われるため、ソースから`#include`を消しても次のコンパイルのタイミングではヘッダーファイルを消した場合でも必ず依存しているヘッダーファイルを探してしまう。
そのため消しても問題ないように`-MP`を付けて偽のターゲット定義をしてやることでヘッダーを探しても大丈夫なようにしてやる。

`-MP`オプションを追加するとこのような形になる。

```main.d
main.o: main.cpp sub1.hpp

sub1.hpp:
```

・依存ファイルをmakeに教えてやる
`-MMD` `-MP`を利用することで依存ファイルができたが、これだけだとmakefileで依存関係が判明してないためそれを教えてやる必要がある。

そこで利用するのがmakefileの`include`を利用してやる。
includeはmakeに現在のmakefileの読み込みを中断させ、続ける前に一つないしそれ以上の他のmakefileを読ませます。
これで生成された`.d`ファイルを読みこませれば依存関係の解消が行え、ヘッダーを変更しただけでもコンパイルされます。

これを踏まえたMakeファイルはこんな感じになりました。

```Makefile
CC=clang++
SRCS=main.cpp sub1.cpp
DEPS=$(SRCS:.cpp=.d)
EXE_FILE=exe_
CXXFLAGS=-c -std=c++14
OBJS=$(SRCS:.cpp=.o)
DEFINE=NDEBUG

-include $(DEPS)

.PHONY: debug
debug: EXE_FILE=exe_debug
debug:CXXFLAGS+=-g -O3 -Wall -W
debug: build

.PHONY: release
release: EXE_FILE=exe_release
release: CXXFLAGS+=-O0 -DNDEBUG
release: build

.PHONY: build
build: $(OBJS)
	$(CC) -o $(EXE_FILE) $(OBJS)

.cpp.o:
	$(CC) -c -MMD -MP -std=c++14 $(CXXFLAGS) $< 

.PHONY: clean
clean:
	rm -f $(EXE_FILE)* $(OBJS)
```

`-include $(DEPS)`とインクルードの前に`-`を付けてます。
これは最初のコンパイルの時に`.d`ファイルが存在しないしコンパイルをして初めて生成されるファイルのため初回は探しても見つけることも創りだすこともできません。
そこで`-include`としてやることでそういったファイルを無視してくれます。

###参考
[Makeでヘッダファイルの依存関係に対応する](http://wagavulin.hatenablog.com/entry/20120405/1333629926)


# 感想
ざっくりmakeを触って見たが正直C++のmakefile書くならcmakeのが構文的でわかりやすいなという感想
1ファイルだけコンパイルしたいなら普通に`clang++`だけでやる方が楽だしxcodeプロジェクトも作れるし
使う候補で考えるとGO言語かなという感じ