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