悲劇のフレームワーク Apache Ant 〜悪魔のantcall〜 (vs depends)

フレームワークっていうよりDSLだけど、いずれも枠組みであって、枠に嵌められるし、外れるとおかしくなる。

フレームワークには大きな枠組みがあって、そのなかで変えるべきところを自分なりに変えれば、そのシステムを好きにすることができます。しかしあくまで「変えるべきところ」がどこなのか、「どのように変えるべき」なのかは他の数多くの制約に従わなければいけません。

http://blog.so-net.ne.jp/shi3z/2008-02-14


Antは元来「Javaでのmake」なんて言われるmake代替ツールだった。その枠組みはこんな感じ

projectとは

  • 特定の環境について期待する副作用群の定義
  • 期待する副作用は、targetとして定義される

targetとは

  • 期待される副作用
  • 先に起こされているべき副作用(depends)と本体からなる
  • 本体は、一度だけ実行される*1
  • 本体は、taskの列として定義する

taskとは

  • 実際に副作用を起こす処理
  • パラメタライズされており、バリエーションを変えて何度でも実行される

手続き型プログラミング言語のような制御機構や関数・サブルーチンはなく、あるのはmake同様、targetの依存(包含と言った方がよいかも)のみ。つまり、静的に決まった包含関係にある副作用定義群として問題を記述するという枠組みのはずだった*2


しかし、上記の枠組みは、こんな直感も引き起こしてしまう。

targetとは

  • そのproject固有なもの

taskとは

  • どのprojectでも使える汎用的なもの


さらに発展して、
targetとは

  • そのproject固有な、サブルーチン。


という認識まで生んでしまった。その悪魔の申し子がantcallだ。確かにこれは手続き型言語の発想で制御しやすく、paramもあるし、使い勝手がよい。…ように思える。

targetで指定できるdepends属性ですが、前提条件として実行しておきたいタスクを指定しておくためのものとしてよくつかわれていますよね。タスクをまとめて実行するのには便利と言えば便利ですが、antcallで一つ一つ指定しておいたほうが使いやすかったりします。

http://d.hatena.ne.jp/kompiro/20080210/1202640279


だが、targetはサブルーチンではない。上記の通り、本来、projectとして1度限り起きるはずのものであり、それは本体だけでなく、dependsも含む。この根本的矛盾を無理矢理整合させるため、targetはproject丸ごとを子projectとして起こし直して実行される。これにより、build.xmlなどの定義は丸ごと再パースされるわ、トップレベルに書かれたtaskは再実行されるわ、プロパティを設定しても親には反映されないわ、dependsもみんな実行されるわで、サブルーチンという直感とはかけ離れた振る舞いを示す。


枠組みを外れると根本的な破綻を呼び寄せるのは、枠組み(フレームワーク)として当たり前の事だ。


しかし、実際問題、targetとdependsだけでは不満もあるだろう。でもそれは、antの根幹への否定といえる。それなのに、Antは自身の枠組みをグダグダにする事で応えてしまった。自分の枠組みを否定した枠組み(フレームワーク)。

*1:antcallなんて最初はなかった

*2:conditionタスクとif/unlessで環境による切り替えは可能だがそれも静的なもの