Makefileの解説
初版:2009/12/5
文責:向川康博

Makefileは,一見すると複雑に見えますが, ルールを知ってしまえばその構造は実に単純であることがわかります. makeコマンドを使う利点や高度な利用法については 他のドキュメントに任せるとして, ここでは Makefileの基本的な構造を解説します.

まず,Makefileの基本構造を以下に示します. [作りたいもの] の後にコロン(:)を書き, その後に必要な[材料]を並べます. 次の行には,先頭にTABを入れた後, [作り方]を書きます. これが基本的な構造です.

Makefileの基本
[作りたいもの]: [材料]
(-----TAB-----) [作り方]

次に,具体的な例を見てみましょう. まず,main.cとsub.cという2つのソースから myprogramという実行バイナリを作成することを考えます. makeコマンドを使わずに,ソースから実行バイナリを直接作成する場合は

% gcc main.c sub.c -o myprogram
とします. これだと,一方のファイルを修正しただけですべてのソースファイルのコンパイルが必要となってしまうので,
% gcc -c main.c
% gcc -c sub.c
% gcc main.o sub.o -o myprogram
と別々にオブジェクトファイルを作成して, 最後にリンクしたほうが効率がいいでしょう. 後者を Makefileにすると以下のようになります.

シンプルなMakefile
myprogram: main.o sub.o
           gcc -o myprogram main.o sub.o
main.o:    main.c
           gcc -c main.c
sub.o:     sub.c
           gcc -c sub.c

makeコマンドは,

% make [作りたいもの]
として実行するのが基本ですが,[作りたいもの]を省略した場合には, 一番最初にある[作りたいもの]が作成されます.つまり,
% make
とすれば,myprogramを作成することになり, その材料であるオブジェクトファイル(main.o と sub.o)が存在しないか, あるいはさらにその材料のソースファイル(main.c と sub.c)よりも日付が古ければ 再帰的にオブジェクトファイルがコンパイルされます. つまり,あるファイルを作りたいときに, その材料はどれなのか,どうやって作るのかが必要となるわけで, その関係を記述したものが Makefileなわけです.

さて,実際に使われる Makefileを見ていきましょう. まずは以下の Makefileを見てください.

一般的なMakefileの例
CC            = gcc
CFLAGS        = -O4 -Wall -I/usr/local/include
DEST          = /usr/local/bin
LDFLAGS       = -L/usr/local/lib
LIBS          = -lhoge -lm
OBJS          = main.o sub.o
PROGRAM       = myprogram

all:            $(PROGRAM)

$(PROGRAM):     $(OBJS)
                $(CC) $(OBJS) $(LDFLAGS) $(LIBS) -o $(PROGRAM)

clean:;         rm -f *.o *~ $(PROGRAM)

install:        $(PROGRAM)
                install -s $(PROGRAM) $(DEST)

最初に「○=○」という記述が多数ありますが, これは後で使う変数の定義と思ってください.

以下は実行バイナリを作成する部分です. 基本通りに,作りたいもの,その材料,作り方が順に書かれています.

$(PROGRAM):     $(OBJS)
                $(CC) $(OBJS) $(LDFLAGS) $(LIBS) -o $(PROGRAM)

以下は少し変則的です. 作りたいもの,その材料,作り方が揃っていますが,意味が違います. 「installしろ」という意味で make install と実行しますが, makeとしては「installを作成しろ」と解釈します. installを作成するためには $(PROGRAM)が必要であり (なければ作成します), 作り方は実行バイナリを所定のディレクトリにコピーすることです. 結局,installというものは作成できていないわけですが, 目的は果たしたことになります.

install:        $(PROGRAM)
                install -s $(PROGRAM) $(DEST)

次はもう少し変則的です. 作りたいもの(all)とその材料は示されてますが, 作り方が書いてありません. allを作りたければ,まず$(PROGRAM)を作れという意味しかありません. これは, 作りたいものを指定せずに makeコマンドを実行すると 一番最初にある[作りたいもの]が作成される ことを利用して, とりあえず make と入力すれば実行バイナリが作成されるようにするための仕掛けです. このルールは,一番最初に書かないと意味がありません.

all:            $(PROGRAM)
次はもっと変則的です.まとめて1行に書いてますが,
clean:;         rm -f *.o *~ $(PROGRAM)
きちんと2行に分けて書くと,
clean:
                rm -f *.o *~ $(PROGRAM)
となります.この場合,材料が明記されていません. make cleanとして実行すれば, 材料は不要なので,rmが実行されます.つまり, make cleanは不要なデータを消去する手続きとして使えるわけです.


注意点

先頭のTAB

「作り方」は TABの後に書く必要があります. よく,Makefileをマウスでカット&ペーストすると, そのTABが複数のスペース置き換わってしまって動かないというトラブルを聞きます. 「作り方」の前は TABです.

暗黙のルール

先ほどの Makefileでは,main.cと sub.cから, それぞれ main.o と sub.oを作成する方法を書いていませんでした. 通常 *.cから *.oの作成手順は省略します. しかし,最適化オプション -Oや, インクルードファイルのありかを明示する -I オプションは *.cから *.oの作成時に指定すべきです. そのために使われるのが,CFLAGSです. *.cから *.oが自動的に作成される際に,CFLAGSがオプションとして使われます.

この暗黙のルールは,意外と落とし穴になる場合もあります. make -p とすれば,

  %: %.sh
  #  実行するコマンド (ビルトイン):
        cat $< >$@ 
        chmod a+x $@
  
という定義が見つかります. 「ほげ」というものを作成する際に, 「ほげ.sh」というシェルスクリプトが存在するならば
  cat ほげ.sh > ほげ
  chmod a+x ほげ
  
として作成せよというルールです. Makefileには,「all」を定義してあることが多いこと, その allの作り方は書いていないこと, そして何となく all.shというスクリプトを書いてしまうことが多いことから, all.shというスクリプトが存在すると意図せぬ動作になります.

応用例など

タイムスタンプ

makeでは[作りたいもの]と[材料]のタイムスタンプを比較し, [材料]の方が新しければ作りなおします. ただし,何らかの事情で[材料]のほうが古いけれどもコンパイルし直したい時はどうすればいいでしょうか. [作りたいもの]を削除する? あるいは,[材料]をエディタで開いて無意味に何かを更新して保存する? そういう時には touchコマンドが使えます. touch main.c とすれば,タイムスタンプのみが更新されます. まさに,「触る」わけです.

LaTeX

makeはプログラミングのためだけのツールではありません. 「作りたいものをある材料からどうやって作るか」という工程にマッチすれば, いろいろな用途に使えます.私がよく使うのは,LaTeXによる文章作成です. この場合の Makefileの例を示します.
makeでコンパイル.make lookでビューアの立ち上げ, make pdfで PDFファイルの作成.そして make cleanで,中間ファイルを消します. makeはプログラミングのためだけのツールではなくて, 材料から何かを作るためのルールを記述するための一般的なツールとして使えるわけです.
LaTeXのためのMakefile
main.dvi: main.tex
        latex $<

look:   main.dvi
        xdvi $< &

pdf:    main.dvi
        dvipdfmx -o main.pdf $<

clean:; rm -fr *~ *.toc *.aux *.dvi *.log

Keyword: make Makefile 書き方 ルール 暗黙 タブ TAB