OMake/C++でPython unittestを使ってスマートにテストしたい

つまりはTestをC++でかくのはメンドクセーから、どうせBOOST_PYTHON_MODULE(hoge)でPython用のインターフェースを公開しているわけだし、PythonでTest書いたらステキーなんじゃないかと思いましたっていう単純な話です。
シェルから起動したようなPythonからimport hogeするには、soの作成やらめんどくさい紆余曲折が必要らしいので、hogeを公開してる実行形式の引数としてファイルが与えられたらそれを内部で評価するという形式で。

>main.cpp

using namespace boost::python;

BOOST_PYTHON_MODULE(hoge)
{
	// hogehoge
}

void python_initialize(int argc, char** argv)
{
	if(PyImport_AppendInittab("hoge", inithoge) == -1)
		throw std::runtime_error("python_initialize: import error");

	Py_Initialize();

	if(argc > 0)
		PySys_SetArgv(argc -1, &argv[1]);
}

void python_finalize()
{
	//Py_Finalize();
}

int main(int argc, char** argv)
{
	python_initialize(argc, argv);

	int rc = 0;
	if(argc > 1) {
		rc= exec_test(argc, argv);
	}
	python_finalize();
	return rc;
}

int exec_test(int argc, char** argv)
{
	try 
	{
		PyObject * pyfile = PyFile_FromString(argv[1], "r");
		if(!pyfile)
			return 404;

		PyRun_SimpleFile(PyFile_AsFile(pyfile), argv[1]);

		Py_XDECREF(pyfile);
	}
	catch (error_already_set)
	{
		PyErr_Print();
	} 

	return 0;
}

Pythonをまともに使うのなんてDjangoいじってた時以来だなーなんて感慨に耽ったりしつつも、Python-unittestの使い方なんぞは忘却の彼方にあるため、unittestの書き方 - Λάδι Βιώσαςを参考にさせていただきつつ、適当に書きます。
>tests/test.py

import unittest
import hoge

class BasicTest(unittest.TestCase):
    def testTest(self):
        print "Test!"

def suite():
    s = unittest.TestSuite()
    s.addTest(unittest.makeSuite(BasicTest))
    return s

if __name__ == "__main__":
    unittest.main()

イェー

% ./build/hoge tests/test.py BasicTest.testTest
Test!
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK
% 

さて、ここからが本題。
Testが起動できたなら、omake -P&を常用する手前、やはりPythonのTestも自動で実行したいもの。
あるTestを編集したら、編集したTestだけ実行できたらいいよね等々、人間というのはやはり、どうやら欲張りなようです。
OMakefileというmakefile界のニッチ市場(・・・か?)で、顔を真っ赤にしつつTry&Errorを繰り返します。

>tests/OMakefile

pytests = $(glob ./*.py)
dummy_logs = $(addsuffix .log, $(removesuffix $(pytests)))

foreach(i, $(dummy_logs)):
	.PHONY: $(i)
%.log: %.py $(BUILD_DIR)/hoge$(EXE)
	$(BUILD_DIR)/hoge$(EXE) $<

.PHONY: runcxxtests
runcxxtests: test$(EXE)
	./test$(EXE)

.PHONY: runtests
runtests: runcxxtests $(dummy_logs)

test$(EXE): $(BUILD_DIR)/libhoge$(EXT_LIB)
LIBS = $(BUILD_DIR)/libhoge
.DEFAULT: runtests $(CXXProgram test$(EXE), $(removesuffix $(glob *.cpp)))

キモは、%.log: %.pyという抽象ルールを使ってテストコードを起動している点。
runpytests: $(pytests)のように、複数のpyファイルに依存するルールを定義してというやり方を最初に考えましたが、「複数のpyファイルの中でどれが更新されたのか」をお手軽に取得する方法が思いつきませんでした。無念。
%.py: %.pyみたいに書ければ落なんですが・・・。
.PHONY: $(i)に関しては、ここで[test1.log: test1.py ...]に.PHONY指定をしておかないと、[test1.log...]が最低限、存在している必要が出てきます。
存在しなければその時点でコンパイルエラーとして判断されて悲しい事になってしまいます。

というわけで、テストのテスト。

% echo >> tests/test.py
*** omake: file tests/test.py changed
*** omake: rebuilding
- build tests <test.log>                                                      
+ ../build/hoge test.py
.[============================================================  ] 01327 / 01331
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK
Test!                                                                          
*** omake: done (0.04 sec, 0/42 scans, 15/60 rules, 25/3609 digests)           
*** omake: polling for filesystem changes

C++テストコードの編集。pyファイルに記述したテストは起動しないでほしい。

% echo >> tests/test.cpp                                   
*** omake: file tests/test.cpp changed
*** omake: rebuilding
- build tests test.o                                                         
+ g++ -pipe -std=c++0x ...
- build tests <runcxxtests>                                                   
+ ./test
Running 15 test cases...
20
                                                                              
*** No errors detected
*** omake: done (3.22 sec, 5/47 scans, 25/73 rules, 26/3629 digests)          
*** omake: polling for filesystem changes

ライブラリを構成するファイルの更新。
全部のテストをやってほしい。

% echo >> src/hoge.cpp
*** omake: file src/hoge.cpp changed
*** omake: rebuilding
- build tests <test.log>                                                     
+ ../build/hoge test.py
.[===========================================================  ] 01323 / 01331
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK
Test!
- build tests <runcxxtests>                                                   
+ ./test
Running 15 test cases...
20

*** No errors detected
*** omake: done (2.19 sec, 6/48 scans, 32/80 rules, 32/3644 digests)          
*** omake: polling for filesystem changes

ここに来て気づくのが、テストに失敗したときは失敗した時点で止まって欲しいもの。
となると、%.logの.PHONY指定はむしろ邪魔でえーーーと
な、涙目・・・。