この記事は Willgate Advent Calendar 2018 の21日目の記事です。
前日は cocoeyes02 さんの 「業務で初めてブラウザE2Eテストを触ったので、つまづいた点と対処した方法を書きます」 でしたがまだ投稿されていないようですね。。
みなさん Makefile 書いてますか?僕は Makefile 好きなのでよく書いてます!
自分はよく書くことが多くスムーズにかけるのですが、経験が少ない人にとってわかりづらいことも多いかと思います。
なので、この記事では Makefile でよく使う書き方の紹介をさせていただきます。
Makefile の役割
書き方を紹介すると言いましたが、そもそも Makefile の役割を先に解説します。
make はもともとプログラムのビルド作業を自動化するツールとして作られました。
make ではその目的のために
ということができます。
ファイルの生成
先ほど書いた通り、ビルド自動化のツールとしてルールに乗っ取ってファイルを生成することができます。またルールのことを Make ではマクロと呼びます。
具体的には下記のような形です。
$ cat Makefile hoge.txt: touch hoge.txt $ make hoge.txt touch hoge.txt $ ls -al hoge.txt -rw-r--r-- 1 zoe wheel 0 12 24 16:29 hoge.txt
※Makefile ではインデントはタブ文字出ないとエラーになるのでご注意を。
このように、
Makefile
{生成したいファイル名}: {任意のコマンド}
と記述することで
make {生成したいファイル名}
とコマンドを実行することで Makefile で記述した {任意のコマンド}
が実行される。
また、すでにファイルが生成済みのルールは実行されません。
$ make hoge.txt make: `hoge.txt' is up to date.
これは、最初に話したようにビルド自動化のためにできたツールであるため、すでにビルド済みのものを再度ビルドするのを防ぎ時間の短縮のための機能です。
依存関係
fileA を生成する前に fileB を生成しておく必要があるとします。その場合下記のように書くことで自動で依存解決をしてくれます。
$ cat Makefile fileA: fileB touch fileA fileB: touch fileB $ make fileA touch fileB touch fileA
また、この場合でも先ほどと同様にすでにファイルがある場合は更新されません。
しかし、この際に依存しているファイルの更新日時の方が新しい場合は、再度実行されます。
$ ls -al file* -rw-r--r-- 1 zoe wheel 0 12 24 16:44 fileA -rw-r--r-- 1 zoe wheel 0 12 24 16:44 fileB $ touch fileB $ ls -al file* -rw-r--r-- 1 zoe wheel 0 12 24 16:44 fileA -rw-r--r-- 1 zoe wheel 0 12 24 16:49 fileB $ make fileA touch fileA $ ls -al file* -rw-r--r-- 1 zoe wheel 0 12 24 16:49 fileA -rw-r--r-- 1 zoe wheel 0 12 24 16:49 fileB
ファイル生成しないマクロ
下記のようなコマンドとして使うことを想定したマクロ定義をしてみます。
$ cat Makefile fileA: fileB touch fileA fileB: touch fileB clear: rm -rf file* $ ls -al file* -rw-r--r-- 1 zoe wheel 0 12 24 16:49 fileA -rw-r--r-- 1 zoe wheel 0 12 24 16:49 fileB $ make clear rm -rf file* $ ls -al file* ls: file*: No such file or directory
このようにファイルを生成しないマクロを定義することもできます。
しかし一つ罠があります。
もともと Make はビルド自動化のためのツールでファイル生成するのが役割と伝えたように今回の make clear
も Make からするとファイル生成のマクロと同じ扱いのため、下記のような問題が起きてしまいます。
$ make fileA touch fileB touch fileA $ touch clear $ ls -al -rw-r--r-- 1 zoe wheel 69 12 24 17:08 Makefile -rw-r--r-- 1 zoe wheel 0 12 24 17:11 clear -rw-r--r-- 1 zoe wheel 0 12 24 17:11 fileA -rw-r--r-- 1 zoe wheel 0 12 24 17:11 fileB $ make clear make: `clear' is up to date.
clear というファイルが仮に存在してしまうと make clear
はファイル生成のマクロではないのに、実行できなくなってしまいます。
これを回避するためには、このマクロはファイル生成のマクロではないですよということを Make に伝える必要があります。
$ cat Makefile fileA: fileB touch fileA fileB: touch fileB clear: rm -rf file* .PHONY: clear $ make clear rm -rf file*
PHPでどういう場面で使うの?
私がPHPのプロジェクトでよく使うものとしては下記のようなものを作ることが多いです。
setup: vendor .env .PHONY: setup vendor: composer install --no-interaction .env: cp .env.example .env test: vendor .env vendor/bin/phpunit -c phpunit.xml .PHONY: test lint: vendor .env vendor/bin/phpcs --standard=phpcs.xml --extensions=php .PHONY: lint
プロジェクト初期設定用の make setup
で vendor
.env
のマクロを実行し必要ファイルをセットアップ。
make lint
で php の linter である phpcs を実行、 make test
で phpunit を実行するように設定されています。
また必要なコマンド一式を Make に書いておくことで、このプロジェクトではこういう技術が利用されている、この設定が必須だ、ということをプロジェクトメンバーに伝えやすくなるため、おすすめです。
まとめ
いかがでしょうか?今回紹介したもの以外にも make の便利な機能もあったりしますので、気になった方はドキュメントも参照してみてください。
また、php以外でも私はdockerやgolangなどのラッパーやプライベート開発のサービスのデプロイコマンドだったり、CIなどからのコマンドのAPIとしても利用したりしています。