wildcardを利用したシンプルな汎用Makefile

CやC++の学習中にいちいちg++とか書いてコンパイルするのが面倒なのでMakefileにも手を出し始めました。shellscriptでも同じことはできるだろうけどやっぱりmakeしたい。。

使用しているmakeのバージョンは3.81。

```shell-session
$ make --version
GNU make 3.81

$ gcc --version
gcc (MacPorts gcc49 4.9.4_0) 4.9.4
```

# 概要
全く関係ない複数のソースコードを同じディレクトリで管理していて、別々に全部コンパイルしたい。これをMakefileで実現するために色々悩んだ際のメモ。
「wildcard」関数がかなり便利で、「*」記号を使って「.cpp」等の特定の拡張子のソースコードを全て自動で探し出してコンパイルできます。
Makefile特有の「foreach」関数やマクロの書き方で結構ハマった。。

なお、bashの以下のようなディレクトリ構造を想定しています。

```bash
.
├── GNUmakefile
│
├── src
│   ├── sourceA.cpp (***.cでも可)
│   ├── sourceB.cpp
│   └── ...
│
├── include
│   ├── header1.hpp (sourceAやsourceBで呼ばれるヘッダファイル)
│   ├── header2.hpp
│   └── ...
│
└── bin
    └──
```

src内のソースファイルの数はいくつあっても問題ありません。
また、sourceA内でheader1を呼び出す際には「#include < >」を使います。(Makefile内でインクルードパスを通すため)

```cpp:sourceA.cpp
#include <header1.hpp>
```

# Makefileの中身

```make:GNUmakefile
# GNUmakefile
#		(c) S.Suzuki 2017.7.21

SUFFIX   = .cpp
COMPILER = g++
CFLAGS   = -Wall -O2

SRCDIR   = ./src
INCLUDE  = ./include
EXEDIR   = ./bin

SOURCES  = $(wildcard $(SRCDIR)/*$(SUFFIX))
OBJECTS  = $(notdir $(SOURCES:$(SUFFIX)=.o))
TARGETS  = $(notdir $(basename $(SOURCES)))

define MAKEALL
$(1): $(1).o
	$(COMPILER) -I$(INCLUDE) $(CFLAGS) -o $(EXEDIR)/$(1) $(1).o
	@$(RM) $(1).o
$(1).o:
	$(COMPILER) -I$(INCLUDE) $(CFLAGS) -c $(SRCDIR)/$(1)$(SUFFIX)
endef

.PHONY: all
all: $(TARGETS)
$(foreach VAR,$(TARGETS),$(eval $(call MAKEALL,$(VAR))))

#make clean
.PHONY: clean
clean: 
	$(RM) $(EXEDIR)/* 
```

依存ファイルは一切なく、シンプルな構造になっているかと思います。
ちなみに「make」コマンドを実行すると、デフォルトでは「GNUmakefile」、「makefile」、「Makefile」の順に探してどれかが実行されます。
それでファイル名を「GNUmakefile」にしてるけど「Makefile」にしても問題ないっぽい。

# 使い方
拡張子、コンパイラの指定を行った上で、Makefileが置いてあるディレクトリ内で、

```shell-session
$ make
```
これだけ。Makefileで書くとスマートにコンパイルできます。
srcディレクトリ内に「sourceA.cpp」と「sourceB.cpp」というソースファイルがあれば、binディレクトリの中に「sourceA」と「sourceB」という実行ファイルが別々に作られます。

また、次のようにすると個別に１つだけのコンパイルも可能です。

```shell-session
$ make sourceA
```

binディレクトリ内の実行ファイルを全て消去したい場合には、

```shell-session
$ make clean
```
とします。

# 解説
Makefileを上から順に４つの部分に分けて説明していきます。

###1. 拡張子、コンパイラ、オプション、各ディレクトリの相対パスの指定

```makefile
SUFFIX   = .cpp        # ソースの拡張子
COMPILER = g++         # 使用するコンパイラ
CFLAGS   = -Wall -O2   # コンパイラオプション
```
「SUFFIX」、「COMPILER」等はMakefileにおける変数であり、自由に名前をつけられます。また、shellscriptと同様に「#」でコメントアウトできます。
「CFLAGS」で指定するコンパイラのオプションのうち、「-Wall」はWarning Allで全ての警告を出力、「-O2」はコンパイル時に最適化を行います。どちらのオプションも実際は無くても動くのでお好み。

```makefile

SRCDIR   = ./src
INCLUDE  = ./include
EXEDIR   = ./bin
```
それぞれのディレクトリのGNUmakefileから見た相対パス。

###2.コンパイルするソースの検索

```makefile
SOURCES  = $(wildcard $(SRCDIR)/*$(SUFFIX))
OBJECTS  = $(notdir $(SOURCES:$(SUFFIX)=.o))
TARGETS  = $(notdir $(basename $(SOURCES)))
```
Makefileでの関数の書き方は、以下のようになっています。

```makefile
$(関数名 引数)
```
wildcard関数は「./src/*.cpp」の型に当てはまるファイルを取得できます。「SOURCES」を上のディレクトリ構造の場合で具体的に展開すると、

```makefile
SOURCES = ./src/sourceA.cpp ./src/sourceB.cpp
```
また、中間ファイルOBJECTSと実行ファイルTARGETSは、以下を使用して展開されます。
notdir関数 　 … ディレクトリではない部分を抽出する
$(変数:a=b) 　… 変数内の文字列aを文字列bに置換する
basename関数 … 拡張子を除いた要素を抽出する

```makefile
OBJECTS = sourceA.o sourceB.o
TARGETS = sourceA sourceB
```

###3.個別コンパイルのテンプレート(マクロ)

```makefile
define MAKEALL
$(1): $(1).o
	$(COMPILER) -I$(INCLUDE) $(CFLAGS) -o $(EXEDIR)/$(1) $(1).o
	@$(RM) $(1).o
$(1).o:
	$(COMPILER) -I$(INCLUDE) $(CFLAGS) -c $(SRCDIR)/$(1)$(SUFFIX)
endef
```
Makefileのマクロ定義は以下のように行います。また、$(1)はマクロの１つ目の引数を意味しており、call関数でマクロを呼び出す際に引数を与えることができます。

```makefile
define マクロ
	@echo "$(1)" #何らかの処理
endef
$(call マクロ,引数)
```
「@」マークが付いた行はmake実行時にコマンド名を表示しないという意味。
上の「MAKEALL」マクロでは、引数を「ARG1」と書くと以下のように展開されます。

```makefile
define MAKEALL
ARG1: ARG1.o
	g++ -I./include -Wall -O2 -o ./bin/ARG1 ARG1.o #ターゲット(実行ファイル)の生成
	@rm -f ARG1.o                                  #使用した中間ファイルの削除
ARG1.o:
	g++ -I./include -Wall -O2 ./src/ARG1.cpp       #中間ファイルの生成
endef
```
Makefileのコンパイル処理の書き方は、生成したいターゲットARG1(実行ファイル)の生成ルールを先に書き、その後に必要なパーツARG1.o(中間ファイル)の生成ルールを書きます。実行される順番はその逆となります。

###4.ソースファイルの数だけコンパイルする(forループ)

```makefile
.PHONY:all
all: $(TARGETS)
$(foreach var,$(TARGETS),$(eval $(call MAKEALL,$(var))))
```
.PHONYは擬似ターゲットと呼ばれるもので、makefileのターゲット(生成しようとするもの)のうち実態がないもの(説明難しい…)。allというものは作らないけど実行するタスクを指し示すために仮に作っているターゲットです。
今回の場合、makeコマンド実行時に引数がない場合のデフォルトターゲットはallであり、TARGETSが指し示すターゲット「sourceA」、「sourceB」等を順番に生成していくことになります。

Makefileでのループには[shellのforループを利用したもの](http://panda.holy.jp/2015/08/340/)が使えるようですが、今回の場合はMakefileのforeach関数で書くとループ部分は一行でスッキリ書けます。(他にいいのあればご教示願います)

srcディレクトリ内に「sourceA.cpp」、「sourceB.cpp」がある場合、TARGETS = sourceA sourceB となり、foreach関数

```makefile
$(foreach VAR,$(TARGETS),$(eval $(call MAKEALL,$(VAR))))
```
はVARを変数として、TARGETSの内容を順に第３引数(eval関数)に渡していきます。すなわち以下のように展開されます。


```makefile

$(eval $(call MAKEALL,sourceA))
$(eval $(call MAKEALL,sourceB))
```
ソースファイルの数の分だけ定義したMAKEALLマクロに展開されて実行ファイルが生成されていきます。

# 参考
[ –– Makefileの関数](http://qiita.com/chibi929/items/b8c5f36434d5d3fbfa4a)
[ –– Makefileの書き方(C言語)](http://www.ie.u-ryukyu.ac.jp/~e085739/c.makefile.tuts.html)
[ –– 複数の実行ファイルを生成するMakefileの書き方](http://d.hatena.ne.jp/sternheller/20090125/1232863249)

# GitHubアカウント
[OpenGL(GLUT)で色々作っています。](https://github.com/szkny)