温故知新:GNU make の拡張機能

新エンジン用のユニットテストには昔ながらの Makefile を使っています。改めて調べてみると GNU make の拡張機能には便利なものがあり、Makefile のメンテナンスが随分楽になりました。

VPATH を自動設定する

新エンジンのディレクトリ構成は、$(project_root)/src/$(各コンポーネント)/ という単位でまとめるようにしています。ユニットテスト用のコードは $(project_root)/src/test/ 以下です。そこで VPATH の出番ですが、今のところ $(各コンポーネント) 部分の名前は流動的なので自動的に設定しておきたいわけです。


調べてみると、$(shell cmd) で shell コマンドの実行結果を設定できることがわかりました。そこで VPATH を以下のように設定します。

VPATH	= $(shell find .. -type d -a ! -name CVS -a ! -name test -a ! -name ..) ../..

毎回 find が走るのは気持ち悪いかもしれませんが、メンテナンスフリーの快適さを味わうともう元には戻れません ;-)

CXXFLAGS を自動設定する

依存関係は VPATH で解決できました。次は #include ディレクティブの検索パスも同様に解決します。

CXXFLAGS= -g $(foreach dir,$(VPATH),-I$(dir))

これだけで VPATH に指定されたディレクトリが #include 時の検索対象に展開されました。素晴しい。

ルールを自動生成する

以前は手抜きをして、テストソースの中でテスト対象クラスのソースを丸ごとインクルードしていました。しかし、依存関係をメンテするのも面倒だし、ビルドの無駄もありすぎなので、テスト対象クラスをまとめた libtest.a ライブラリを作成するようにしました。こうすると、テストプログラムのビルドルールはテストソースのオブジェクトファイルとテストライブラリをリンクするという処理に共通化できます。


通化と繰り返し処理は仲良し、ということでビルドルールを自動生成するようにしました。

...
LIB	= libtest.a
OBJS	= SKKKeymapEntry.o SKKKeymap.o SKKRuleTreeNode.o SKKRomanKanaConverter.o \
	SKKInputBuffer.o jconv.o

TARGET	= SKKKeymapEntry_TEST SKKKeymap_TEST SKKRuleTreeNode_TEST SKKRomanKanaConverter_TEST \
	SKKInputBuffer_TEST

###
### build template
###
define BUILD_template
$(1): $(1).o $(LIB)
	$(CXX) -o $(1) $(1).o -L. -ltest
endef

###
### rule
###
all: $(LIB) $(TARGET)

$(foreach prog,$(TARGET),$(eval $(call BUILD_template,$(prog))))

$(LIB): $(OBJS)
	$(AR) -ru $@ $(OBJS)

clean:
	$(RM) $(TARGET) *.o $(LOG) $(LIB)

test: all
	for i in $(TARGET); do ./$$i ; done
...

all ターゲットのあとの $(foreach ...) で TARGET マクロの各エントリを引数に BUILD_template を評価します。すると、

SKKKeymapEntry_TEST: SKKKeymapEntry_TEST.o $(LIB)
	$(CXX) -o SKKKeymapEntry_TEST SKKKeymapEntry_TEST.o -L. -ltest     

SKKKeymap_TEST: SKKKeymap_TEST.o $(LIB)
	$(CXX) -o SKKKeymap_TEST SKKKeymap_TEST.o -L. -ltest     
...

といったビルドルールが展開されるわけです。これで、テスト対象クラスが増えるたびにテストプログラムのビルドルールを手で追加するという苦痛から解放されます。馴染みの道具も点検してみるもんですね。