3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Makefile触ってみた

3
Posted at

みなさん、エンジニア楽しんでいますか?

Goの学習をしていたところ、Makefileというのが出てきました。
実務では触れたことがなく、調べてところGoの開発で使われることがあるとのことで、備忘録的に調べたことを共有します。

同じように初めてMakefileを書く方向けに、実際に使っているMakefileを例にして書き方を解説します。
環境はmacOSで、DBはDocker Compose上のPostgreSQL、マイグレーションは golang-migrate のCLIで実行しています。

Makefileとは

Makefileは、make コマンドが読み込むファイルです。
もともとはC言語のビルドに使われていたもので、「このファイルが変わったら、このコマンドで作り直す」という手順を書いておくためのものでした。

Goでは go build がビルドを全部やってくれるので、この使い方はあまりしません。
代わりに、長いコマンドに短い名前を付けておく用途で使われています。
package.json や composer.json の scripts と同じような使い方です。

私の場合、マイグレーションのたびにこのコマンドを打っていました。

migrate -path migrations -database "postgres://stockade:stockade@localhost:5432/stockade?sslmode=disable" up

これをMakefileに書いておくと、make migrate-up で実行できるようになります。

ファイル名は Makefile で、拡張子は付けません。
小文字の makefile でも動きますが、Makefile と書くのが一般的です。
置き場所はプロジェクトのルートです。

まずは書いてみる

DBのコンテナを起動するコマンドを登録してみます。

Makefile
up:
	docker compose up -d

up: の部分をターゲット、その下の行をレシピと呼びます。
up という名前は自分で決めたもので、make up と打つと up のレシピが実行されます。

$ make up
docker compose up -d

実行したコマンドが最初に表示され、そのあとにdocker composeの出力が続きます。
表示させたくない場合は、レシピの先頭に @ を付けます。

レシピの行は、必ずタブでインデントします。
スペースでインデントすると、次のエラーになります。

$ make up
Makefile:2: *** missing separator.  Stop.

エラー文にはタブのことが書かれていないので、初見だと原因が分かりません。
このエラーが出たら、エディタの設定でタブがスペースに変換されていないか確認してください。

.PHONYを付ける

Makeは、ターゲット名を「これから作るファイルの名前」として扱います。
そのため、フォルダに up という名前のファイルがあると、もう作ってあると判断してレシピを実行しません。

$ touch up
$ make up
make: 'up' is up to date.

ファイルを作らないターゲットは、.PHONY に書いておきます。
.PHONY に書いたターゲットは、同じ名前のファイルがあっても毎回実行されます。

Makefile
.PHONY: up

up:
	docker compose up -d

.PHONY は、Makeにもともと用意されている名前です。

複数のコマンドを書く

同じ書き方でターゲットを増やしていきます。

Makefile
.PHONY: up down reset

up:
	docker compose up -d

down:
	docker compose down

reset:
	docker compose down -v
	docker compose up -d

reset のように、レシピは複数行書けて、上の行から順番に実行されます。

ただし、1行ごとに別のシェルで実行されるので、cd で移動しても次の行には引き継がれません。

Makefile
bad:
	cd migrations
	pwd
$ make bad
cd migrations
pwd
/path/to/stockade

migrations に移動したつもりでも、pwd は元のフォルダを表示しています。
移動してから何かしたい場合は、cd migrations && pwd のように1行で書きます。

また、make とだけ打つと、一番上に書いたターゲットが実行されます。
このMakefileなら up が実行されます。

変数を渡す

マイグレーションファイルを作るコマンドは、ファイル名を毎回変える必要があります。
こういう場合は、変数を使います。

Makefile
migrate-create:
	migrate create -ext sql -dir migrations -seq $(NAME)
make migrate-create NAME=add_index

Makefileの中の $(NAME) が、コマンドで渡した add_index に置き換わって実行されます。
Makeの変数は $(名前) の形で書き、NAME の部分は自分で決めた名前です。

実際にどんなコマンドになるかは、make -n で確認できます。
-n を付けると、コマンドを実行せずに表示だけしてくれます。

$ make -n migrate-create NAME=add_index
migrate create -ext sql -dir migrations -seq add_index

.envを読み込む

DBの接続情報は .env に書いていて、最初はこうなっていました。

.env
POSTGRES_USER=stockade
POSTGRES_PASSWORD=stockade
POSTGRES_DB=stockade
POSTGRES_PORT=5432
DATABASE_URL=postgres://stockade:stockade@localhost:5432/stockade?sslmode=disable

POSTGRES_ で始まる4つはDocker ComposeがDBを作るときに使い、DATABASE_URL はアプリとmigrateが接続に使います。
ユーザー名とパスワードを2回書いているので、パスワードを変えたときに DATABASE_URL を直し忘れそうです。

なので、.env には4つだけを残して、DATABASE_URL はMakefileで組み立てることにしました。

Makefile
include .env
export

DATABASE_URL := postgres://$(POSTGRES_USER):$(POSTGRES_PASSWORD)@localhost:$(POSTGRES_PORT)/$(POSTGRES_DB)?sslmode=disable

include .env で、.env の中身をMakefileに読み込んでいます。
これで、.env に書いた POSTGRES_USER などを $(POSTGRES_USER) で使えるようになります。

export は、Makefileの変数を環境変数としてレシピのコマンドに渡す指定です。
何も付けずに export とだけ書くと、Makefileで作った DATABASE_URL も含めて、全部の変数が渡ります。
これを書かないと、Makefileの中では $(DATABASE_URL) が使えても、レシピから起動したアプリからは読めません。

:= は、その行の時点で値を確定させる代入です。
include、export、:= はMakeの機能で、DATABASE_URL は自分で決めた名前です。

.envの書き方に注意

include は .env をMakefileとして読み込むので、シェルと同じ感覚で書くと値が変わってしまうことがあります。
以下は、それぞれ組み立てた DATABASE_URL がどうなるかです。

.env
# クォートも値に含まれる → postgres://stockade:"stockade"@...
POSTGRES_PASSWORD="stockade"

# $w が変数として展開されて消える → postgres://stockade:paord@...
POSTGRES_PASSWORD=pa$word

# コメントの前の空白が値に残る → postgres://stockade:stockade @...
POSTGRES_PASSWORD=stockade # ローカル用

.env の値はクォートで囲まず、$ や行末のコメントも使わないようにしておきましょう。

また、.env が無いとMakefile自体が動きません。

$ make up
Makefile:1: .env: No such file or directory
make: *** No rule to make target '.env'.  Stop.

cloneした直後など、.env を作り忘れているとこのエラーが出ます。

完成したMakefile

今使っているMakefileです。

Makefile
include .env
export

DATABASE_URL := postgres://$(POSTGRES_USER):$(POSTGRES_PASSWORD)@localhost:$(POSTGRES_PORT)/$(POSTGRES_DB)?sslmode=disable

.PHONY: up down reset migrate-up migrate-down migrate-create migrate-version psql test

# DB コンテナを起動する
up:
	docker compose up -d

# DB コンテナを止める(データは残る)
down:
	docker compose down

# データごと消して作り直す
reset:
	docker compose down -v
	docker compose up -d

# マイグレーションを最新まで適用する
migrate-up:
	migrate -path migrations -database "$(DATABASE_URL)" up

# マイグレーションを1つ戻す
migrate-down:
	migrate -path migrations -database "$(DATABASE_URL)" down 1

# マイグレーションファイルを作る(例:make migrate-create NAME=add_index)
migrate-create:
	migrate create -ext sql -dir migrations -seq $(NAME)

# 今どのバージョンまで適用されているか
migrate-version:
	migrate -path migrations -database "$(DATABASE_URL)" version

# コンテナの中の psql に入る
psql:
	docker compose exec db psql -U $(POSTGRES_USER) -d $(POSTGRES_DB)

# テストを実行する
test:
	gotestsum --format testname -- -count=1 ./...

レシピの中の migrate はgolang-migrateのコマンドで、gotestsum はテスト結果を見やすく表示してくれるツールです。

DBを起動してテーブルを作り、中身を確認するところまでは、この3つで済みます。

$ make up
docker compose up -d
[+] up 14/14
 ✔ Image postgres:17-alpine   Pulled     6.0s
 ✔ Network stockade_default   Created    0.0s
 ✔ Volume stockade_pgdata     Created    0.0s
 ✔ Container stockade-db      Started    0.2s

$ make migrate-up
migrate -path migrations -database "postgres://stockade:stockade@localhost:5432/stockade?sslmode=disable" up
1/u init_schema (21.91525ms)

$ make psql
docker compose exec db psql -U stockade -d stockade
psql (17.11)
Type "help" for help.

make migrate-upで表示されるコマンドには、パスワードがそのまま出ています。
ローカル用の値なので私はそのままにしていますが、気になる場合はその行に @ を付けてください。

おわりに

学習程度で使っているだけですが、結構面白いなと思いました。
特にdocker系のコマンドなど、長くて実行するのが面倒だなと感じていたのですが、そう感じた時はMakefileに書こーってなります。
タブでインデントすることと、.PHONYを付けることさえ守ればすぐに書けると思います。

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?