AIエージェントを使うようになってから、古くからある make コマンドの便利さを改めて感じている。
make は C 言語のビルドで使う古い道具、という印象が強かった。自分も昔から知っていたが、仕組みをきちんと理解して使っていたわけではない。
ところが、Claude Code や Codex のような CLI 型 AI エージェントを相棒に開発すると、make はかなり相性がいい。
理由は単純で、プロジェクトでよく使う操作を make test、make build-release、make device-debug のような短いコマンドにまとめられるからだ。人間と AI エージェントが同じコマンドを見て、同じように実行できる。
特に iOS アプリ開発では効果が大きい。make device-debug でビルド、実機インストール、起動までできるようにしておくと、Xcode を開かずにターミナルだけで開発を進められる場面が増える。Xcode は他の IDE と操作感がかなり違い、毎回開くのが負担に感じることもある。Xcode への依存を半分でも減らせると思うと、iOS アプリ開発の心理的なハードルも下がる。左半分のターミナルで Claude Code などの CLI AI と対話し、右半分や別タブで make を叩く。あるいは AI エージェント自身に make test を実行してもらう。
GitHub Issue とターミナルがあれば、基本的な開発作業がかなり済んでしまう。
makeは何をする道具なのか
make は、Makefile に書かれたルールを読んで、必要なコマンドを実行する道具である。
GNU Make のマニュアル
では、make は大きなプログラムのどの部分を再コンパイルする必要があるかを自動判定し、そのためのコマンドを実行するユーティリティとして説明されている。
もともとの発想は、ファイル同士の依存関係を見て、変更があった部分だけを更新することだ。
たとえば C の世界なら、main.c から main.o を作り、複数の .o をリンクして実行ファイルを作る。ヘッダーファイルが変わったら、それに依存するソースを再コンパイルする。こういう関係を毎回人間が覚えて実行するのは面倒だし、間違いやすい。
そこで Makefile に「このファイルを作るには、このファイルが必要で、このコマンドを実行する」と書いておく。
基本形はこうなる。
target: prerequisites
command
target は作りたいものや実行したい名前、prerequisites は前提になるファイル、command は実際に実行するコマンドである。
ただし、現代の個人開発で便利なのは、ファイル更新の差分ビルドだけではない。test、clean、deploy、screenshots のような「名前付きコマンド集」としても使えるところだ。
.PHONY: test clean deploy
test:
npm test
clean:
rm -rf dist
deploy:
./deploy.sh
このくらいなら、シェルスクリプトを並べるよりも見通しがいい。make help を用意しておけば、プロジェクトで使える操作一覧にもなる。
makeの歴史をざっくり知る
make はかなり古い道具である。
Stuart I. Feldman による論文「Make – A Program for Maintaining Computer Programs」は、Software: Practice and Experience の 1979 年 4 月号に掲載されている。CiNii Research の書誌情報 では、Make は UNIX システム上で 1975 年から使われていたと説明されている。
つまり、make は最近登場した自動化ツールではない。半世紀近く前から、プログラムを複数の部品に分け、それぞれの依存関係を追い、必要な処理を実行するために使われてきた道具である。
今の感覚で見ると、Makefile のタブ必須ルールや独特の変数展開は古くさく感じる。実際、もっと分かりやすいタスクランナーやビルドツールはたくさんある。
それでも make が残っているのは、かなり低いレイヤーで、雑に強いからだと思う。
- 多くの Unix 系環境で使いやすい
- シェルコマンドをそのまま呼べる
- 言語やフレームワークに依存しない
make targetという入口が短い- AI エージェントにも説明しやすい
特に最後が、AI 時代になって急に輝く理由になっている。
AI時代にmakeが便利な理由
AI エージェントに作業を頼むとき、毎回長い手順を自然言語で説明するのはあまりよくない。
「このプロジェクトでは、このスキームで、このデバイス向けに、このスクリプトを使って、必要なら CocoaPods を入れて、最後にこのテストを走らせて」と説明していると、どこかでずれる。
一方で、Makefile に作業の入口をまとめておけば話が早い。
- テストして:
make test - デバッグビルドして:
make build-debug - 実機へ入れて:
make device-debug - App Store 用画像を作って:
make screenshots
こうなると、AI エージェントへの指示がかなり短くなる。
修正後に
make testを実行して、失敗したら原因を見て直してください。
実機確認用に
make device-debugが通る状態まで直してください。
このように書ける。
重要なのは、人間が確認するときも同じコマンドを使えることだ。AI が実行した make test と、自分が実行する make test が同じなら、認識のズレが減る。
もうひとつ地味にうれしいのが、シェルの補完だ。環境にもよるが、make まで入力して Tab キーを押すと、Makefile に書いた test、device-debug、screenshots などのターゲット名を補完できる。npm にも npm completion はあるので補完自体が不可能なわけではないが、プロジェクト固有の作業入口を make のターゲットとして並べておくと、コマンドを思い出す負担がかなり減る。
iOS開発ではXcodeを開かない入口になる
iOS アプリ開発では、普通に考えると Xcode を開いて、スキームを選び、デバイスを選び、ビルドボタンを押す。
もちろん Xcode は必要な場面がある。Storyboard を見る、署名設定を確認する、クラッシュログを追う、GUI で調整する。そういう作業では Xcode が強い。
ただ、日々の開発で毎回 Xcode を操作したいかというと、そうではない。むしろ開発の腰を重くすることがある。
AI エージェントとターミナル中心で開発していると、Xcode の画面操作は文脈の切り替えになる。さっきまで GitHub Issue を見て、ターミナルで AI と対話していたのに、ビルドだけ Xcode へ移動する。この切り替えが地味に重い。
そこで、iOS アプリの Makefile に次のようなターゲットを用意した。
% make help
Usage: make <target> [VARIABLE=value]
build Build Debug for the iOS Simulator
test Run unit and UI tests
test-unit Run unit tests only
test-ui Run UI tests only
screenshots Auto-capture screenshots and generate App Store images
build-debug Build Debug for device without deploying
build-release Build Release for device without deploying
device-debug Build, install, and launch Debug on device
device-release Build and install Release on device
clean Clean the selected simulator scheme
list-devices List available iOS Simulators
list-schemes List workspace schemes
pods Install CocoaPods dependencies
Defaults:
WORKSPACE=memo.xcworkspace
SCHEME=memo-Debug
CONFIGURATION=Debug
SIMULATOR_NAME=iPhone 17
SIMULATOR_OS=latest
DESTINATION=platform=iOS Simulator,name=iPhone 17,OS=latest
これだけあると、ターミナルからほぼ一通りの作業ができる。
make build-debug と make build-release で、デバッグモードとリリースモードを一発で切り替えられる。Xcode でスキームや設定を毎回選び直すより、手元の作業としてはかなり軽い。
make device-debug なら、ビルドして、実機に入れて、起動するところまで進められる。make device-release なら、リリース構成で実機インストールできる。
make test、make test-unit、make test-ui は、人間が確認したいときにも便利だし、AI エージェントに自動実行してもらう入口としても使いやすい。
make screenshots では、App Store に投稿するスクリーンショットの作成まで自動化した。これも自分で毎回やると面倒だが、Makefile に入っていればコマンド一発で済む。
実際のMakefile例
この Makefile は、自分が開発している iOS アプリ「スーパーメモ」で使っている。
スーパーメモは、買い物リストや短いメモをタグで整理し、必要なメモを通知センターやロック画面から確認できる iPhone アプリである。このアプリの開発で、ビルド、実機デプロイ、テスト、App Store 用スクリーンショット生成までを make にまとめた。
実際には、次のような Makefile にしている。
WORKSPACE ?= memo.xcworkspace
SCHEME ?= memo-Debug
CONFIGURATION ?= Debug
SIMULATOR_NAME ?= iPhone 17
SIMULATOR_OS ?= latest
DESTINATION ?= platform=iOS Simulator,name=$(SIMULATOR_NAME),OS=$(SIMULATOR_OS)
DERIVED_DATA ?= build/DerivedData
XCODEBUILD = xcodebuild \
-workspace "$(WORKSPACE)" \
-scheme "$(SCHEME)" \
-destination "$(DESTINATION)" \
-derivedDataPath "$(DERIVED_DATA)"
.DEFAULT_GOAL := help
.PHONY: help build build-debug build-release device-debug device-release test test-unit test-ui screenshots clean list-devices list-schemes pods
help:
@printf "%s\n" "Usage: make <target> [VARIABLE=value]"
@printf "\n"
@printf "%-16s %s\n" "build" "Build Debug for the iOS Simulator"
@printf "%-16s %s\n" "test" "Run unit and UI tests"
@printf "%-16s %s\n" "test-unit" "Run unit tests only"
@printf "%-16s %s\n" "test-ui" "Run UI tests only"
@printf "%-16s %s\n" "screenshots" "Auto-capture screenshots and generate App Store images"
@printf "%-16s %s\n" "build-debug" "Build Debug for device without deploying"
@printf "%-16s %s\n" "build-release" "Build Release for device without deploying"
@printf "%-16s %s\n" "device-debug" "Build, install, and launch Debug on device"
@printf "%-16s %s\n" "device-release" "Build and install Release on device"
@printf "%-16s %s\n" "clean" "Clean the selected simulator scheme"
@printf "%-16s %s\n" "list-devices" "List available iOS Simulators"
@printf "%-16s %s\n" "list-schemes" "List workspace schemes"
@printf "%-16s %s\n" "pods" "Install CocoaPods dependencies"
@printf "\n"
@printf "%s\n" "Defaults:"
@printf " WORKSPACE=%s\n" "$(WORKSPACE)"
@printf " SCHEME=%s\n" "$(SCHEME)"
@printf " CONFIGURATION=%s\n" "$(CONFIGURATION)"
@printf " SIMULATOR_NAME=%s\n" "$(SIMULATOR_NAME)"
@printf " SIMULATOR_OS=%s\n" "$(SIMULATOR_OS)"
@printf " DESTINATION=%s\n" "$(DESTINATION)"
build:
$(XCODEBUILD) -configuration "$(CONFIGURATION)" build
test:
$(XCODEBUILD) test
test-unit:
$(XCODEBUILD) test -only-testing:memoTests
test-ui:
$(XCODEBUILD) test -only-testing:memoUITests
screenshots:
./scripts/generate-app-store-screenshots.sh
screenshots-themes:
./scripts/generate-app-store-screenshots.sh --only-themes
screenshots-composite:
./scripts/generate-app-store-screenshots.sh --skip-snapshot
build-debug:
DEPLOY=0 ./scripts/build-debug.sh
build-release:
DEPLOY=0 ./scripts/build-release.sh
device-debug:
./scripts/build-debug.sh
device-release:
./scripts/build-release.sh
clean:
$(XCODEBUILD) clean
list-devices:
xcrun simctl list devices available
list-schemes:
xcodebuild -list -workspace "$(WORKSPACE)"
pods:
pod install
ポイントは、難しい Makefile 技法を使っていないことだ。
WORKSPACE ?= memo.xcworkspace のようにデフォルト値を置き、必要なら実行時に上書きできる。
make build SCHEME=memo-Release CONFIGURATION=Release
このように、環境ごとの差し替えもできる。
そして .DEFAULT_GOAL := help にしておくと、単に make と打ったときにヘルプが出る。Makefile は便利だが、ターゲット名は忘れやすい。だから、忘れる前提で make help を最初から作っておく。
AIエージェントに作ってもらうと現実的になる
正直、この Makefile を自分で一項目ずつ調べながら作ると思うと、かなり大変で、あまりやりたくない。
xcodebuild の引数、実機デプロイ用スクリプト、スクリーンショット生成、help の整形、デバッグとリリースの切り替え。ひとつずつ詰めると、地味に時間がかかる。
しかし AI エージェントに任せると、かなりサクッと作れてしまう。
ここで面白いのは、AI エージェントが Makefile を作る側にも、Makefile を使う側にも回れることだ。
最初はこう頼む。
この iOS プロジェクトで、よく使う xcodebuild と実機デプロイのコマンドを Makefile にまとめてください。
make helpで一覧が出るようにしてください。
すると、AI エージェントが既存のワークスペース名、スキーム名、スクリプト構成を読みながら Makefile を作ってくれる。
その後はこう頼める。
変更後に
make testを実行してください。
実機向けに
make build-releaseが通るか確認してください。
実際には、make test などといちいち言わずとも「テストして」で通じることが多い。
AI が作った作業入口を、次の AI 作業でそのまま使えるようになる。これによって、開発作業に統一感が出るようになった。
これは小さいようで大きい。プロジェクト固有の作業手順が、自然言語のプロンプトではなく、リポジトリ内の実行可能な知識として残る。
MakefileはプロジェクトのREADMEより強い場合がある
README に「テストはこのコマンドで実行します」と書くのも大事だ。
ただ、README は読まれないことがある。管理を忘れて古くなることもある。書いてあるだけでは実行されない。
Makefile は、説明であると同時に実行入口でもある。
make help を見れば何ができるか分かる。make test を叩けば実際に走る。間違っていれば、その場で失敗する。
AI エージェントにとってもこれは扱いやすい。README を読んで手順を推測するより、Makefile のターゲットを見て実行するほうがブレにくい。
個人的には、AI 時代の Makefile は「人間と AI の共通操作盤」だと思っている。
書きすぎないほうが使いやすい
もちろん便利だからといって、Makefile に何でも詰め込むと逆に分かりにくくなる。
最初は、よく使うものだけでいい。
make helpmake buildmake testmake cleanmake device-debugmake build-release
このくらいで十分役に立つ。
また、Makefile の中に長い処理を全部書くより、複雑な処理は scripts/ 配下のシェルスクリプトへ逃がし、Makefile は入口に徹するほうが読みやすい。
今回の例でも、実機ビルドやスクリーンショット生成は ./scripts/build-debug.sh や ./scripts/generate-app-store-screenshots.sh に任せている。Makefile は、それらを覚えやすい名前で呼び出しているだけだ。
このくらいの薄さがちょうどいい。
関連記事と参考リンク
- GitHub IssueからCodexを動かすagent runnerを作ったら世界線が変わった
- AIエージェントに没頭した20日間。Codex、Claude、iOSアプリ開発まで
- git worktreeでAI時代の並列開発を試す。Codexに別ブランチを同時に任せるには
- シェルコマンドの本をAmazonで探す
- Claude Code による AI 駆動開発入門をAmazonで探す
まとめ
make は古い道具だが、AI エージェント時代になってむしろ便利さが増している。
理由は、プロジェクト固有の作業を短いコマンドにまとめ、人間と AI エージェントが同じ入口から実行できるようにするからだ。
iOS 開発でも、make device-debug、make device-release、make test、make screenshots のようなターゲットを用意しておけば、Xcode を開かずに進められる作業がかなり増える。
AI エージェントに Makefile を作ってもらい、その Makefile を次の AI 作業で使う。これが回り始めると、GitHub Issue とターミナルだけで進む範囲が一気に広がる。
昔からある make を、AI 時代の開発操作盤として再発見した感じがある。