はじめに

カルテットの開発部内でなんとなく「シェルに詳しいおじさん」というイメージを持たれている永井です。(本当は特に詳しくないです)
最近ふと思い立ってGNU Makeコマンド(以下Makeコマンド)を使い始めたんですが、思いのほか使い心地が良いので紹介したいと思います。

Makeコマンドとは

何かソースコードをダウンロードしてきて、インストールする時に入力する

make && make install

みたいなコマンドのことです。

皆さんも一度は入力した覚えがあるんじゃないでしょうか?
最近はソースコードからmakeコマンドを実行してアプリケーションをコンパイルするということは少なくなりましたが、最新のソースコードを利用したい時や自分でパッチを当てて再コンパイルしたい時など、まだまだ使う場面はあるかと思います。

タスクランナーとしてのMakeコマンド

makeコマンドを使ったことはあっても、Makefileを自分で作成したことがある人となると極端に少ないと思います。
自分も「なんだかMakeコマンド周辺のテクノロジーは古臭くて小難しいなあ」というイメージを持っていました。

タスクランナーとしてよく使われているツールと言えば、GruntGulpといったところが頭に浮かびますでしょうか。
またnpm-scriptsComposerscriptsなど、新たにツールをインストールしなくてもパッケージ管理ツールに付随する機能で済んでしまうことも少なくありません。

が、実際に使い始めてみると結構使いところがあるなと感じています。
個人的に良いなと感じたところを挙げていきたいと思います。

ポータビリティが高い

ここではとても狭義な意味でのポータビリティを指していまして、macOSやUnix系のOSならば、余程珍しいものでなければ動作するという意味でのポータビリティです。
一応Windowsでも動作するような記事を見かけましたが、実用に耐えられるかは不明です・・・。

またnpm-scriptsはNode環境が必要ですし、ComposerはPHP環境が必要です。
それぞれの環境にはバージョンがありますし、開発しているアプリケーションが特定バージョンに依存していて干渉してしまうということも少なからずあるのではないでしょうか?
インフラ部門と開発部門が分かれていたりすると、この辺りのレギュレーションがシビアだったりすることもあるかと思います。(弊社はこの限りではありません)

また特にシェルを指定しなければ、Makefileの中身はBource Shellで書くことになるかと思います。
もしそのままBource Shellを利用することになればポータビリティを高めることに一役買うことになるかと思います。

個人的に開発環境で行いたいタスクを書く場所としての利用

私が「 普段開発しているプロジェクトがMakefileを利用していなかった」というのも大きいのですが、複数人で開発している場合にcomposer.jsonpackage.jsonに書くべきかどうか悩むようなタスクが存在します。
「ローカルである程度運用してみて、有用だと思われた場合プロジェクト全体に影響するファイルへ追加する」ということは良くやっているので、そういったお試しタスクを書く場所として重宝しています。

従うべきルールがある

make <ターゲット>

という感じでコマンドを実行するのですが、このターゲットの部分は任意に作成することが出来ます。
マニュアルには、以下のターゲットを持つようにしましょうというルールが明記されています。

  • all
  • install
  • install-html
  • install-dvi
  • install-pdf
  • install-ps
  • uninstall
  • install-strip
  • clean
  • distclean
  • mostlyclean
  • maintainer-clean
  • TAGS
  • info
  • dvi
  • html
  • pdf
  • ps
  • dist
  • check
  • installcheck
  • installdirs

ターゲット名だけでなんとなく意味のわかるものから、使ったことがなかったりピンとこないものまで幾つかあるかもしれません。
個人で利用する時にこれらのルールに従うとなるとかなり窮屈に感じますし、不特定多数に配布するものでなければ特に従う必要はないかなと思っています。
しかし、こういったルールがあることでターゲット名を決める時の参考にすることが出来るので、個人的には非常に助かります。
もしMakefileを他人に配布することになったとしても、これらのルールに従っているターゲット名であれば、タスク内容を推測しやすくなるのではないでしょうか。

ファイルが一つにまとめられる

Makeコマンドを利用する前までは、細かいタスク毎にシェルスクリプトを作成していました。
今でも入り組んだことをやろうとする時はシェルスクリプトを別途用意しますが、数行で済むような簡単なコマンドの羅列であればMakefileで事足ります。

また、コメントも書くことが出来るので、簡易的なドキュメントとしてもファイルが一つなのは助かります。

実際にどんなところで使っているか

まだ使い始めて1ヶ月程度ですが、こんなところで使い始めています。

  • git-pull後のSymfonyプロジェクトのビルド
  • リモートホストに保存されている圧縮されたDBデータのダンプファイルのローカルPCへのリストア
  • dotfilesのファイル配置とファイル削除

dotfilesのMakeifileサンプル

実際に利用しているdotfilesのMakefileを晒してみます。

TARGETS := \
"gitconfig" \
"gitignore" \
"git-template" \
"git-commit-message" \
"tmux" \
"tmux.conf" \
"vim" \
"vimrc" \
"zshrc"

.PHONY: all
all: ## submodule update init
	git submodule update --init --recursive

.PHONY: install
install: ## create target's symlink in home directory
	@for TARGET in $(TARGETS); do \
		if [ -e "$(HOME)/.$$TARGET" ]; then \
			echo "already exists $$TARGET"; \
		else \
			ln -s $(CURDIR)/$$TARGET $(HOME)/.$$TARGET; \
			echo "created $$TARGET"; \
		fi \
	done

.PHONY: uninstall
uninstall: ## delete created symlink
	@for TARGET in $(TARGETS); do \
		if [ -h "$(HOME)/.$$TARGET" ]; then \
			rm $(HOME)/.$$TARGET; \
			echo "deleted .$$TARGET"; \
		elif [ -e "$(HOME)/.$$TARGET" ]; then \
			echo "no symlink $$TARGET"; \
		else \
			echo "no exists $$TARGET"; \
		fi \
	done

.PHONY: help
help:
	@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'

make allでgit-submoduleのアップデートを行っています。
ちなみにgit-submoduleは主にvimのPluginの管理に利用しています。
make installでホームディレクトリ直下にシンボリックリンクを作成し、make uninstallで作成したシンボリックリンクを削除します。
make helpで各ターゲットのコメント表示するようにしています。
実行するとこんな感じになります。

$ make help
all                            submodule update init
install                        create target's symlink in home directory
uninstall                      delete created symlink

以下のサイトを参考にさせて頂きました。
参考: Makefileを自己文書化する | プログラミング | POSTD

必要な情報が一つのMakefileに簡潔にまとめられ、ターゲット名も基本ルールにある程度沿ったもので作成できたので、個人的には見通しが良くなったなと感じています。

おわりに

Makeコマンドをタスクランナーとして利用する的な記事は、以前から存在することは知っていました。
いくつかの記事を見ても、その時はGruntGulpを覚えるのを面倒に感じる人達が

「それMakeで出来るよ」

って言っているだけかと思っていました。
しかし実際に使ってみるとこれはこれで良いものだなと感じると共に、根強いファンがいるのも頷けます。

また、今回は説明を省きましたが、

のような、本来のコンパイルツールとしての強力な機能も多数持ち合わせています。
自分はまだまだそれらを有効活用するところまで出来てませんが、Makeコマンドは今後も長く使い続けられるんじゃないかと思いますので、徐々に覚えていけたらなと思います。