Makefileを使ってみよう

# 結論
## この分かりやすい概念図
Makefile
![Gazo](https://github.com/muzudho/HowToMakeGame/blob/master/img/2017-03/201703212237a1.png?raw=true)

コマンドライン実行
![Gazo](https://github.com/muzudho/HowToMakeGame/blob/master/img/2017-03/201703212237a2.png?raw=true)

Makefile
![Gazo](https://github.com/muzudho/HowToMakeGame/blob/master/img/2017-03/201703212237a3.png?raw=true)

これを作るには　何が必要で、どうやって作る、と並べる。
名前通り　メイク・ファイルって感じ。

## 例外

Makefile
![Gazo](https://github.com/muzudho/HowToMakeGame/blob/master/img/2017-03/201703212237a4.png?raw=true)
**つくりかた** が無いやつや、 **必要なもの** がないやつもいる。
こういうやつは .PHONY といって、そのうち出てくる。

# 今回の記事が始まる
## 使用環境

|抽象度0|抽象度-1|抽象度-2|
|---|---|---|
|OS|Linux|Ubuntu 16.04|
|使用言語|C++||
|コンパイラー|G++||
|コンパイラー・ユーティリティー|Makefile||

なんだこの表。

で、こんな感じでフォルダ構成を作った。

```
t37a1.d/
    |
    +---tamesi37a1.cpp
    |
    +---tamesi37a1_msgq.cpp
    |
    +---tamesi37a1_msgq.hpp
```

依存関係はこんな感じ。

```
tamesi37a1_msgq.hpp
    ^
    |
    +---tamesi37a1.cpp
    |
    +---tamesi37a1_msgq.cpp
```

## 結論から言うと

Makefile

```Makefile
.PHONY: all clean

all: tamesi37a1.exe

tamesi37a1_msgq.o: tamesi37a1_msgq.cpp tamesi37a1_msgq.hpp
        g++ -std=c++11 -c tamesi37a1_msgq.cpp -o tamesi37a1_msgq.o -lev -lamqpcpp -pthread

tamesi37a1.o: tamesi37a1.cpp tamesi37a1_msgq.hpp
        g++ -std=c++11 -c tamesi37a1.cpp -o tamesi37a1.o -lev -lamqpcpp -pthread

tamesi37a1.exe: tamesi37a1.o tamesi37a1_msgq.o
        g++ -std=c++11 tamesi37a1.o tamesi37a1_msgq.o -o tamesi37a1.exe -lev -lamqpcpp -pthread

clean:
        rm -f tamesi37a1.d tamesi37a1.o tamesi37a1_msgq.o tamesi37a1.exe tamesi37a1.out.log tamesi37a1.err.log
```

こう書けば　よかったわけなんだが、そんなん初見で分からんし。

## 今回の記事・再
今までは 他人の書いた Makefile が既にあって、

```
make profgen_sse
```

とか書けば G++ がコンパイルを進めてくれて楽だった。
これからは 自分で Makefile が書けないと不便そうだ。

えっ、統合開発環境？ GUI？ 何それ、知らないぜ。

自分が書いたプログラム・ファイルが１つだけなら

```
g++ -std=c++11 tamesi37a1.cpp -o tamesi37a1.exe -lev -lamqpcpp -pthread
```

とでも打鍵すれば　コンパイルしてくれて楽だった。
じゃあ Makefile の使い方を調べていくか。

（トリビアなmakefile入門）
http://www.jsk.t.u-tokyo.ac.jp/~k-okada/makefile/

# フォルダーと Makefile
make コマンドは勝手に Makefile を開いてくれる。ということは、1つのフォルダーの中には 1つのMakefile しか置けないのか？

フォルダーに入ったり出たりするのは不便だな……。

# make コマンド
make コマンドと言えば、

```
make なんか名前
```

と叩くものだった。この　なんか名前　のことを **ターゲット** と呼ぶのらしい。

他人の作った Makefile を眺めてみると、

```Makefile
なんか名前:
	何か

なんか名前:
	何か

なんか名前:
	何か

なんか名前:
	何か
```

といった部分がある。ところで今一つ信じられない話しだが 半角スペースと **タブ** は使い分ける必要があるかもしれない。

もうこれからは なんか名前 と呼ばずに ターゲット と呼ぶぞ。

```Makefile
ターゲット:
	何か

ターゲット:
	何か

ターゲット:
	何か

ターゲット:
	何か
```

## clean
よく使うコマンドに

```
make clean
```

がある。浮かむ瀬の Makefile を見てみると

```
clean:
	rm -f $(OBJECTS) $(DEPENDS) $(TARGET) ${OBJECTS:.o=.gcda}
```

といった感じで　ターゲットと、その下に何かがある。この書き方を覚えれば良さそうだ。

じゃあ、

Makefile

```
tamesi37a1: tamesi37a1_msgq.hpp tamesi37a1_msgq.cpp
	g++ -std=c++11 tamesi37a1.cpp -o tamesi37a1.exe
```

こんな書き方でいいのか？ 試してみよう。

```
# make tamesi37a1
g++ -std=c++11 tamesi37a1.cpp -o tamesi37a1.exe
In file included from tamesi37a1.cpp:88:0:
tamesi37a1_msgq.hpp: In lambda function:
tamesi37a1_msgq.hpp:206:30: error: passing ‘const rotationBuffer::RotationBuffer’ as ‘this’ argument discards qualifiers [-fpermissive]
    rotaBuf.push_block(message);
                              ^
```

あっ！　コンパイル・エラーが出てきた！
まず　そっちを直そう……。

# -MMD
依存しているヘッダ・ファイルなんか読んでくれてなさげ。

「依存関係の自動解決（第二回）」（ソフトウェアまわりの備忘録）
http://gmaj7sus4.hatenablog.com/entry/2013/11/15/163242

なんだか便利そうなので付けておこう。

```
tamesi37a1: tamesi37a1_msgq.hpp tamesi37a1_msgq.cpp
        g++ -std=c++11 -MMD tamesi37a1.cpp -o tamesi37a1.exe -lev -lamqpcpp -pthread
```

こうする。

```
# make tamesi37a1
g++ -std=c++11 -MMD tamesi37a1.cpp -o tamesi37a1.exe -lev -lamqpcpp -pthread
/tmp/ccH1NL7t.o: In function `main':
tamesi37a1.cpp:(.text+0x47e): undefined reference to `msgq::Settings::Settings()'
tamesi37a1.cpp:(.text+0x49c): undefined reference to `msgq::Settings::parseCommandlineArgs(int, char**)'
tamesi37a1.cpp:(.text+0x4cb): undefined reference to `msgq::Settings::GetQueueName[abi:cxx11](int)'
tamesi37a1.cpp:(.text+0x4df): undefined reference to `msgq::Settings::GetQueueLifeSpan(int)'
tamesi37a1.cpp:(.text+0x649): undefined reference to `msgq::Settings::~Settings()'
tamesi37a1.cpp:(.text+0x705): undefined reference to `msgq::Settings::~Settings()'
collect2: error: ld returned 1 exit status
Makefile:2: recipe for target 'tamesi37a1' failed
make: *** [tamesi37a1] Error 1
```

ぶへっ。

.text+0x47e とか出ているが行数ではないのか。

「静的ライブラリのリンク時にundefined referenceエラーが出る(gcc)」（gtkmm 入門）
http://www.hakodate-ct.ac.jp/~tokai/tokai/gtkmm/etc/p1.htm

さっぱり分からん。こうか。

```
tamesi37a1: tamesi37a1_msgq.cpp tamesi37a1_msgq.hpp
	g++ -std=c++11 -MMD tamesi37a1.cpp -o tamesi37a1.exe -lev -lamqpcpp -pthread
```

変わんね。

「C/C++でundefined reference toエラーが出る場合」（なんとな～くしあわせ？の日記）
http://nantonaku-shiawase.hatenablog.com/entry/20120107/1325961201

```
LDFLAGS = -lev -lamqpcpp -pthread

tamesi37a1: tamesi37a1_msgq.cpp tamesi37a1_msgq.hpp
	g++ -std=c++11 -MMD tamesi37a1.cpp -o tamesi37a1.exe $(LDFLAGS)
```

じゃあ、こうか？

```
# make tamesi37a1
Makefile:4: *** missing separator.  Stop.
```

違うようだ。

```
LDFLAGS = -lev -lamqpcpp -pthread

tamesi37a1: tamesi37a1_msgq.cpp tamesi37a1_msgq.hpp
	g++ -std=c++11 -MMD tamesi37a1.cpp -o tamesi37a1.exe CFLAGS='$(LDFLAGS)'
```

じゃあこうか？

```
# make tamesi37a1
Makefile:4: *** missing separator.  Stop.
```

ちがうようだ。
半角スペースをタブに変えてみる。

```
# make tamesi37a1
g++ -std=c++11 -MMD tamesi37a1.cpp -o tamesi37a1.exe CFLAGS='-lev -lamqpcpp -pthread'
g++: error: CFLAGS=-lev -lamqpcpp -pthread: No such file or directory
Makefile:4: recipe for target 'tamesi37a1' failed
make: *** [tamesi37a1] Error 1
```

うーむ。

```
tamesi37a1: tamesi37a1_msgq.cpp tamesi37a1_msgq.hpp
	g++ -std=c++11 -MMD tamesi37a1_msgq.cpp -o tamesi37a1_msgq.o -lev -lamqpcpp -pthread
	g++ -std=c++11 -MMD tamesi37a1.cpp -o tamesi37a1.exe -lev -lamqpcpp -pthread
```

これでどうか？

```
# make tamesi37a1
g++ -std=c++11 -MMD tamesi37a1_msgq.cpp -o tamesi37a1_msgq.o -lev -lamqpcpp -pthread
tamesi37a1_msgq.cpp: In function ‘std::__cxx11::string getTimeslipNow()’:
tamesi37a1_msgq.cpp:11:35: error: ‘std::__cxx11::string getTimeslipNow()’ was declared ‘extern’ and later ‘static’ [-fpermissive]
 static std::string getTimeslipNow()
                                   ^
```

エラーが増えた。

「C++のための即席Makefile」（簡潔なQ）
http://qnighy.hatenablog.com/entry/20100117/1263729044

分からん。

```
.PHONY: all clean
.SUFFIXES: .cpp .o

all: hello.exe

clean:
	$(RM) hello.exe

.cpp.o:
	g++ -O2 -Wall -o $@ -c $^
.cpp:
	g++ -O2 -Wall -o $@ $^ -lm
```

説明を読むと　こういう構成かと思うが分からん。

# ハローワールド

study.cpp

```
// #pragma once
// #include "stdafx.h"

#include <iostream> // <<
#include <string> // std::getline

int main()
{
	std::cout << "hello, world" << std::endl;

	std::cout << "Please, push any key." << std::endl;
	std::string unuse;
	std::getline(std::cin, unuse);
	return 0;
}
```

これを Makefile でコンパイルしてみよう。

単純にコンパイルするだけなら

```
g++ -std=c++11 study.cpp -o study.exe
```

だが

Makefile

```Makefile
all:
	g++ -std=c++11 study.cpp -o study.exe
```

と書いて

```
make
```

とだけ打鍵すると　同じく実行ファイルは作られた。

```
# ./study.exe
hello, world
Please, push any key.

```

# ハローワールド2

Student.hpp

```
#ifndef STUDENT_HPP
#define STUDENT_HPP
//#pragma once

#include <string>

class Student
{
	std::string name;
public:
	Student();
	~Student();

	std::string getName();
};

#endif // STUDENT_HPP
```

Student.cpp

```
#include "stdafx.h"
#include "Student.hpp"


Student::Student()
{
	name = "Muzudho";
}


Student::~Student()
{
}

std::string Student::getName()
{
	return name;
}
```

main.cpp

```
// g++ -std=c++11 main.cpp -o main.exe

#pragma once
#include "stdafx.h"

#include <iostream> // <<
#include <string> // std::getline

#include "Student.hpp"

int main()
{
	Student student;

	std::cout << "hello, world. name=[" << student.getName() << "]" << std::endl;

	std::cout << "Please, push any key." << std::endl;
	std::string unuse;
	std::getline(std::cin, unuse);
	return 0;
}
```

この何の面白味もないファイルのコンパイルに挑戦しよう。

コメントをもらっている。

まず Student.cpp と Student.hpp を合体させて Student.o を作り、
次に main.cpp と Student.hpp を合体させて main.o を作り、
最後に Student.o と main.o を合体させて main.exe を作ったらいいのだろうか？

```Makefile
Student.o: Student.cpp Student.hpp
	g++ -std=c++11 -c Student.cpp -o Student.o

main.o: main.cpp Student.hpp
	g++ -std=c++11 -c main.cpp -o main.o

main.exe:
	g++ -std=c++11 main.o Student.o -o main.exe
```

こんなんで　いいんだろうか？

```
# make
make: *** No rule to make target 'Student.cpp', needed by 'Student.o'.  Stop.
```

なんのこっちゃ。

じゃあ、こうか？

```
# make main.exe
g++ -std=c++11 main.o Student.o -o main.exe
g++: error: main.o: No such file or directory
g++: error: Student.o: No such file or directory
g++: fatal error: no input files
compilation terminated.
Makefile:8: recipe for target 'main.exe' failed
make: *** [main.exe] Error 1
```

こうでもない。

```
# ls
main.cpp  Makefile  Stident.cpp  Student.hpp
```

スチイデントがいるな。

```
# mv Stident.cpp Student.cpp
# make
g++ -std=c++11 -c Student.cpp -o Student.o
Student.cpp:1:20: fatal error: stdafx.h: No such file or directory
compilation terminated.
Makefile:2: recipe for target 'Student.o' failed
make: *** [Student.o] Error 1
```

stdafx.h は コメントアウトするか。

```
# make
g++ -std=c++11 -c Student.cpp -o Student.o
# ls
main.cpp  Makefile  Student.cpp  Student.hpp  Student.o
```

Student.o は作られたが……。

「Makefileの書き方に関する備忘録」（minus9d's diary）
http://minus9d.hatenablog.com/entry/20140203/1391436293

うーむ。

```
# make main.o
g++ -std=c++11 -c main.cpp -o main.o
main.cpp:3:9: warning: #pragma once in main file
 #pragma once
         ^
```

#pragma もコメントアウトしておこう。

```
# make main.o
g++ -std=c++11 -c main.cpp -o main.o
# ls
main.cpp  main.o  Makefile  Student.cpp  Student.hpp  Student.o
```

これで main.o まででけた。

```
# make main.exe
g++ -std=c++11 main.o Student.o -o main.exe
# ls
main.cpp  main.exe  main.o  Makefile  Student.cpp  Student.hpp  Student.o
```

main.exe もでけた。

```
# ./main.exe
hello, world. name=[Muzudho]
Please, push any key.

```

実行ファイルはでけた。

**フォールスルー** してくれないのか。

## クリーンしてみよう

```
# ls
main.cpp  main.exe  main.o  Makefile  Student.cpp  Student.hpp  Student.o
```

じゃあ、こうか？

Makefile 抜粋

```Makefile
clean:
	rm -f main.o Student.o main.exe
```

```
# make clean
rm -f main.o Student.o main.exe
# ls
main.cpp  Makefile  Student.cpp  Student.hpp
```

クリーンは一発でうまくいったな！

## フォール・スルーはどうやるのか？

```
# make main.exe
g++ -std=c++11 main.o Student.o -o main.exe
g++: error: main.o: No such file or directory
g++: error: Student.o: No such file or directory
g++: fatal error: no input files
compilation terminated.
Makefile:8: recipe for target 'main.exe' failed
make: *** [main.exe] Error 1
```

うーむ。

Makefile

```
Student.o: Student.cpp Student.hpp
        g++ -std=c++11 -c Student.cpp -o Student.o

main.o: main.cpp Student.hpp
        g++ -std=c++11 -c main.cpp -o main.o

main.exe: main.o Student.o
        g++ -std=c++11 main.o Student.o -o main.exe

clean:
        rm -f main.o Student.o main.exe
```

ターゲットの横に、上にあるターゲットを書いて　つなげたらどうか？

```
# make main.exe
g++ -std=c++11 -c main.cpp -o main.o
g++ -std=c++11 -c Student.cpp -o Student.o
g++ -std=c++11 main.o Student.o -o main.exe
# ls
main.cpp  main.exe  main.o  Makefile  Student.cpp  Student.hpp  Student.o
```

おっ！

```
# ./main.exe
hello, world. name=[Muzudho]
Please, push any key.

```

なーんだ、１行目は依存関係で、２行目は　タブ＋コマンド　なのか。
じゃあ　がんばって Makefile 書いていくか……。