2016年だけどMakefileを使ってみる


# この資料はなに？

Makeの良さを伝えるものです

- CとかC++の持ち物じゃない
- 普段使うコマンドのショートカットみたいな
- 2016年でもMakefileは便利

---

![ 2016-10-19 19.11.07.png](https://fringe81.qiita.com/files/6b83b0a2-f9d5-4505-67e0-647d64e1ffd2.png " 2016-10-19 19.11.07.png")

[頑張らないでScala 〜VOYAGE GROUPにおけるアドネットワーク開発の戦略〜 // Speaker Deck](https://speakerdeck.com/kiris/wan-zhang-ranaidescala-voyage-groupniokeruadonetutowakukai-fa-falsezhan-lue?slide=17)

---

# Makeってなに？Makefile???

> make(メイク)は、プログラムのビルド作業を自動化するツール。コンパイル、リンク、インストール等のルールを記述したテキストファイル (makefile) に従って、これらの作業を自動的に行う。
出典：https://ja.wikipedia.org/wiki/Make

つまり、自動化のためのツール。

---

> make が生成するのはふつう C のプログラムだが、べつに C のプログラムに限らず、Makefile に書く生成コマンドの書きように よっては、TeX のファイルだろうが、Java のプログラムだろうが 何でも make を利用して作業を軽減することができる。 
出典：http://www.unixuser.org/~euske/doc/makefile/

なので、CだろうがC++だろうがScalaだろうが関係ない。

---

ぐぐるとこんなの出てきて難しそう...

```Make
PROGRAM = main
OBJS = main.o
CC = gcc
CFLAGS = -Wall -O2
.SUFFIXES: .c .o

.PHONY: all
all: depend $(PROGRAM)

$(PROGRAM): $(OBJS)
	$(CC) -o $(PROGRAM) $^

.c.o:
	$(CC) $(CFLAGS) -c $<

.PHONY: clean
clean:
	$(RM) $(PROGRAM) $(OBJS) depend.inc

.PHONY: depend
depend: $(OBJS:.o=.c)
	-@ $(RM) depend.inc
	-@ for i in $^; do cpp -MM $$i | sed "s/\ [_a-zA-Z0-9][_a-zA-Z0-9]*\.c//g" >> depend.inc; done

-include depend.inc
```

http://www.ie.u-ryukyu.ac.jp/~e085739/c.makefile.tuts.html

---

## 何が良いの？

ぼくがおもうMakeのいいところ

- わざわざインストールしなくていい(はず)
  - デフォルトで入ってる(`/usr/bin/make`)
- タブ補完が効く
- よく使うコマンドを保存出来る
  - .bashrcに`alias`はるのとは違う
    - ディレクトリ(プロジェクト)毎にコマンドを用意できるので環境は汚れない
- よく使うコマンドを共有できる
  - 「配信サーバに対するリクエストってどうやって送ればいいですか？」に応える

---

# サンプル

Makefileはこんな感じ。

```Makefile
show-ip:
        ipconfig getifaddr en0
```

これを使ってみる。

```shell-session
$ make show-ip
ipconfig getifaddr en0
192.168.0.27
```

これは`show-ip`というタスクで`ipconfig getifaddr en0`というコマンドを実行している。
難しいコマンドをおぼえなくていいし、Makefileの文法も簡単なので学習コストは低い。

---

# 書き方

ざっくりとはこれだけ。

```
<変数宣言>

<タスク名>:
  <実行したいshell>
  
<タスク名>:<必要ファイル名>
  <実行したいshell>
```

**インデントはタブ文字**なので注意。
ほんとは変数じゃなくてマクロと呼ばれる。

---

## 実行するシェルを標準出力に表示しない方法

実行すると実行するシェルが出力されてしまうので、鬱陶しい場合は`@`を先頭につける。

```Makefile
show-files:
        ls -lh

show-files-suppress:
        @ls -lh
```

実行するときは何も変わらない

```shell-session
$ make show-files
ls -lh
total 8
-rw-r--r--  1 petitviolet  staff    51B Oct 15 15:10 Makefile
$ make show-files-suppress
total 8
-rw-r--r--  1 petitviolet  staff    51B Oct 15 15:10 Makefile
```

---

## 変数宣言

Makefileの中では変数を使用することが出来る。

```shell-session
$ cat Makefile
MESSAGE := "HELLO"

hello:
        @echo $(MESSAGE)
$ make hello
HELLO
```

タスクで使用する際に上書き出来る。

```shell-session
$ make hello MESSAGE=こんにちは
こんにちは
```

---

## タスク実行に必要なファイルを指定できる

`activator`というファイルが存在していれば...みたいなタスクを定義する。

```Makefile
compile: activator
  @echo "Yey!"
```

ファイルが無いと失敗し、ファイルがあれば成功する。

```shell-session
$ ls
Makefile
$ make compile
make: *** No rule to make target 'activator', needed by 'compile'.  Stop.
$ touch activator
$ make compile
Yey!
```

順番にタスクを実行するような場合、ファイルの作成を持って成否判定を出来たりして便利！

---

## タスクからタスクを呼ぶ

あるタスクの中から別のタスクを実行することも出来る。

```Makefile
task-a:
	@echo "A"
	@touch a.txt
	@make task-b

task-b: a.txt
	@echo "B"
	@touch b.txt
	@make task-c

task-c: b.txt
	@echo "C"
	@touch c.txt

clean: a.txt b.txt
	@rm a.txt b.txt c.txt
	@echo 'complete!!!'

doit:
	@make task-a
	@make clean
```

実行するとこんな感じになる。

```shell-session
$ make doit
A
B
C
complete!!!
```

---

## 複数行にわたるコマンドを実行したい時

一行ごとに別のshellとして実行される点に注意。

```shell-session
$ ls
Makefile
$ cat Makefile
make-tmp-file-home:
        @cd ~
        @touch hogehoge
$ make make-tmp-file-home
$ ls
Makefile  hogehoge
```

これはサブシェルの中で`cd`して、別のサブシェルの中で`touch`しているので、カレントディレクトリでの操作となる。

---

`&&`と`\`でつないで1つのシェルとして実行すれば良い。

```shell-session
$ ll $HOME | grep 'hogehoge' | wc -l
       0
$ cat Makefile
make-tmp-file-home:
        @cd ~ && \
        touch hogehoge
$ make make-tmp-file-home
$ ll $HOME | grep 'hogehoge' | wc -l
       1
```

---

## シェル変数と制御文

上記の点さえ気をつければfor文も書ける。

```Makefile
LENGTH := 3

show-seq:
	@for i in {1..$(LENGTH)}; do \
		echo $$i; \
	done
```

ここでシェル変数が`$$i`となっている点に注意。