リポジトリのセットアップや、docker composeを使った開発環境のローカル起動などに
シェルスクリプトを使う人は多いだろう。
ただ、以下のような悩みは無いだろうか?
-
./bin/〇〇.shが量産されて、どれが何だっけ?とよく混乱する - ↑の使い方を分かりやすくするために、READMEを都度メンテしなくてはいけない
- 実行に必要なファイルの存在チェックをシェルスクリプトで書いて消耗する
- コマンドの依存関係をREADMEで説明するのが面倒
Makefileはこの悩みを全て解消する。
はじめに(2025年08月追記)
筆者はMakefileからmiseに移行した。もしやりたいことが以下で実現できるのであれば、miseをおすすめする。
📄タスクランナーはMakefileよりmiseがおすすめ、ついでに使い方も
Makefileとは
本来「C言語のコンパイルから実行までをコマンド一つで実行しよう」というものである。
しかし、特徴として「ショートカットコマンドを量産できる」ので
1つのコマンドが長くなりがちな
DockerコマンドやPython(仮想環境)の実行コマンドに相性が良い。
シェルスクリプトとの比較
シェルスクリプトと比較したときのメリデメは以下の通りだ。
見て分かる通り、デメリットはほぼ無いに等しいのでMakefile推しである。
メリット
- 書き方や実行のルールが決まっているので、他の人も読みやすい(Makefileそのものがドキュメントとして機能する)
- 例:
start.shとかstop.shなど複数ファイル用意することなく、全てMakefileファイルで完結
- 例:
- 実行時に必要なファイルをチェックしてくれるので、バリデーション処理を自前で書かなくて良い
- 例: 実行前に
.envファイルの存在チェックをしたい
- 例: 実行前に
- コマンド間の依存関係を定義することができる
- 例:「ビルドしてから実行する」
デメリット
- makeコマンドのインストールが必要→
brew install makeでOK - 文法を覚える必要がある→この記事の内容で十分
- 実行時に引数を扱うことができない→「同じ内容で長いコマンド」を省略したいだけなので特に困らない
実行方法
-
Makefileという定義ファイルを作成 -
Makefileがあるディレクトリで、「make 任意のコマンド名」を実行する
Makefileの書き方
コマンド名: 実行に必要なファイル
実際に実行するコマンド ポイントは3つ
- 「実行に必要なファイル」は、省略可能。ただし記載しておくことでファイルチェックをしてくれる
- 「実際に実行するコマンド」の先頭はスペースではなくtabにする必要がある
- 実行するときのコマンドは「
make コマンド名」
実行例
Makefile
hello:
echo "hello" 実行結果
$ make hello
echo "hello"
hello 応用編
シンプルなショートカットコマンドの定義だけであれば、上記の説明で困ることはない。
ここからは少し複雑なコマンドを定義したいときに必要なことを解説する。
①makeコマンドは続けて実行可能
Makefile
hello1:
echo "hello1"
hello2:
echo "hello2" 実行結果
# 「make コマンド1 コマンド2 ...」というふうに続けて実行可能
$ make hello1 hello2
echo "hello1"
hello1
echo "hello2"
hello2 連続して実行することができるので、定義するコマンドは最小単位にしておくとスマート。
②必要なファイルはスペース区切りで複数指定できる
Makefile
build: Dockerfile test.txt
docker build -t python-sample:1.0 . ディレクトリにDockerfileは存在するが、test.txtは存在しない場合、以下のようにエラーが発生する。
$ make build
make: *** No rule to make target `test.txt', needed by `build'. Stop. ③変数を使うことも可能
定義するときは「変数名 = 値」
利用するときは「$(変数名)」で使うことができる。
Makefile
MESSAGE = "hello, world."
hello:
echo $(MESSAGE) 実行結果
$ make hello
echo "hello, world."
hello, world. ④コマンド同士の依存関係も自動で解決してくれる
先述した基本コマンドの「必要なファイル」という部分は
実はファイルに限らず、他のmakeコマンドも定義することができる。
これにより、「コマンドAを実行するためにはコマンドBを実行しておく必要がある。だからコマンドB実行→コマンドA実行という流れで実行しよう。」という定義を行うことができる。
Makefile
# 実行するためには、ビルドの手順が必要だと定義する
run: build
echo "実行します"
build:
echo "ビルドします" 実行結果
# 実行用のrunコマンドを実行する
$ make run
echo "ビルドします"
ビルドします
echo "実行します"
実行します run前にはbuildが必要なため、buildを自動で実行し、その後にrunを実行するようになる。
まとめ
- Makefileは長いコマンドをショートカットするのに便利
- 「実行前に必要なものが揃っているか?」を定義することができる
- READMEの追記と
〇〇.shの量産に悩まされたらMakefileを使おう