makeコマンドを使えば、Makefileにあらかじめ記述しておいた手順にしたがって、C/C++などのソースファイルから実行ファイルを自動で生成できます。
Makefileの基本的な構造は、
処理名: 依存するファイルあるいは処理名
[タブひとつ]実行されるコマンド
となります。以下のサンプルを使用するとき、タブがうまくコピーできなくてエラーになるかもしれません。手元でタブに置換して使用してください。
Makefile
Sample: sample.cpp
g++ -o sample sample.cpp
sample.cpp
#include <iostream>
using namespace std;
int main() {
cout << "This is a sample." << endl;
return 0;
}
実行例
$ make
g++ -o sample sample.cpp
$ ./sample
This is a sample.
処理は複数記述できます。makeコマンドの引数を指定しなければ一番上の処理が実行されます。
Makefile
Sample1: sample1.cpp
g++ -o sample1 sample1.cpp
Sample2: sample2.cpp
g++ -o sample2 sample2.cpp
sample1.cpp
#include <iostream>
using namespace std;
int main() {
cout << "This is Sample1." << endl;
return 0;
}
sample2.cpp
#include <iostream>
using namespace std;
int main() {
cout << "This is Sample2." << endl;
return 0;
}
Sample2の処理を実行したければ、
$ make Sample2
とします。
変数を利用できます。
Makefile
CC = g++
CFLAGS = -g -Wall
Sample1: sample1.cpp
$(CC) $(CFLAGS) -o sample1 sample1.cpp
Sample2: sample2.cpp
$(CC) $(CFLAGS) -o sample2 sample2.cpp
処理をネストさせることができます。一番上にALLという名称の処理を用意して、依存する処理名にSample1とSample2を半角スペースで区切って指定します。すると、ALLの処理を実行すると再帰的にSample1,Sample2の処理も実行されます。
Makefile
CC = g++
CFLAGS = -g -Wall
ALL: Sample1 Sample2
Sample1: sample1.cpp
$(CC) $(CFLAGS) -o sample1 sample1.cpp
Sample2: sample2.cpp
$(CC) $(CFLAGS) -o sample2 sample2.cpp
ヘッダファイルが更新された場合にも処理が実行されるようにするためには、依存ファイルを記述する箇所にヘッダファイルを追記します。
Makefile
CC = g++
CFLAGS = -g -Wall
ALL: Sample1 Sample2
Sample1: sample1.cpp sample1.h
$(CC) $(CFLAGS) -o sample1 sample1.cpp
Sample2: sample2.cpp sample2.h
$(CC) $(CFLAGS) -o sample2 sample2.cpp
処理のネスト関係を利用すると、分割コンパイル (オブジェクトファイルmain.oとsub.oを-cフラグで別々に生成してから、ALLの処理でリンク) もできます。
Makefile
CC = g++
CFLAGS = -g -Wall
ALL: main.o sub.o
$(CC) $(CFLAGS) -o main main.o sub.o
main.o: main.cpp
$(CC) $(CFLAGS) -o main.o -c main.cpp
sub.o: sub.cpp sub.h
$(CC) $(CFLAGS) -o sub.o -c sub.cpp
main.cpp
#include <iostream>
#include "sub.h"
using namespace std;
int main() {
cout << sub::str << endl;
return 0;
}
sub.h
namespace sub {
extern char str[32];
}
sub.cpp
#include "sub.h"
namespace sub {
char str[32] = "Externally defined string.";
}
実行例
$ make
g++ -g -Wall -o main.o -c main.cpp
g++ -g -Wall -o sub.o -c sub.cpp
g++ -g -Wall -o main main.o sub.o
$ ./main
Externally defined string.
$@
と $<
という特殊変数を用いると Makefile 内の共通部分の記述を省略できます。
Makefile
CC = g++
CFLAGS = -g -Wall
ALL: main.o sub.o
$(CC) $(CFLAGS) -o main main.o sub.o
main.o: main.cpp
$(CC) $(CFLAGS) -o $@ -c $<
sub.o: sub.cpp
$(CC) $(CFLAGS) -o $@ -c $<
sub.o: sub.h
$@
には処理名が入っており、$<
には依存するファイルあるいは処理名のうち更新されたものが入っています。sub.hだけ更新される場合があやしいので、sub.oを二つに分けてみました。
どの処理名よりも先に.cpp.oという処理を記述すると、依存ファイルがcppで処理名がoの処理を共通化させることができます。
Makefile
CC = g++
CFLAGS = -g -Wall
.cpp.o:
$(CC) $(CFLAGS) -o $@ -c $<
ALL: main.o sub.o
$(CC) $(CFLAGS) -o main main.o sub.o
sub.o: sub.h
.cpp.oを用いない別の記述も可能です。処理名がワイルドカード%.oで一致した場合、依存ファイル%.cppの%に%.oの.oを除いた部分が代入されます。
CC = g++
CFLAGS = -g -Wall
%.o: %.cpp
$(CC) $(CFLAGS) -o $@ -c $<
ALL: main.o sub.o
$(CC) $(CFLAGS) -o main main.o sub.o
sub.o: sub.h
拡張子を置換したリストを生成するためにもワイルドカードを使用できます。
Makefile
CC = g++
CFLAGS = -g -Wall
OBJS = main.o sub.o
DOCS = $(OBJS:%.o=%.txt)
TARGET = main
%.o: %.cpp
$(CC) $(CFLAGS) -o $@ -c $<
ALL: $(OBJS)
$(CC) $(CFLAGS) -o $(TARGET) $(OBJS)
sub.o: sub.h
docs:
touch $(DOCS)
clean:
rm -rf $(OBJS) $(TARGET) $(DOCS)
実行例
$ make
g++ -g -Wall -o main.o -c main.cpp
g++ -g -Wall -o sub.o -c sub.cpp
g++ -g -Wall -o main main.o sub.o
$ make docs
touch main.txt sub.txt
$ make clean
rm -rf main.o sub.o main main.txt sub.txt
@
を付けるとコマンドが出力されなくなります。
$ make
ok
echo ok2
ok2
Makefile
hello:
@echo ok
echo ok2
.PHONY: hello
hello:
@echo ok
echo ok2
同名のファイルが存在しており .PHONY
がない場合、タスクが実行できなくなります。
$ ls
hello Makefile
$ make
make: 'hello' is up to date.
.DEFAULT_GOAL
を指定すると、二つ目以降のタスクが既定で実行されるように設定できます。
$ make
help
Makefile
.DEFAULT_GOAL := help
hello:
@echo ok
help:
@echo help