GNU make って便利だなって話

Lab の課題で UnixMakefile について学習するという課題が出たので GNU make についてまとめていこうと思います。

make とは

プログラムのビルド作業を自動化するツール。コンパイル、リンク、インストール等のルールを記述したテキストファイル(makefile)に従って、これらの作業を自動的に行う。

Wikipediaより

自分のイメージとしてはコンパイルクリーンなどを自動化できるツールというようなイメージです。


make の仕方

Makefile というテキストファイルに作業内容を記述します。
具体例と共に触れて行きましょう。 以下のような構成のディレクトリ foo があったとします。

foo$ ls
main.c
bar.c bar.h
foobar.c foobar.h

main.c は bar.c と foobar.c から構成されていて、
bar.c は bar.h に、foobar.c は foobar.h に依存しています。

はじめに Makefile を作成します。

foo$ touch Makefile

作成した Makefile を適当なエディタで開きます。
とりあえず以下の内容を書き込みます。
この時、インデントは tab でおこなってください。

CC = gcc
TARGET = main
OBJS = bar.o foobar.o
 
all: $(OBJS)
  $(CC) main.c -o $(TARGET) $(OBJS)
 
.c.o:
  $(CC) -c $<
 
bar.o: bar.h
foobar.o: foobar.h

Makefile の解説

1 ~ 3 行目では変数宣言を行っています。
1 行目では CC という変数に gcc を代入しています。
2 行目で 変数 TARGET に生成される実行ファイルの名前を代入しています。
3 行目では 変数 OBJS に実行ファイル作成時に使用するオブジェクトファイルの名前を代入しています。
5 行目では all は慣用名で、 $(OBJS) に含まれる全てのターゲットを構築します。
実際に make と打った際に実行されるのがこのブロックです。
6 行目には具体的な処理を書いており、以下の実行結果の 4 行目に表されるプログラムが実行されています。
8, 9 行目では オブジェクトファイル ( .o ファイル) は必ず .c ファイルから生成されるというルールがあることを利用して、.o ファイルが必要になった際に .c ファイルから .o ファイルを自動で生成しています。
11, 12 行目では .o ファイルが依存している .h ファイルを宣言しています。

実行結果 

foo$ make
gcc -c bar.c
gcc -c foobar.c
gcc main.c -o main bar.o foobar.o
 
foo$ ls
Makefile
main.c main
bar.c foobar.c
bar.h foobar.h
bar.o foobar.o
 
foo$ ./main
・・・
・・・

実行ファイルや .o ファイルが生成されていることが確認できると思います。


make clean をしよう

make には ターゲットと呼ばれるものを追加することで様々なプログラムを以下のように実行することができます。

make $(target)

ターゲットには前述の all の他に clean や install などがあります。
all は特別で、下のどちらで打っても同じ結果になります。

make
make all

clean について少し触れていきましょう

make clean ですること

clean は基本的に make 時に作った不要なファイルなど (例えば .o ファイルなど) を削除するためのものです。
以下のように新しくターゲットを追加することができます。
ここで TARGET や OBJS は前述の Makefile で定義したものと同じです。

clean:
  rm -rf $(TARGET)
  rm -rf $(OBJS)

これは以下のように実行できます。

foo$ make clean
rm -rf main
rm -rf bar.o foobar.o


make pkg をしよう

今度は ディレクトリをパッケージ化してみましょう。
ターゲット名は pkg とし、ファイルを tar.gz 形式で圧縮します。

方針

はじめにディレクトリを作ります。 (ディレクトリ名はなんでも良いです。)
そのディレクトリにソースファイルを全てコピーします。
そのディレクトリを tar.gz 形式で圧縮します。
その後作成したディレクトリを削除します。
これを Makefile に書くと以下のようになります。

FILE = Makefile main.c bar.c bar.h foobar.c foobar.h
DIR = test
 
pkg:
  if [ -d $(DIR) ]; then rm -rf $(DIR); fi
  mkdir $(DIR)
  cp $(FILE) ./$(DIR)
  tar zcvf $(DIR).tar.gz $(DIR)
  rm -rf $(DIR)

1, 2 行目は変数宣言を行なっています。
5 行目では test という名前のディレクトリが存在していた場合にそのディレクトリを削除しています。
6 行目で test ディレクトリを作成し、
7 行目で FILE に宣言されたファイルを作成した test ディレクトリにコピーしています。
8 行目で test ディレクトリを圧縮し、tar ファイルを生成します。
9 行目では不要になった test ディレクトリを削除しています。

実行結果

foo$ make pkg
if [ -d test ]; then rm -rf test; fi
mkdir test
cp main.c Makefile bar.c bar.h foobar.c foobar.h ./makelab1
tar zcvf test.tar.gz test
a test
a test/bar.h
a test/foobar.h
a test/main.c
a test/Makefile
a test/bar.c
a test/foobar.c
rm -rf test
 
foo$ ls
Makefile
main.c
bar.c bar.h
foobar.c foobar.h
test.tar.gz
 
foo$ tar -zxvf test.tar.gz
x test/
x test/bar.h
x test/foobar.h
x test/main.c
x test/Makefile
x test/bar.c
x test/foobar.c
 
foo$ ls
Makefile
main.c
bar.c bar.h
foobar.c foobar.h
test

tar ファイルが生成されていること、解凍すると test ディレクトリが生成されていることが確認できると思います。

終わり

gnu make について解説をしてきました。
gnu make は使いこなせるようになると非常に便利です。
ぜひ使ってみてください。