負けないための make

make の簡単な使い方をまとめます。通常の C 言語のコンパイルができることを前提に話します。

make とは

make は、決められた規則に従って必要なコマンドを自動的に実行してくれるツールです。もともとは C 言語のコンパイル・リンクを行うために作られましたが、現在ではソフトウェアのインストールや一時ファイルの消去など、様々な場面で使われています。

make は、更新時刻を見て必要なファイルのコンパイルだけを行ってくれるなど、ソフトウェアの生産性を大幅に向上させることが可能です。

make を使うためにすることは、Makefile と呼ばれる設定ファイルを書くことだけです。Makefile を書けば、後は makeと叩くだけで必要なことが全て行なわれます。

最初の Makefile

まず簡単な Makefile の書き方を説明します。ここでは、prog1.c, prog2.c という C 言語のソースと、ライブラリ libm.a を使って、実行ファイル prog を作ることを考えましょう。Makefile を使わないならば、次のようにしてコンパイルをするでしょう。

gcc prog1.c prog2.c -lm -o prog

毎回これを入力してコンパイル・リンクをするのはそんなに大変ではないかもしれません。しかし、ファイルの数が 100 個あったらどうでしょう? 100 個もあるとコンパイルだけでも時間がかかってしまいます。分割コンパイルをするために何度もgcc -c prog39.c 等と入力するのは大変です。そんなときこそ make の出番です。

上の例をコンパイル・リンクするために Makefile 必要な記述は次のとおりです。注意するべきは、インデントが必要なことと、インデント には TAB 文字を使わなければならないことです。

prog : prog1.o prog2.o
    gcc -lm -o prog prog1.o prog2.o
prog1.o : prog1.c
    gcc -c prog1.c
prog2.o : prog2.c
    gcc -c prog2.c

それぞれのエントリは、コロンの左のターゲット名、コロンの右にある依存関係、そして TAB 文字から始まる、ターゲットを作成するためのコマンドからなっています。

make と叩くと、まず最初に現れるターゲットを作成しようとします。つまり、この例だと prog です。prog : prog1.o prog2.o は、prog には prog1.o, prog2.o という依存関係があるという意味で、これは prog を作るためには prog1.o と prog2.o が必要であるということを示しています。そこでまず prog1.o と prog2.o というターゲットを作ることを試みます。それが出来たとしたら、gcc -o prog prog1.o prog2.o によって、prog が作られます。

prog1.o を作るには、prog1.o というターゲットを持つエントリを見ると prog1.c が必要であるが分かりますが、prog1.c というターゲットはありません。この場合、prog1.c というファイルがあれば prog1.c が作られていると解釈します。prog1.c が作られているとわかったら、prog1.o は gcc -c prog1.c によって作られます。prog2.o も同様です。

また、この状態で prog1.c だけを更新し make と叩くと、ファイルの更新時刻を見て prog1.o だけを再生成してくれます。prog2.o は再生成する必要がないと判断され再生成されません。

2回目の Makefile

さて、上記の Makefile では、prog1.o : prog1.c … というエントリを何度も書かなければならないため大変面倒です。実は、これは1つのエントリで記述することが出来ます。これはサフィックスルールと呼ばれます。

.c.o :
    gcc -c $<

このエントリは、filename.o というターゲットは依存関係 filename.c から gcc -c filename.c で作ることが出来る、ということを表しています。$< はマクロです。このエントリを作るために必要なターゲットで、現在のターゲットよりも後で変更されたものを表しています。

このサフィックスルールを使えば、最初の例は次のように書き直せます。

prog : prog1.o prog2.o
    gcc -lm -o prog prog1.o prog2.o

.c.o :
    gcc -c $<

3回目の Makefile

今度は、prog1.o, prog2.o, …, prog9.o まで 9 つのファイルが必要な場合を考えましょう。この場合 progi.o をターゲットとコマンドに何度も書くのは大変ですし、ファイルを追加した場合、両方に定義を追加しなければなりません。この場合、マクロを用いて後で定義を置き換えるようにします。

OBJS = prog1.o prog2.o ... prog9.o
prog : $(OBJS)
    gcc -lm -o prog $(OBJS)

.c.o :
    gcc -c $<

$(OBJS) は、$(OBJS) を OBJS = … で定義された右辺に置き換えなさいという意味です。これで、ファイルを追加した場合は OBJS だけを変更すれば良くなり、メンテナンス性が増します。

コンパイルオプションや、リンカオプションもマクロにしてしまうことが可能です。$@ はターゲット名を表すマクロです。

CC = gcc
LD = gcc
LIBS = -lm
CFLAGS =
LDFLAGS =
OBJS = prog1.o prog2.o

prog : $(OBJS)
    $(LD) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)

.c.o :
    $(CC) $(CFLAGS) -c $<

これだけでも普段の生活上かなり便利になることでしょう。他のマクロや他の使い方に関しては、機会があれば執筆します。

2006-01-02