引き続き「UNIXプログラミングの道具箱」(make)
自分でMakefileを書いたこともあるけど、サンプルのコピペで全く内容を理解できていなかった。
make
Makefileには、次の構文で記述する。
ターゲット [ ... ]:[依存コンポーネント [ ... ] ] コマンド
ターゲットは生成したいファイルで、依存コンポーネントはそのために使用するファイル。
ターゲットと依存コンポーネントはコロン(:)区切りで1行に記述する。
それぞれ複数のものを指定したい時は、スペース区切りで並べる。
コマンドの前はタブが必要らしい。
makeは以下の場合にコマンドを実行するそうな
- ターゲットが存在しない
- ターゲットが存在しても、依存オブジェクトの方がタイムスタンプが新しい場合
とりあえずmakeしてみる
Hello Worldを出すsample.c用のMakefileだと次のようになる
sample: sample.c cc -o sample sample.c
でも、1ファイルから1オブジェクトを作る場合は、
Makefileなしでも"make sample"ってやるとコンパイルできてしまう。
[kobakoba0723@fedora13-intel64 temp]$ ls sample.c [kobakoba0723@fedora13-intel64 temp]$ make sample cc sample.c -o sample [kobakoba0723@fedora13-intel64 temp]$ ./sample Hello World
依存オブジェクトが複数でてくるような状態だとさすがにコンパイルできないみたい
[kobakoba0723@fedora13-intel64 temp]$ ls Makefile sample.h sample1.c sample2.c [kobakoba0723@fedora13-intel64 temp]$ cat Makefile sample1: sample1.o sample2.o cc -o sample1 sample1.o sample2.o sample1.o: sample1.c sample.h cc -c sample1.c sample2.o: sample2.c sample.h cc -c sample2.c [kobakoba0723@fedora13-intel64 temp]$ rm Makefile [kobakoba0723@fedora13-intel64 temp]$ make sample1 cc sample1.c -o sample1 /tmp/ccYmfd6M.o: In function `main': sample1.c:(.text+0x29): undefined reference to `sample2' collect2: ld はステータス 1 で終了しました make: *** [sample1] エラー 1
単一のファイルの場合は、どんな作用が働いてコンパイルできてるんだろう。。。
仮想ターゲット
ファイルとして生成しないターゲットを指定するのに使う。
make clean とかでファイルを消すのに使ってたのはこれみたい。
そう考えてみると、makeってコンパイルするためだけのコマンドじゃないのか。
あるターゲットに対して、特定のコマンドを実行させるルールを書くものなんだから。
ほぼ共通のコンポーネントの中で、構成によって特定のファイルだけ使い分けたいなんてこともできるのか。
[kobakoba0723@fedora13-intel64 temp]$ ls Makefile sample1.c sample2.c sample3_1.c sample3_2.c sample4.c [kobakoba0723@fedora13-intel64 temp]$ cat Makefile compA: sample1.c sample2.c sample3_1.c sample4.c mkdir compA cp sample1.c sample2.c sample3_1.c sample4.c compA compB: sample1.c sample2.c sample3_2.c sample4.c mkdir compB cp sample1.c sample2.c sample3_2.c sample4.c compB [kobakoba0723@fedora13-intel64 temp]$ make compA mkdir compA cp sample1.c sample2.c sample3_1.c sample4.c compA [kobakoba0723@fedora13-intel64 temp]$ make compB mkdir compB cp sample1.c sample2.c sample3_2.c sample4.c compB [kobakoba0723@fedora13-intel64 temp]$ diff compA compB compAだけに発見: sample3_1.c compBだけに発見: sample3_2.c
マクロ
以下のように定義しておくと、make 実行時に定義した内容に自動的に置き換えられる。
(定義時) マクロ名=マクロ内容 (定義追加時) マクロ名:=$(マクロ名) 追加内容
特殊マクロとして次のようなものが使える。
マクロ | 利用可能場所 | 意味 |
---|---|---|
$@ | コマンド行 | ターゲット名 |
$? | コマンド行 | ターゲットよりタイムスタンプの新しい依存コンポーネント |
$ | 拡張子ルール内 | ソースコンポーネント名 |
$* | 拡張子ルール内 | コンポーネントのファイル名から拡張子を除いたもの |
あぁ、こういう意味だったんだ。なんの呪文かと思ってたよ。
拡張子ルールと拡張子リスト
ある拡張子から別の拡張子のファイルを生成するための規則
.c から .o を生成するためのルールは
.c.o: $(CC) $(CFLAGS) -c $<
でも、このルールは大概の処理系に組み込まれているから書かなくても大丈夫だけど、可搬性を持たせるために書いておいても良い
で、この拡張子ルールに使える拡張子を指定するのが拡張子リスト
拡張子リストは、.SUFFIESという特別なターゲットを使って設定する。
ターゲットの依存コンポーネントに何も書かなければ、既に定義されている拡張子リストをクリアし、
スペース区切りで拡張子を指定すると、指定した拡張子が拡張しリストに追加される。
.c.o と .cpp.o を拡張子ルールとして定義できるようにするには、
.SUFFIXES: .SUFFIXES: .o .c .cpp .h
拡張子リストは左ほど優先度が高いから同じ名前の .c ファイルと .cpp ファイルがあると、.c から .o が生成される
マクロと拡張子ルール/リストを使うと、さっきの2つのソースから sample1 を生成するMakefileは
CC=cc CFLAGS= TARGET=sample1 OBJS=sample1.o sample2.o .SUFFIXES: .SUFFIXES: .o .c all: $(TARGET) sample1.o: sample1.c sample.h sample2.o: sample2.c sample.h .c.o: $(CC) $(CFLAGS) -c $< # よくよく考えたら、sample.h は必要ないのかな $(TARGET): $(OBJS) $(CC) $(CFLAGS) -o $(TARGET) $(OBJS)
.c と .h から .o を作るはずなのに、コマンドに .h が出てこなくても問題ないのはなんでだ。
.h だけ touch して、make すると全てコンパイルが走るし、なんでだろう。