Makefile#2: Rule in Makefile
Makefile に書く内容は9割がこれのため。
自動変数についても参照すべし。
明示的ルール
とくに捻りのない、素直な方法。
ターゲットは一つ以上持てるので、こんなのも可能
vpath.o variable.o: make.h config.h getopt.h gettext.h dep.h
これは以下の同じ
vpath.o: make.h config.h getopt.h gettext.h dep.h
variable.o: make.h config.h getopt.h gettext.h dep.h
あと、必須項目は別けてかくことも出来る。makeはターゲットを見つけ次第必須項目を 追加 していく。
vpath.o: make.h config.h
vpath.o: getopt.h gettext.h dep.h
ワイルドカード
~, *, ?, [...], [^...] などのワイルドカードも使える。
ターゲット、必須項目、コマンド行とそれぞれ使えるが、コマンド行のワイルドカードはshellによる展開になる。
-
~
ホームディレクトリ
~takio
と書くと指定ユーザーのホームディレクトリとなる
-
*
0文字以上のなんでも
-
?
1文字以上のなんでも
-
[...]
文字集合
[a-z]
とか
-
[^...]
[...]の否定(補集合)
こんな感じで使えるけど、大雑把すぎるので注意が必要
というか非推奨
prog: *.c
$(CC) -o $@ $^
.PHONY (擬似ターゲット)
通常ターゲットと同名のファイル名がある場合、そのファイルが更新不要であればコマンド行は実行されない。
一方で all, clean, とかファイルではないけどターゲットのラベルみたいに指定したい時に、 .PHONY を使用する。
.PHONY: clean
clean:
rm -f *.o lexer.c
上記のように指定すれば、'clean' という名のファイルが存在していても影響がない。
-
一般的な擬似ターゲット
ターゲット 機能 all アプリケーションを構築するすべての作業を行う clean ソースから作られたバイナリを削除する distclean 元の配布物に含まれて田舎立つベテの生成物を削除する TAGS tags作成 info Texinfoのソースより GNU info ファイルを作成する check このアプリケーションに関する全てのテストを実行
VPATH, vpath
こんな感じのソースツリーとすると
.
|-- include
| |-- counter.h
| `-- lexer.h
|-- Makefile
`-- src
|-- counter.c
|-- count_words.c
`-- lexer.l
VPATH
makefile の位置でmakeを実行するには、各ファイルへパスを通さないとダメだけど、 VPATH を使うと良い感じになる。
VPATH = src
これで counter.c, count_words.c へパスが通るが、インクルードパスは CPPFLAGS とか使わないとダメらしい。
CPPFLAGS = -I include
もちろんコマンド行はシェルに渡されるので、この変数は無効
vpath
VPATH 変数はルール全てに適応されてしまって、同じファイルとかあると困ったことに。。。
で、 vpath を使えば欠点を補えるが
vpath %.c src
vpath %.l src
vpath %.h include
ちと単純すぎて使えそうに無いかも。。。
パターンルール
組み込みルール
GNU make には組み込みルールってのがあって内容に関しては、次のコマンドラインオプションで参照できる。
$ make --print-data-base
出力の内容はパターンルールで構成されている。
こんな感じ
%: %.o
# commands to execute (built-in):
$(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@
%.o: %.c
# commands to execute (built-in):
$(COMPILE.c) $(OUTPUT_OPTION) $<
%.c: %.l
# commands to execute (built-in):
@$(RM) $@
$(LEX.l) $< > $@
次のような冗長なルールに対して、組み込みルールを適応すると冗長性が解消される。
-
before
count_words: count_words.o counter.o lexer.o -lfl gcc $^ -o $@ count_words.o: count_words.c gcc -c $< counter.o: counter.c gcc -c $< lexer.o: lexer.c gcc -c $< lexer.c: lexer.l flex -t $< > $@
-
after
VPATH = src include CPPFLAGS = -I include count_words: counter.o lexer.o -lfl count_words.o: counter.h counter.o: counter.h lexer.h lexer.o: lexer.h
最初の実行ファイルの依存関係として下記が合致し参照される。結果 count_words.o が認識される。
%: %.o
# commands to execute (built-in):
$(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@
下記のパタンルールで .c が .o へと変換される。
Makefile内のルールにコマンド行を記述していないのがミソで、書いてしまうと暗黙のルール(Implict rule)が適応されない。
%.o: %.c
# commands to execute (built-in):
$(COMPILE.c) $(OUTPUT_OPTION) $<
さらに .l に関して .c への変換は次の通り
%.c: %.l
# commands to execute (built-in):
@$(RM) $@
$(LEX.l) $< > $@
Implict ruleで生成されたファイルはmakeで認識されていて、ターゲット完成後削除される。
ちなみに、どんなルールが適応されてるかは、下記を実行すると見れる。
$ make --print-data-base
--just-print オプションを付けると、実行の予行内容も見れる。
$ make --just-print --print-data-base
$ make -n -p # 短縮形
% (パターン)
% を使うことでパタンを指定できる。ただし文字列中に1回のみ使用できる。
%,v
s%.o
wrapper_%
静的パターンルール
特定のターゲットに対して提供されるルール
こんな感じ
$(OBJECTS): %.o: %.c
$(CC) -c $(CFLAGS) $< -o $@
$(OBJECTS) 変数でリストしたファイルに対してのみ、 "%.o: %.c" ルールが適応される。
$(OBJECTS) 変数内の *.o が %.o のパタンに合致しターゲットとして認識され、 %.c として必須項目へ設定される。
サフィックスルール
一般的なmakeでもサポートする文法。
-
ダブルサフィックスルール
.c.o: $(COMPILE.c) $(OUTPUT_OPTION)A $<
これは GNU make のパターンルールだと下記と同じ
%.o: %.c $(COMPILE.c) $(OUTPUT_OPTION)A $<
-
シングルサフィックスルール
.p: $(LINK.p) $^ $(LOADLIBES) $(LDLIBS) -o $@
同じく GNU make だと
%: %.p $(LINK.p) $^ $(LOADLIBES) $(LDLIBS) -o $@
このサフィックスルールを適応するには .SUFFIXES に設定されてないとダメっぽい。一部だが、組み込みルールはこんな設定
.SUFFIXES: .out .a .ln .o .c .cc .C .cpp .p .f .F .r .y .l
独自に追加したければ、
.SUFFIXES: .pdf .html
と書いておけば追加になる。
拡張子が期待と違う設定になってて困る場合は、クリアしておく
(--no-builtin-rules or -r でもOK)
.SUFFIXES:
とまぁ、文法的には GNU make の方が明快なので、知識程度に覚えておく
暗黙ルール (Implict rule)
makefileに指定するターゲットに対しての コマンド行を書かなければ 、暗黙ルール (Implict rule) が適応される。
また組み込みルールが何かの弊害になる場合削除もできる。
例えばLispの拡張子が .l の場合、flex出力を .c に変換する機能と衝突する。
下記のようにルールを取り除けばOK
%.o: %.l
%.c: %.l
コマンド行の伴わないパタンはルール削除を示す。
例えばCの単一ファイルの場合
関係のある組み込みルールは
%.c:
%: %.c
# commands to execute (built-in):
$(LINK.c) $^ $(LOADLIBES) $(LDLIBS) -o $@
たとえば、 main.c のみのプロトタイプ・プログラムを作った場合、makefileを作成せずにこのようにmake出来る。
$ make main
cc main.c -o main
また、組み込みルールにて勝手に作成された中間ファイルはターゲット完成後に削除される。
カスタマイズ
組み込みルールは便利だが、カスタマイズの方法はしっかり把握すべし。
# default
CC = cc
# default
COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c
# default
OUTPUT_OPTION = -o $@
このようにデフォルトでは変数が設定されてる。なのでこれに準ずれば楽チン
変数名 | 内容 |
---|---|
CC | %.c とかに使われるコンパイラ |
CFLAGS | 基本的にはコンパイラオプション |
CPPFLAGS | プリプロセッサ(PP) 向けのオプション |
TARGET_ARCH | アーキテクチャ指定 |
カスタマイズ性確保のために上書き設定も良いとされてる。
COMPILE.c = $(CC) $(CFLAGS) $(INCLUDES) $(CPPFLAGS) $(TARGET_ARCH) -c
INCLUDES = -I project/include
特殊ターゲット
.PHONYのようなmakeのデフォルト動作を変更するターゲット。
特殊ターゲット: 必須項目
12種類らしいが、大別すると3種類になるらしい。
- makeの挙動を変更するためのもの
- 帯域フラグとして、対象の必須項目を無視するためのもの
- .SUFFIXES
依存関係
gccには依存関係の生成の為のコマンドラインオプションがある
$ echo "#include <stdio.h>" > stdio.c
$ gcc -M stdio.c
stdio.o: stdio.c /usr/include/stdc-predef.h /usr/include/stdio.h \
/usr/include/features.h /usr/include/x86_64-linux-gnu/sys/cdefs.h \
/usr/include/x86_64-linux-gnu/bits/wordsize.h \
/usr/include/x86_64-linux-gnu/gnu/stubs.h \
/usr/include/x86_64-linux-gnu/gnu/stubs-64.h \
/usr/lib/gcc/x86_64-linux-gnu/4.8/include/stddef.h \
/usr/include/x86_64-linux-gnu/bits/types.h \
/usr/include/x86_64-linux-gnu/bits/typesizes.h /usr/include/libio.h \
/usr/include/_G_config.h /usr/include/wchar.h \
/usr/lib/gcc/x86_64-linux-gnu/4.8/include/stdarg.h \
/usr/include/x86_64-linux-gnu/bits/stdio_lim.h \
/usr/include/x86_64-linux-gnu/bits/sys_errlist.h
上記のままで使えそうだが、gccのこの出力を中間ファイル .d で扱ったとして、このファイルを含めた依存関係は
stdio.o stdio.d : stdio.c /usr/include/stdc-predef.h /usr/include/stdio.h \
~省略~
なのでmakefileへ取り込む際のコマンドスクリプトの書き方にコツが要る。
%.d: %.c
$(CC) -M $(CPPFLAGS) $< > $@.$$$$; \
sed 's,\($*\)\.o[ :]*,\1.o $@: ,g' < $@.$$$$ > $@; \
rm -f $@.$$$$
sedはLinuxコマンドで置き換えとかするやつ。
($).o[ :]
これは検索部で、ターゲット名の本体部を正規表現で抽出している。
\1.o $@:
こっちは置換部。正規表現で抽出したターゲットほ本体部を .o と .d で置き換えている。
今までのやつに適応すると。。。
-
Makefile
VPATH = src include CPPFLAGS = -I include SOURCES = count_words.c \ lexer.c \ counter.c count_words: counter.o lexer.o include $(subst .c,.d,$(SOURCES)) %.d: %.c $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ sed 's,\($*\)\.o[ :]*,\1.o $@: ,g' < $@.$$$$ > $@; \ rm -f $@.$$$$
-
result
$ make -n Makefile:13: count_words.d: No such file or directory Makefile:13: lexer.d: No such file or directory Makefile:13: counter.d: No such file or directory cc -M -I include src/counter.c > counter.d.$$; \ sed 's,\(counter\)\.o[ :]*,\1.o counter.d: ,g' < counter.d.$$ > counter.d; \ rm -f counter.d.$$ lex -t src/lexer.l > lexer.c ~省略~
-
counter.d
counter.o counter.d: src/counter.c /usr/include/stdc-predef.h include/lexer.h \ include/counter.h
ライブラリ管理
-
ar (アーカイバ)の基本。
$ ar rv libcounter.a counter.o lexer.o
rv はコマンドラインオプションで r(=replace), v(=verbose) かと
-
gcc でのライブラリの扱い
-lc でも libc.a のように指定しても取り込んでくれる
-l を使うとパスを検索してくれる。
ターゲットとしてライブラリの設定
普通に考えるとこう書くはず
libcounter.a: counter.o lexer.o
$(AR) $(ARFLAGS) $a $^
ちなみに、組み込み設定では
# default
ARFLAGS = rv
# default
AR = ar
$^ は必須項目を全てリストするので、 $? を使うと少しスマート
libcounter.a: counter.o lexer.o
$(AR) $(ARFLAGS) $a $?
これに便利な組み込みルールが設定されている たぶんこれ。。。
(%): %
# commands to execute (built-in):
$(AR) $(ARFLAGS) $@ $<
これを使うとこう書くことになる。
libcounter.a: libcounter.a(lexer.o counter.o)
libcounter.a(%): % と認識される(のか?)、で結果として
$@ = libcounter.a
$< =lexer.o or counter.o
となって、 AR が実行される。
必須項目にライブラリを設定
ファイル名でも -l を使ってもOK。
-lを使った場合、自動変数($^, $?)へは探索後の絶対パスへ置き換えて設定される。ただしターゲットへの指定には -l は使えず、ルールが構築できない。。。
-
Makefile
count_words: counter.o lexer.o -lcounter libcounter.a: libcounter.a(lexer.o counter.o)
-
shell
$ make make: *** No rule to make target `-lcounter', needed by `count_words'. Stop.
二重コロンルール
通常のルールは一ターゲットにコマンドスクリプトは一括りに統一されるが。
file-list:: generate-list-script
chmod +x $<
generate-list-script $(files) > file-list
file-list:: $(files)
generate-list-script $(files) > file-list
こんな感じで書くと同一ターゲットの別必須項目毎に違う処理を記述出来る。 あんまり使い道ないけど
Discussion