Makefileとはなんぞや
MakefileはC言語などで面倒なコンパイルや実行、ファイル操作などを自動化してくれます。
例えば、a.c b.c c.cというファイルをまとめてコンパイルして、その実行ファイル名をhogeにして実行し、その後実行ファイルを削除する、という一連の流れを行おうとすると、
gcc a.c b.c c.c -o hoge
./hoge
rm -rf hoge
のように毎回3回もコマンドを打つ必要が出てきます。
面倒ですね...
ところがMakefileを使うと、
run:
gcc a.c b.c c.c -o hoge
./hoge
rm -rf hoge
とMakefileに記述し、make run
を実行すると、上のコマンドと同様なことを実行してくれます。
Makefileのメリット
- コンパイルや実行、ファイル操作などのコマンドを自動化してくれる。
- 他の人がコンパイルする際に、必要なファイルやオプションなどを知る必要がなく実行できる。
Makefileの書き方
初級編
Makefileの基本構造
Makefileは主に二つの構造から成り立っています。
一つは実行ファイル名やコンパイルしたいファイルをまとめた変数などを設定する部分。
SRCS=a.c b.c c.c
NAME=hoge
もう一方は、コンパイルや実行、ファイル操作などをまとめるルールを記述する部分です。
run:
gcc a.c b.c c.c -o hoge
./hoge
rm -rf hoge
ルールの記述
ルールの基本構造は以下のようになっています。
[ターゲット]: [依存ファイル]
[実行したいコマンド]
依存ターゲット
については中級編でお話しするため一旦忘れてください。
ターゲット
は生成したいファイル名や、動作名(.oファイルを消したかったらcleanなど)を書くことが多いです。実際にそのターゲットのルールを実行したい場合にはmake [ターゲット]
で実行できます。
実行したいコマンド
にはターゲットを実行した際に行ってほしい、コンパイルや実行、ファイル操作などのコマンドを記述します。Tab
で開けて記述することに注意してください。
変数の設定
ここでは、実行ファイル名やコンパイルしたいファイル、フラグなどを変数として設定することができます。
基本的にな構造としては左辺が変数名
、右辺が変数の定義
となっています。
[変数名]: [変数の定義]
例えば以下のようなMakefileがあったとします。
run:
gcc a.c b.c c.c -o hoge -Wall
./hoge
rm -rf hoge
このMakefileに対して変数を設定すると以下のようになります。
CC=gcc
SRCS=a.c b.c c.c
Name=hoge
CFLAGS=-Wall
RM=rm -rf
run:
$(CC) $(SRCS) -o $(NAME) $(CFLAGS)
./$(NAME)
$(RM) $(NAME)
あれ?見づらくなった...?
僕も初めて変数を使ったMakefileを見た時同じことを思いました。
結論から言ってしまうと慣れます。
Makefileには暗黙のルールというのが存在しているため、ソースファイルはSRCS
という変数名にして定義するという慣習があります。
なのでMakefileを何度も見ていくうちに変数名から大体何をやっているかわかるようになります。
また、42個ものソースファイルがあったとして、それをルールに全て書かれると、どこまでがソースファイルなのかわかりにくくなってしまいます。
慣れてくるとコンパイルされているファイル名を知ることなく、このターゲットで何が行われているかが一眼でわかるのでとても良いです。
中級編
依存ファイル
依存ファイルは初級編の部分で少しお話しした、ターゲットの隣に記述するものです。
[ターゲット]: [依存ファイル]
[実行したいコマンド]
依存ファイルはターゲットを実行する際に必要なファイルを記述します。
例えば以下のような例ではhoge
という実行ファイルを作るためには、hoge.o
, hoge2.o
という二つのファイルが必要であるため、make hoge
を実行すると、hogeのコマンドを実行する前にhoge.o
, hoge2.o
のターゲットが実行されます。
hoge: hoge.o hoge2.o
gcc -o hoge hoge.o hoge2.o
hoge.o:
gcc -c hoge.c -o hoge.o
hoge2.o:
gcc -c hoge2.c -o hoge2.o
$ make hoge
gcc -c -o hoge.o hoge.c
gcc -c -o hoge2.o hoge2.c
gcc -o hoge hoge.o hoge2.o
動的マクロ
-
$@
現在のターゲットの名前 -
$?
ターゲットよりも新しい依存関係のリスト -
$<
make が暗黙の規則を使用するときの依存ファイル名 -
$*
接尾辞を除いたターゲット名 -
$%
処理されるライブラリメンバーの名前 -
$^
依存ファイルのリスト
具体的な使い方についてはこちらの記事がよくまとまっていました。
パターンルール
c言語ではソースファイル(.c)をコンパイルしてオブジェクト(.o)を出力させるのが一般的です。
gcc -c a.c => a.oというオブジェクトファイルを生成
gcc -c b.c
gcc -c c.c
cc -o hello a.o b.o c.o => helloというプログラムを生成
これを42個ものファイルに対して書くのは大変です...
そこでMakefileではオブジェクトファイル(.o)を同一名のソースファイル(.c)から生成する際に、%.o: %.c
というターゲットを用意することでこれらの処理をまとめて行うことができます。
特殊変数
変数を設定することができることはお伝えしましたが、デフォルトで変数が用意されているものもあります。それらは自分で変数の定義を明記することなく、デフォルトの値で呼び出すことができます。以下に代表的な4つの変数です。
-
$(CC)
Cのコンパイルを行う。(デフォルトはcc
) -
$(RM)
ファイルの削除を行う。(デフォルトはrm -f
) -
$(AR)
アーカイブの作成、更新、および、アーカイブからの抽出を行う。(デフォルトはar
) -
$(CFLAGS)
Cのコンパイラに与えるフラグ(デフォルトは無し)
暗黙のルール
hoge: hoge.o hoge2.o
cc -o hoge hoge.o hoge2.o
というようなMakefileがあった場合に、hoge.oは依存ターゲットとして記述されていますが、このMakefile上にはそのターゲットが存在していません。このような場合に、Makefileでは暗黙のルールを参照します。
C言語の場合だと、$(CC) -c $(CPPFLAGS) $(CFLAGS)
が暗黙のルールとして適用され、.cから.oを生成します。
つまり以下のように記述されていることと同義だと言えます。
hoge: hoge.o hoge2.o
cc -o hoge hoge.o hoge2.o
hoge.o:
$(CC) -c $(CPPFLAGS) $(CFLAGS) hoge.c -o hoge.o
hoge2.o:
$(CC) -c $(CPPFLAGS) $(CFLAGS) hoge2.c -o hoge2.o
その他
- ターゲットを指定せずにmakeを実行すると、一番上のターゲットが実行される。
run:
gcc a.c b.c c.c -o hoge
hoge2:
echo "これはでない"
$ make
gcc a.c b.c c.c -o hoge
上級編
relink
relinkとは、実行ファイルなど生成したいファイルが既に存在しているにも関わらず、コンパイルやアーカイブファイルの作成を実行の度に行ってしまうことです。
run:
gcc a.c b.c c.c -o hoge
$ make
gcc a.c b.c c.c -o hoge
$ make
gcc a.c b.c c.c -o hoge
上記のような場合ですと、1回目のmakeでhogeという実行ファイルが作成されたにも関わらず、2回目でも全く同様に実行ファイルを生成してしまっています。
これはいけないことなのでしょうか?
答えはもちろんYesです。例えば420個もの大量ななソースファイルがあるとして、変更差分がないにも関わらず、それを毎回コンパイルするのは効率がいいとはとても言えません。
Makefileではターゲット名と生成ファイル名を同一にする
ことで、relinkを起こさないように対策をすることができます。
hoge:
gcc a.c b.c c.c -o hoge
$ make
gcc a.c b.c c.c -o hoge
$ make
make: `hoge` is up to date.
上記のような場合ですと、1回目のmakeでhogeという実行ファイルが作成されるのは同様ですが、2回目は既にhogeが存在しているため、コンパイルが実行されませんでした。
ソースファイルに変更があった場合には、再度生成が行われます。
$ make
gcc a.c b.c c.c -o hoge
$ make
make: `hoge` is up to date.
$ touch a.c
$ make
gcc a.c b.c c.c -o hoge
最後に
ざっくりとMakefileについてお伝えしてきましたが、この記事だとMakefileの半分も説明できてないぐらいMakefileが奥が深いです。
Makefileは知れば知るほど便利になってくるので、これを機に皆さんも色々と勉強して見ましょう!
参考文献