業務で初めて ant を使った自動化に取り組む機会があり、気づきがあったのでメモします。いやいや、お前の気づきはまだまだ甘いぜ、てな突っ込みは大歓迎ですのでよろしくお願いします。
build.xml 分割編とか、build.properties 分割編とか、シリーズ化予定。全体的にぼかしますので、関係者の方は気づいてしまってもスルーでお願いします。
動機
業務アプリケーション開発で、各種コンポーネントのビルド・デプロイ・バージョン管理等を自動化したいのです。できるだけ汎用性を持たせながら。
target の基本(おさらい)
ant では target を使ってビルドやデプロイなどひとまとまりの処理を記述します。
<target name="build">
<!-- ビルド処理 -->
</target>
<target name="deploy" depends="build">
<!-- デプロイ処理 -->
</target>
上記 deploy は depends="build" で依存関係を指定しています。これによって、deploy 前に必ず build が実行されるようになります。ここまでは、まあ普通の話です。
extension-point による依存指定の反転
ant では target とほぼ同じように使える extension-point というものが用意されています。ただしextension-point には処理を記述することができません。ただ目印として、そこにあるだけです。
<extension-point name="ex:build" />
<extension-point name="ex:deploy" depends="ex:build" />
じゃあ、処理はどこに記述するのよ?という疑問が当然出てくるわけですが、それは別の target として記述します。
<target name="build" extensionOf="ex:build">
<!-- ビルド処理 -->
</target>
<target name="deploy" extensionOf="ex:deploy">
<!-- デプロイ処理 -->
</target>
すると ant ex:deploy で build と deploy がそれぞれ実行されることになります。target の extensionOf で拡張したい(=実行タイミングを合わせたい) extension-point の名前を指定します。拡張する target の数に制限はありません。
要するに、イベントリスナー的に使うことができるわけです。
extension-point の使いどころと注意点
extension-point を使うことで、大きな流れをデザインして後から細部を定義する、ということが可能になります。これにより自然と、メインとなる build.xml とそれに取り込まれる build-client.xml、build-server.xml、build-webapp.xml、build-batch.xml などで構成する形になるでしょう。
build-*.xml には、必要に応じて extension-point を拡張する target を記述します。こうすることで、build.xml を修正することなく、追加の build-*.xml をどんどんプラグインできることがわかると思います。
extension-point の注意点としては、拡張側 target の実行順序を決めることができないことです。よって、どの target が先に実行されても問題のないように設計する必要があります。コンポーネント間で依存関係があるならあらかじめ extension-point のレベルで分割しておき、前後関係を指定できるようにするなどの工夫が必要です。
build.xml のテストしやすさを確保する
extension-point を使って大きな流れをデザインした build.xml が出来あがると、部分ごとに抜き出してテストしたくなるのが人情というものです。しかし、各処理の依存関係を生真面目に depends で指定すると、こうしたテストが困難になってきます。
<extension-point name="tfs-label" depends="tfs-checkin"/>
<extension-point name="tfs-checkin" depends="deploy"/>
<extension-point name="deploy" depends="build"/>
<extension-point name="build" depends="build-libs"/>
<extension-point name="build-libs" depends="tfs-checkout"/>
<extension-point name="tfs-checkout" depends="tfs-get"/>
<extension-point name="tfs-get" depends="deploy-check"/>
<extension-point name="deploy-check" depends="init"/>
<extension-point name="init"/>
この状態で tfs-label のテストをすれば、芋づる式に init まで依存関係を遡ってしまいます。これはいただけない。
というわけで、extension-point 間の depends は全て取っ払ってテスト用とし、別途、運用時に使う target を用意してあげるのが妥協点かと思います。
<target name="deploy-all" depends="deploy-init,deploy-body,deploy-finish"/>
<target name="deploy-init" depends="init,deploy-check,tfs-get,tfs-checkout"/>
<target name="deploy-body" depends="build-libs,build,deploy"/>
<target name="deploy-finish" depends="tfs-checkin,tfs-label"/>
<extension-point name="tfs-label"/>
<extension-point name="tfs-checkin"/>
<extension-point name="deploy"/>
<extension-point name="build"/>
<extension-point name="build-libs"/>
<extension-point name="tfs-checkout"/>
<extension-point name="tfs-get"/>
<extension-point name="deploy-check"/>
<extension-point name="init"/>
これで tfs-label や deploy を抜き出して心置きなくテストすることができます。運用時には依存関係が正しく定義された deploy-all を使うわけです。
ただ、あまり多段な target を構成すると把握しにくくなるので、そのあたりのさじ加減は好みと経験に応じて試行錯誤が必要になるでしょう。
まとめ
- extension-point を使うと自動化プロセスの大規模化にも対応できそう
- depends を指定するときは、build.xml のテスト性を確保することも忘れずに
- ant って結構練られてるよね