Makefileの基本
ソースが増えると毎回長い gcc コマンドを打つのは大変です。Makefile にビルド手順を書いておけば、make と打つだけで必要な部分だけを再ビルドできます。基本の書式は「ターゲット: 依存ファイル」の行に続けて、TABで字下げした実行コマンドを書きます。make は依存ファイルの更新時刻を見て、変更があったターゲットだけを作り直すため無駄が省けます。コマンド行の字下げは必ずスペースではなくTAB文字である必要があり、ここを間違えると Missing separator エラーになります。
ソースファイルが1つのうちは gcc prog.c -o prog で十分だ。だが、プログラムが育ってファイルが何枚にもなると話が変わる。
複数の .c をそれぞれコンパイルしてオブジェクトファイル(.o)を作り、それらをまとめてリンクし……という長いコマンドを、変更のたびに手で打ち直すのは現実的ではない。打ち間違いも起きる。
どのファイルを作り直すべきかを毎回考えるのも面倒で、考え違いをすれば古い .o が混じったまま動かしてしまう事故も起きる。
この面倒と事故を一掃する仕組みが Makefile だ。ビルド手順を1つの設定ファイルにあらかじめ書いておき、make と打つだけで自動実行させる。
make コマンドはこの Makefile を読んでビルドを進める。Makefile にやり方を一度書いておけば、以後は make の3文字でビルドが完了する。
しかも、変更があった部分だけを賢く作り直してくれるため、扱うソースの規模が大きくなるほど効果が大きくなる。その「やり方」の書き方を見ていこう。
🧱 ルールは3つの要素でできている
Makefile は、いくつもの「ルール」を並べて書く。身構える必要はない。1つのルールは3つの要素からできているだけだ。
作りたい成果物であるターゲット、それを作るのに必要な入力である依存ファイル、そして実際に実行するコマンドである。
書式は、1行目に「ターゲット: 依存ファイル」と書き、次の行から実行コマンドを字下げして書く。
たとえば次のような形だ。hello: hello.c と書いた行に続けて、字下げして gcc hello.c -o hello と書く。
これは「hello を作るには hello.c が必要で、作り方は gcc hello.c -o hello だ」という意味のルールである。
このターゲットと依存ファイルの結び付きが、これから見る make の判断のすべての土台になる。
⏱ make が全部を作り直さずに済む理由
make の核心は、依存ファイルの更新時刻(タイムスタンプ)を見て、必要なものだけを作り直す点にある。
ここで、さきほどのターゲットと依存ファイルの結び付き、すなわち「作るもの」と「そのために必要なもの」の関係が効いてくる。
make は、ターゲットと依存ファイルの更新時刻を比べ、依存ファイルのほうが新しい(=ソースが変更された)ときだけコマンドを実行する。逆に、ターゲットのほうが新しければ「もう最新だ」と判断して何もしない。
だから、たくさんのファイルがあっても、編集した1つに関係する部分だけが再コンパイルされ、触っていないファイルは再利用される。
たとえば main.o file1.o file2.o の3つをリンクして実行ファイルを作る構成を考える。それぞれの .o は対応する .c を依存ファイルに持つルールで作られている。
ここで file1.c だけを直したなら、make はタイムスタンプを比べて file1.o だけを作り直し、main.o と file2.o はそのまま使い、最後にリンクし直すだけで済ませる。
ソースが何十枚もある大きなプログラムほど、この「変わったところだけ」の効き目は大きく、ビルド時間の無駄を劇的に減らす。
🧹 ファイルを作らないターゲット
ターゲットは必ずしも実ファイルである必要はない。実ファイルを作らない便宜的なターゲットもよく使われる。
代表が clean で、生成物を消すためのルールだ。clean: と書いた行に続けて、字下げして rm -f *.o hello のように書いておくと、make clean と打つだけで .o や実行ファイルをまとめて削除できる。
同様に、複数の成果物をまとめてビルドする入り口として all というターゲットを用意し、Makefile の先頭付近に置く慣習もある。
先頭に置くのには理由がある。make を引数なしで実行すると、ファイルの先頭にある最初のターゲットが既定で作られるため、all を先頭に置いて「make 一発で全部ビルド」を実現するのだ。
clean のように実ファイルと結び付かないターゲットは、慣れてくると .PHONY という指定で明示するのが望ましいとされる。
⚠ 字下げを間違えると動かない
Makefile で初学者がほぼ必ず一度ははまるのが、コマンド行の字下げだ。
ルールのコマンド行の先頭は、スペースではなく必ずTAB文字でなければならない。見た目は同じ「字下げ」でも、make はこの1文字を厳密に区別する。
スペースで字下げしてしまうと、make は Makefile:行番号: *** missing separator. Stop. という、いわゆる Missing separator エラーを出して止まる。
やっかいなのは原因が目に見えにくいことだ。エディタの設定によってはTABが自動でスペースに変換されることがあり、これが見えにくいトラブルを生む。
「コマンドの行頭はTAB」。これはルールというより鉄則として、最初に強く意識しておいてほしい。エラーが出たら、まず字下げがTABかスペースかを疑うのが定石だ。
🛠 Makefileは汎用の自動化ツール
Makefile は、Cプログラムのビルドにとどまらず、決まった手順を名前で呼び出す汎用の自動化ツールとして広く使われる。
テストを走らせる test、配置を行う install、ドキュメントを生成する docs といったターゲットを用意し、make test のように呼び分ける運用は一般的だ。
組込みLinuxの開発でも、カーネルや各種ソフトのビルドはほぼ make を起点に進む。
実際の現場では、毎回同じファイル名を書くのを避けるため変数(CC や CFLAGS など)を使ったり、パターンルールで .c から .o への変換をまとめて書いたりと、より省力化した書き方に発展していく。
だが出発点はあくまで「ターゲット・依存ファイル・コマンド」という素朴なルールの集まりだ。
まずは小さな Makefile を1つ手で書き、make と make clean が動く感覚をつかむことが、自動ビルドへの確かな入り口になる。