UNIX/Linuxの部屋 コマンド:make


※空白区切りで AND 検索 (例:「ファイル 削除」)

コマンド make 依存関係を調べて最適な実行をする。

一般的には、プログラムをコンパイルする際に、Makefile というファイルに依存関係を記述し、make に自動的にコンパイルさせる、といった使い方をする。一括処理をさせるだけならシェルスクリプトを使えばよいのでは?、と思うかもしれないが、make はファイルの依存関係と それらのタイムスタンプを調べ、必要な処理だけを行うという機能を持っている。

例えば program というコマンドのソースが、source-1.c、source-2.c、source-3.c、program.h という 3つのソースファイルと1つのヘッダファイルから構成され、各 source-*.c の中で program.h を include しているとする。一括してコンパイルしたい場合は
% cc -o program source-1.c source-2.c source-3.c
とすればよいが、あるソースファイルだけを書き換えた場合でも、全てのソースをいちいちコンパイルすることになり、時間の無駄である。そこで Makefile というファイルを作成し、その中に
program: source-1.o source-2.o source-3.o
cc -o program source-1.o source-2.o source-3.o
source-1.o: source-1.c program.h
cc -c source-1.c
source-2.o: source-2.c program.h
cc -c source-2.c
source-3.o: source-3.c program.h
cc -c source-3.c
と書いておく。
  • 1行目は、program は source-1.o、source-2.o、source-3.o から構成される
  • 2行目は、program を作成するには cc -o program ... とすればよい
ということを示している。その後の行も同じく、
  • source-1.o は source-1.c と program.h から構成される
  • source-1.o を作成するには cc -c source-1.c とすればよい
ということを表す。一般的には Makefile は
ターゲット: 依存するファイル
(TAB)ターゲットを作成するためのコマンド
という情報の羅列である。なお、TAB の部分は必ず TAB にしなければならない (スペースではダメ)。

何もない状態 (コンパイルしていない状態)で make を実行すると、カレントディレクトリの Makefile が読まれ、最初に出てくるターゲット名 (この場合は program) を作成しようとする。よって
% make
cc -c source-1.c
cc -c source-2.c
cc -c source-3.c
cc -o program source-1.o source-2.o source-3.o
が実行される。ここで source-1.c の内容を書き換えたとしよう。ファイルが更新されたので、source-1.c のタイムスタンプが新しくなった。再度 make を実行すると、make は各ファイルのタイムスタンプを調べる。その結果 source-1.o より source-1.c が新しいので
% make
cc -c source-1.c
cc -o program source-1.o source-2.o source-3.o
と、source-2.c と source-3.c は再コンパイルせず、必要なコンパイルだけを実行してくれる。一方、program.h を書き換えた場合は、全てのソースがこのヘッダファイルを参照しているため、
% make
cc -c source-1.c
cc -c source-2.c
cc -c source-3.c
cc -o program source-1.o source-2.o source-3.o
と、全てのファイルがコンパイルし直される。

サフィックスルール
さて、先程の Makefile だが、同じファイル名が何度も繰り返し現れ、非常に無駄が多い。もう少し短くまとめてみよう。make が力を発揮するのは、サフィックス (拡張子) の関係を指示したときである。拡張子と拡張子との関係を「サフィックスルール」という。

ここで *.o は *.c から生成されることに着目しよう。特に、source-1.o は source-1.c から作られ、source-2.o は source-2.c から作られ…というふうに、foo.c → foo.o のように拡張子のみが段階的に変わっていくことに注目してほしい。

そこで役に立つのがサフィックスルールである。
.SUFFIXES : .c .o
.c.o:
cc -c $<
と書くと、*.c についてそれぞれ cc -c を実行し、*.o を作成してくれる。$< は現在のターゲットを表している。つまり、
program: source-1.o source-2.o source-3.o
cc -o program source-1.o source-2.o source-3.o
.SUFFIXES : .c .o
.c.o:
cc -c $<
と Makefile に記述した場合、
  • program は source-1.o source-2.o source-3.o から作成される
  • source-1.o は source-1.c から作成される
  • source-2.o は source-2.c から作成される
  • source-3.o は source-3.c から作成される
ということを表している。ここで source-3.c だけを更新して make すると
% make
cc -c source-3.c
cc -o program source-1.o source-2.o source-3.o
が実行されるが、このとき make は以下のような順番で依存関係のチェックを行う。
  • program は source-1.o source-2.o source-3.o から作成されるので、*.o を新たに作成すべきかどうか調べる
  • .c.o: により、source-1.o は source-1.c から作成されることを知り、source-1.c のタイムスタンプを調べる → source-1.o の方が新しい → コンパイルする必要なし
  • 同様に source-2.o は source-2.c から作成されることを知り、source-2.c のタイムスタンプを調べる → source-2.o の方が新しい → コンパイルする必要なし
  • source-3.o は source-3.c より古い (source-3.c が更新されている) ので、cc -c source-3.c を実行 ($< には現在のターゲットである source-3.cが入っている)
  • source-3.o が更新されたので、cc -o program ... で program を作成
ただしこれでは *.c と program.h の依存関係が示されていない。そこで
.c.o: program.h
cc -c $<
とすると、source-1.o は source-1.c と program.h から作成されることを表すことができる。

もう少し読みやすくするためにマクロというものを使おう。マクロは変数のようなもので、
MACRO = value
で代入し、$(MACRO) や ${MACRO} で値を参照することができる。目的のファイルは program で、オブジェクトファイルは 3つの *.o でなので、
TARGET = program
OBJS = source-1.o source-2.o source-3.o
と書ける。また、
CC = cc
Cコンパイラ名もマクロで定義しておこう。このように抽象化しておくと cc の代わりに gcc を使いたくなった場合でも、簡単に変更が可能である (FreeBSD や Linux では cc=gcc だが、Solaris などの cc は商用のコンパイラである)。program は OBJS に依存するので
$(TARGET): $(OBJS)
$(CC) -o $@ $(OBJS)
と書ける。$@ は現在のターゲット名が代入されている。さらに clean というターゲットを作成し、いつでも作業途中の全ファイルを消せるようにしよう。

まとめると以下のような Makefile になる。
TARGET = program
OBJS = source-1.o source-2.o source-3.o
CC = cc

.SUFFIXES : .c .o

$(TARGET): $(OBJS)
$(CC) -o $@ $(OBJS)
.c.o: program.h
$(CC) -c $<
clean:
rm -f $(TARGET) $(OBJS)
以上で、
  • make とタイプすると、実行ファイル program を作成することができる。
  • 一部のファイルを更新すると、必要な部分だけを再コンパイルしてくれる。
  • make clean とすることで、program と *.o を削除する。
という Makefile が完成した。

マクロは make の引数で指定することもできる。
% make CC=gcc
とすると、$(CC) の部分が gcc に置き換わって gcc でコンパイルされる。

なお、make でよく使われるルールはデフォルトルールとして、あらかじめ決まっている。FreeBSD では /usr/share/mk/sys.mk がデフォルトルールとして読み込まれる。実は
CC = cc
.SUFFIXES : .c .o
はデフォルトルールとして定義されているので、先にあげた Makefile では この部分を省略してもよい。

make 実行時は、tee コマンドを併用すると便利。

make してエラー(コンパイラのエラーなどではなく、make 自体のエラー)が出た場合、gmake (GNU make) を使うと うまくいく場合がある。なお、Linux では GNU make が make となっている。

make は奥が深く、とてもここで説明しきれるものではない。詳しく知りたい場合は書籍を購入してほしい。
>> FreeBSDオンラインマニュアル(man) FreeBSD make(1)
>> Linuxオンラインマニュアル(man) Linux make(1)

読み方 make (UNIXコマンド) [めいく]

このコマンドをローマ字読みすると [まけ] になってしまうので alias で kachi という名前にしている人がいる。


頑張って書いたおすすめコンテンツ!