Makefileをタスクランナーとして使う:make dev・make test・make lint の書き方
はじめに
MakefileはもともとC言語などのビルドを自動化するツールですが、現在では言語に関係なくタスクランナーとして広く使われています。
make dev # 開発サーバーを起動 make test # テストを実行 make lint # Lintを実行 make build # ビルドを実行
プロジェクトのルートに Makefile を置くことで、チームメンバーが長いコマンドを覚えなくても make だけで操作できるようになります。
基本的な書き方
ターゲット名: 依存関係
実行するコマンド
重要:コマンドの先頭はタブ文字(スペースではなく)です。
hello:
echo "Hello, World!"
make hello # echo "Hello, World!" # Hello, World!
デフォルトでは実行するコマンド自体も表示されます。コマンドの先頭に @ をつけると非表示になります。
hello:
@echo "Hello, World!"
make hello # Hello, World!
.PHONY の意味と必要な理由
make は本来ファイルのビルドツールなので、ターゲット名と同じ名前のファイルが存在すると「すでに最新」と判断してコマンドを実行しません。
# testというファイルが存在する場合 touch test make test # make: 'test' is up to date. ← コマンドが実行されない!
.PHONY に登録することで、ファイルの存在に関係なく常にコマンドを実行するようになります。
.PHONY: test dev lint build
test:
@go test ./...
dev:
@go run main.go
タスクランナーとして使う場合は、すべてのターゲットを .PHONY に登録するのが基本です。
よく使うタスクの例
開発サーバー起動
.PHONY: dev
dev:
@npm run dev
# Railsの場合
dev:
@bin/rails server
# Pythonの場合
dev:
@uvicorn main:app --reload
テスト実行
.PHONY: test
test:
@npm test
# RSpecの場合
test:
@bundle exec rspec
# Goの場合
test:
@go test ./...
Lint・フォーマット
.PHONY: lint fmt
lint:
@npm run lint
fmt:
@npm run format
# Go(複数コマンドをまとめる)
lint:
@golangci-lint run
fmt:
@gofmt -w .
ビルド
.PHONY: build
build:
@npm run build
変数を使う
変数を定義して複数の場所で使い回せます。
APP_NAME = myapp
PORT = 8080
.PHONY: dev build
dev:
@PORT=$(PORT) go run main.go
build:
@go build -o $(APP_NAME) .
環境変数を上書きして実行することもできます。
make dev PORT=3000
引数を渡す
make の引数として変数を渡せます。
.PHONY: migrate
migrate:
@bundle exec rails db:migrate RAILS_ENV=$(ENV)
make migrate ENV=production
引数のデフォルト値は ?= で設定できます。
ENV ?= development
migrate:
@bundle exec rails db:migrate RAILS_ENV=$(ENV)
make migrate # RAILS_ENV=development make migrate ENV=test # RAILS_ENV=test
依存関係:他のターゲットを先に実行する
ターゲット名の後ろに依存するターゲットを書くと、先にそちらが実行されます。
.PHONY: build test lint
lint:
@npm run lint
test: lint
@npm test
build: test
@npm run build
make build # まず lint が実行される # 次に test が実行される # 最後に build が実行される
CIでよく使うパターンです。
まとめて実行するターゲット
all という名前のターゲットを作ると make だけで実行できます(all は引数なしの make のデフォルトターゲット)。
.PHONY: all lint test build
all: lint test build
lint:
@npm run lint
test:
@npm test
build:
@npm run build
make # lint → test → build が順番に実行される make all # 同じ
ヘルプを表示する
各ターゲットの説明を ## コメントで書いておき、help ターゲットで一覧表示できます。
.PHONY: help dev test lint build
help: ## ヘルプを表示する
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | \
awk 'BEGIN {FS = ":.*?## "}; {printf " %-10s %s\n", $$1, $$2}'
dev: ## 開発サーバーを起動する
@npm run dev
test: ## テストを実行する
@npm test
lint: ## Lintを実行する
@npm run lint
build: ## ビルドする
@npm run build
make help # help ヘルプを表示する # dev 開発サーバーを起動する # test テストを実行する # lint Lintを実行する # build ビルドする
まとめ
| 機能 | 書き方 |
|---|---|
| 基本のタスク | ターゲット名: + タブ + コマンド |
| コマンドを非表示 | コマンド先頭に @ |
| ファイル判定を無効化 | .PHONY に登録(必須) |
| 変数 | 変数名 = 値 / $(変数名) |
| デフォルト値付き変数 | 変数名 ?= デフォルト値 |
| 依存関係 | ターゲット: 依存ターゲット |
| デフォルト実行 | all: ターゲットを定義 |
- タスクランナーとして使う場合は
.PHONYを忘れずに - コマンドに
@をつけると出力がすっきりする helpターゲットを作っておくとチームで使いやすくなる