Practice Ant

Antを利用した継続的インテグレーション



Antとは

Antは,ビルドツールの一種です.Javaをベースとしていますので,OSのタイプに限定されることがないという点で,makeよりも優れているといわれています.

また,The Jakarta Projectのビルドにも使われていますし,Ant自身もAntでビルドされています.(Antのソースアーカイブをダウンロードしてきて,展開すると,Antのビルドファイルを見つけることができます.)


ダウンロード

The Jakarta Project(http://jakarta.apache.org/ant/index.html)のサイトから,Antのアーカイブをダウンロードします.
ダウンロードするファイルは,

jakarta-ant-1.3-bin.zip(または,jakarta-ant-1.3-bin.tar.gz)
jakarta-ant-1.3-optional.jar

でOKです.optional.jarは,オプションタスクを利用するときに必要になるアーカイブです.すぐには使用しないかもしれませんが,ダウンロードしておきましょう.


インストール手順

ここでは,WinNT環境へのインストール手順を示していきます.パス等の表記が異なるだけで,自分の環境に置き換えれば,特に問題ないでしょう.

まずは,JDKがすでにインストールされていることを確認してください.また,JDKがインストールされているディレクトリに対して,JAVA_HOME環境変数を設定します.

次に,Antのアーカイブを展開して,インストールしたいディレクトリにコピーします.このディレクトリに対して,ANT_HOMEを設定し,ANT_HOME\binへのパスを設定すれば,Antのインストールは完了です.


動かしてみる

早速,インストールできたかどうかを確認してみます.コマンドプロンプトを起動して,"ant"と入力してリターンを押してみてください.

C:\>ant
Buildfile: build.xml does not exist!
Build failed

上のようなエラーメッセージが表示されれば,Antがインストールは成功しています.(もし,Antを実行したディレクトリよりも親ディレクトリに,"build.xml"というファイルが存在していると,ビルドが成功してしまう可能性がありますので注意してください.)

ここで,Antの動作を簡単に説明しておきます.(詳細は,Antのドキュメントを参照してください.)
まずは,Antのコマンドラインオプション一覧を見てみます.

C:\>ant -help
ant [options] [target [target2 [target3] ...]]
Options:
-help print this message
-projecthelp print project help information
-version print the version information and exit
-quiet be extra quiet
-verbose be extra verbose
-debug print debugging information
-emacs produce logging information without adornments
-logfile <file> use given file for log
-logger <classname> the class which is to perform logging
-listener <classname> add an instance of class as a project listener
-buildfile <file> use given buildfile
-D<property>=<value> use value for given property
-find <file> search for buildfile towards the root of the
filesystem and use it

細かい説明は置いておいて,-buildfileオプションに注目すると,このオプションで,Antが実行するビルドプロセスが記述されたビルドファイルを指定するようになっています.

先ほどは,このオプションを指定しなかった訳ですが,このオプションの指定がなかったときは,Antはデフォルトで,"build.xml"という名前のファイルをビルドファイルとして使用する仕様となっています.そして,カレントディレクトリで,ビルドファイルが見つからないときは,親ディレクトリを探し,また見つからないときは,,,とルートまで遡ってビルドファイルを探します.最後までさかのぼっても見つからないと,「Buildfile: build.xml does not exist!」と教えてくれるわけですね.


ビルドファイルの構成

さて,Antに自動ビルドさせるには,ビルドファイルが必要だ,というのが分かったところで,早速ビルドファイルを作ってみたいと思います.
まずは,あくびが出るほど簡単なサンプルを動かして,その構成を説明したいと思います.

はじめに,テンポラリのディレクトリを作成して,そのディレクトリに,"build.xml"という名前のビルドファイルを作成します.build.xmlの内容は,以下の通り.

<?xml version="1.0" encoding="utf-8"?>
<project name="sample" default="prepare" basedir=".">
<target name="prepare">
<mkdir dir="./classes"/>
</target>
</project>

早速,Antを実行してみます.Antを実行するときには,作成したbuild.xmlファイルがあるディレクトリに移動するのを忘れないようにしてください.
また,Antを実行するときのパラメータとして,"prepare"を指定していますが,これはビルドファイル中に定義されているターゲットの名前のことです.ここでは,"prepare"ターゲットしかないので,これを指定します.

C:\>cd tmp

C:\tmp>dir /B
build.xml C:\tmp>ant prepare
Buildfile: build.xml prepare:
[mkdir] Created dir: C:\tmp\classes BUILD SUCCESSFUL Total time: 1 second
C:\tmp>dir /B
build.xml
classes

予想通り?だと思いますが,classesディレクトリが作成されたことが確認できると思います.それでは,結果が分かったところで,上記build.xmlについての簡単な解説をします.

まず,最初の行ですが,これは単なるXML宣言です.特に説明は要らないと思います.

次に2行目のproject要素ですが,

<project name="sample" default="prepare" basedir=".">
...
</project>

属性 説明
name プロジェクト名
default ターゲットが指定されないときに使用される,デフォルトターゲットの名前
basedir ビルド時に使用されるパスの,ベースディレクトリ

ターゲットというのは,3行目に記述されているtarget要素のことをあらわしています.このビルドファイルでは,ターゲットprepareしか定義されていませんので,これをデフォルトのターゲットとしています.

3行目は,target要素です.

<target name="prepare">
...
</target>

この例では,ターゲット名を指定するname属性しか定義していませんが,他にも,ターゲット間の依存関係を定義するdepends属性なども存在します.

4行目は,mkdir要素です.

<mkdir dir="./classes"/>

mkdirは,Antで実行できる標準のタスクで,文字通りディレクトリの作成を行います.この例では,カレントディレクトリに,classesディレクトリを作成します.(ビルドファイルでは,パス区切り文字には,"/"を使用するので注意!)
また,この例ではターゲット内のタスクは,mkdirのみでしたが,複数のタスクを記述することもでき,その組み合わせによって,さまざまなビルドが可能となります.

さて,非常に単純なサンプルでしたが,基本的に,ビルドファイルというのはタスクの組み合わせに過ぎません.ターゲットは,タスクをひとまとめにして,見通しの良いかたまりとしたようなものです.(タスクが定義済み関数で,ターゲットが自作の関数のようなものでしょうか?)
だから,複雑に見えるビルドファイルも,一つ一つのタスクの振る舞いさえ分かってしまえば,実は大したことをしてなかったりします.
次の段階として,実践的なビルドの例を紹介して,Antの優秀さの一面を体験してみましょう.


実践的プロジェクトで試す

まずは,実践的なプロジェクトとして,以下のようなディレクトリ階層構造のプロジェクトを想定します.プロジェクトのホームディレクトリの下に,ソースコードを格納するためのsrcディレクトリを作成して,その下にパッケージの階層に沿った形で,ディレクトリ階層を作成した,よく見かけるプロジェクトだと思います.

/PROJECT_HOME(プロジェクトのホームディレクトリ)
  /src
    /com
      /mamezou
        /sample
          /model
            SampleModel.java
            /test
              SampleModelTest.java
          /view
            SampleView.java
            /test
              SampleViewTest.java

このサンプルプロジェクトにおけるクラスは,サンプルのクラスが2つと,それに対するユニットテストクラス2つの計4つです.クラスの実装は以下のようになっていると考えてください.(あくまでもサンプルですので,内容は全く役に立ちません)

プロジェクトサンプル1 → sample1.project.lzh

package com.mamezou.sample.model;

public class SampleModel {
    public int getSampleValue() {
        return 100;
    }
}


package com.mamezou.sample.view;

public class SampleView {
    public int getSampleValue() {
        return 100;
    }
}


package com.mamezou.sample.model.test;

import junit.framework.*;
import com.mamezou.sample.model.*; public class SampleModelTest extends TestCase { public SampleModelTest(String name) { super(name);
}
public void testGetSampleValue() { SampleModel sample = new SampleModel(); assertEquals(100, sample.getSampleValue()); }
}

package com.mamezou.sample.view.test; import junit.framework.*; import com.mamezou.sample.view.*;

public class SampleViewTest extends TestCase { public SampleViewTest(String name) { super(name); }
public void testGetSampleValue() { SampleView sample = new SampleView(); assertEquals(50, sample.getSampleValue()); } }

次に,ビルド結果として期待する,ディレクトリ階層構造および生成物を示します.Antに上記サンプルプロジェクトを自動ビルドさせた結果がこうなるべき,というものですね.

/PROJECT_HOME(プロジェクトのホームディレクトリ)
  /src
    ・・・(省略)
  /build
    sample.jar
    /apidocs
      ・・・(Javadocが出力される)
    /classes
      /com
        /mamezou
          /sample
            /model
              SampleModel.class
              /test
                SampleModelTest.class
            /view
              SampleView.class
              /test
                SampleViewTest.class

build以下のディレクトリが生成物になるわけですが,分かりづらいかもしれませんので,自動ビルドを手順を追って説明します.

  1. ディレクトリを作成(/PROJECT_HOME/build, /PROJECT_HOME/build/apidocs, /PROJECT_HOME/build/classes)する.
  2. ソースをコンパイルして,classファイルを/PROJECT_HOME/build/classesディレクトリ下に置く.
  3. Javadocドキュメントを,/PROJECT_HOME/build/apidocs下に生成する.
  4. ユニットテストを除いたクラスを,/PROJECT_HOME/build/sample.jarとして,圧縮する.

これらの作業が自動化されると相当便利になりますね.では,実際にこれらの作業を自動化するためのAntのビルドファイルを紹介します.
今回は,ビルドファイルにコメントを埋め込む形で,ビルドファイルの内容について説明をしたいと思います.ビルドファイルの簡単な概要は分かっていると思いますので,こちらのほうが読みやすいと思います.
また,Antのドキュメントは併用して読むようにしてください.

ビルドファイルサンプル1→ sample1.build.xml

<?xml version="1.0" encoding="utf-8"?>
<project name="sample" default="" basedir=".">
<!--
ビルドファイル内で使用するプロパティを設定します.
プロパティとは,nameとvalueの対からなる変数定義のようなものです.
名前:"src.dir",値:"./src"というプロパティを属性内で参照するためには,
${src.dir}と記述します. 展開例: dir="${src.dir}" → dir="./src" また,Javaのシステムプロパティは,特に定義なしで参照することができること は覚えておきましょう. システムプロパティ例: java.home, java.vendor, user.version, java.class.path等 -->
<!-- ディレクトリ名の定義 --> <property name="src.dir" value="./src"/> <property name="build.dir" value="./build"/> <property name="build.classes" value="${build.dir}/classes"/> <property name="build.apidocs" value="${build.dir}/apidocs"/> <!-- JARアーカイブファイル名の定義 --> <property name="build.sample.jar" value="${build.dir}/sample.jar"/>
<!-- prepareターゲットの定義 ここでは,主にディレクトリの作成を行っています.prepareという名前は, あくまでも名前なので別の名前でも構いません. 準備ターゲットでは,タイムスタンプ関連プロパティを設定する,Tstampタスク を呼び出しておくと,タイムスタンプをファイル名などに使えて便利なのを覚え ておきましょう.
-->
<target name="prepare"> <mkdir dir="${build.dir}"/> <mkdir dir="${build.classes}"/> <mkdir dir="${build.apidocs}"/> </target> <!-- complieターゲットの定義 ソースコードのコンパイルを行います.
ターゲットの属性として,depends属性を指定していますが,ここでは,compile ターゲットがprepareタスクに依存していることをあらわしています. Antを実行するときに,ターゲットを指定して実行することができますが,指定 されたターゲットが依存しているターゲットが存在するときは,先に依存先のタ ーゲットが実行されることになるので,compileターゲットを指定してAntを実行 すると,必ずprepare → compileという順番で,ターゲットが実行されることに なります. -->
<target name="compile" depends="prepare"> <javac srcdir="${src.dir}" destdir="${build.classes}"/> </target>
<!-- apidocsターゲットの定義
Javadocドキュメントの出力を行います.ここでは,一番シンプルな指定で出力 させていますが,形式の詳細を設定することもできます. 詳しくは,Antのドキュメントを参照してください. -->
<target name="apidocs" depends="compile"> <javadoc sourcepath="${src.dir}" destdir="${build.apidocs}" packagenames="com.*"/> </target>
<!-- jarターゲットの定義
JARアーカイブを作成します.リリース時のアーカイブには,ユニットテストの クラスファイルは,必要ありませんので,jarタスクのexcludes属性を使用して, ユニットテストファイルを除外してJARアーカイブを作成しています.
このように,ユニットテストを含めない形でのアーカイブ作成が簡単にできるの で,ユニットテストのコードをテストパッケージとして,ソースコードから遠い 場所に配置しなくて済みますね. -->
<target name="jar" depends="apidocs"> <jar jarfile="${build.sample.jar}" basedir="${build.classes}" excludes="**/test/**"/> </target>
</project>

このビルドファイルを,PROJECT_HOME下に置いた後,PROJECT_HOMEに移動して,Antを実行 ,,,っとその前に,ユニットテストでは,JUnitを使用してますので,ビルド環境にJUnitがインストールされていない場合は,先にこちらをダウンロードして,インストールしておかなければなりません.

JUnit.org(http://www.junit.org/

ここでは,JUnitの使用方法は省略させていただきます.
また,Antでのテストの自動化を視野に入れると,CLASSPATHをビルドファイルで設定するのは面倒なので,JAVA_HOME\jre\lib\extディレクトリにコピーしておくと便利です.

ここまでの準備ができたら,実行します.指定するターゲットは,jarターゲットを指定すれば,それに依存するターゲット全てが実行されるのでOKです.今回は,実行結果は省略しますが,"BUILD SUCCESSFUL"と表示されればビルドは成功です!期待通りの生成物ができたかどうかを確認してみてください.

このビルドファイルでは,いきなり使用したタスクの数が多くなってしまいましたが,それほど難しくなく,読みこなせたのではないでしょうか?また,ディレクトリ名などは,プロパティを使用して指定していますが,このようにしておくことで,何らかの変更があったときに対処しやすいビルドファイルになるので,参考にしてみてください.ソースコードに限らず,「Once and Only Once」の精神で行きましょう.

また,他のJakarta Projectでも,Antを使用しての自動ビルドが活用されていますので,是非こうしたオープンソースプロジェクトからビルドファイルの作り方のコツを盗んできましょう.


Antによるユニットテストの自動化&レポート集計

ここまでで,プロジェクトの自動ビルドまでが達成されました.しかし,eXtremeに少しでも近づくために,テストも自動化したくなるのが人情です.
幸いにも,Antの拡張オプションとして,JUnitタスクがありますので,これを利用することで,自動テストを簡単に行うことができます.さらには,テスト結果をXML形式で出力する機能や,さらにテスト結果をまとめてくれる機能なども,提供されているのです.

そうはいったものの,具体的にどの程度のテスト結果が出力されるのかが分からないと,ピンとこないと思いますので,まずはサンプルをご覧ください.

テスト結果サンプル → SampleModelTestクラスのテスト結果(XML形式)
テスト結果集計サンプル → 集計サンプル

どうでしょう?集計サンプルの方で,きちんと集計されているのを見て,これは使えそうだ!と思われる方も多いのではないかと思います.
気分が乗ってきたところで,早速,テスト結果集計までを実現するための手順を説明していきたいと思います.

まず,オプションタスクである,JUnit,JUnitReportを使うために,一番初めにダウンロードしておいた,optional.jarを,ANT_HOME\libディレクトリにコピーします.

次に,JUnitタスクで必要となる,JUnitのアーカイブが,インストールされていることを確認します.(今回は,JAVA_HOME\jre\lib\extディレクトリ下にコピーされているものと想定します.ここに置いておくと,JUnitタスクで,classpath属性を指定しなくて良かったりしますので,便利です.もちろん,タスク内できちんと指定して頂いても構いません.)

次に,XML形式で出力されたテスト結果を,HTML形式で集計するために必要となる,Xalan1を手に入れる必要があります.The Apache XML Project(http://xml.apache.org/xalan/)のサイトから,Xalan1のアーカイブをダウンロードしてきます.(現在のバージョンのAntでは,JUnitReportタスクでXalan2はサポートされていないようです.Xalan1 compatibility jar of Xalan2ならOKのようですが,試したことはありません.)
ダウンロードするファイルは,

xalan-j_1_2_2.zip(または,xalan-j_1_2_2.tar.gz)

でOKです.
Xalanのアーカイブを展開すると,ファイルがいっぱいできますが,この中で必要となるのは,xalan.jarとxerces.jarの2つだけです.(xerces.jarについては,最新のXerces Javaでも問題ありません.)
このファイルも,ここではJAVA_HOME\jre\lib\extディレクトリ下に置いてしまいます.(Xercesなどは,別のディレクトリに置いてあることが多いと思いますので,パスを設定して頂いても構いません.)

さて,ここまでで,JUnit,JUnitReportタスクを実行できる環境が整いました.早速,ユニットテストの自動化&レポート集計のための,ターゲット例を見てみましょう.

<target name="runtests" depends="compile">
    <!--
JUnitタスクでは,ユニットテストを自動実行して,その結果をファイルに記録 します.

haltonfailure属性をnoに指定しておかないと,ユニットテストに失敗したとき に,ビルド作業も中断してしまいます. JUnitReportタスクで,レポートの集計を行いたいので,失敗しても,そのまま テストを続けるように,指定します.
-->

<junit printsummary="yes" haltonfailure="no">
<classpath>
<pathelement location="${build.classes}"/>
<pathelement path="${java.class.path}"/>
</classpath>
<!-- テスト結果は,XML形式で出力します. プレーンテキストでも出力できますが,集計にJUnitReportが使えなくなっ てしまいます.
-->

<formatter type="xml"/>
<!-- reports.dirについては,Propertyタスクで定義済みと考えて置いてくださ い.ビルドファイル全体は,後述します. reports.dirディレクトリに,テスト結果が出力されます. テスト結果のファイル名は,何も指定しないデフォルトで,"TEST-*.xml"と いった名前になります.
-->

<batchtest fork="yes" todir="${reports.dir}"> <!-- ここでは,拡張子を除いたファイル名の末尾が"Test"となっているテス トファイルを対象としています.
-->

<fileset dir="${src.dir}">
<include name="**/*Test.java"/>
</fileset>
</batchtest>
</junit>
<!-- JUnitReportタスクでは,JUnitタスクで出力された,テスト結果(XML形式のもの )を集計します.
-->

<junitreport todir="${reports.dir}"> <!-- 集計対象とする,テスト結果のfilesetを指定します.
-->

<fileset dir="${reports.dir}"> <include name="TEST-*.xml"/> </fileset>

<!-- 集計結果のHTMLファイル群を格納するディレクトリを指定します.
-->
<report format="frames" todir="${correctreports.dir}"/> </junitreport> </target>

ここでは,見やすいように,JUnit関連のターゲットだけ切り出して説明を付加しましたが,このままでは未定義のPropertyなどが存在するために実行できませんので,ビルドファイルサンプル2として,ビルドファイル全体のサンプルを示しておきます.

ビルドファイルサンプル2 → sample2.build.xml

<?xml version="1.0" encoding="utf-8"?>
<project name="sample" default="" basedir=".">
<!--
ビルドファイル内で使用するプロパティを設定します.
プロパティとは,nameとvalueの対からなる変数定義のようなものです.
名前:"src.dir",値:"./src"というプロパティを属性内で参照するためには,
${src.dir}と記述します. 展開例: dir="${src.dir}" → dir="./src" また,Javaのシステムプロパティは,特に定義なしで参照することができること は覚えておきましょう. システムプロパティ例: java.home, java.vendor, user.version, java.class.path等 -->
<!-- ディレクトリ名の定義 --> <property name="src.dir" value="./src"/> <property name="build.dir" value="./build"/> <property name="build.classes" value="${build.dir}/classes"/> <property name="build.apidocs" value="${build.dir}/apidocs"/> <!-- JARアーカイブファイル名の定義 --> <property name="build.sample.jar" value="${build.dir}/sample.jar"/>
<!-- prepareターゲットの定義 ここでは,主にディレクトリの作成を行っています.prepareという名前は, あくまでも名前なので別の名前でも構いません. 準備ターゲットでは,タイムスタンプ関連プロパティを設定する,Tstampタスク を呼び出しておくと,タイムスタンプをファイル名などに使えて便利なのを覚え ておきましょう.
-->
<target name="prepare"> <mkdir dir="${build.dir}"/> <mkdir dir="${build.classes}"/> <mkdir dir="${build.apidocs}"/> </target> <!-- complieターゲットの定義 ソースコードのコンパイルを行います.
ターゲットの属性として,depends属性を指定していますが,ここでは,compile ターゲットがprepareタスクに依存していることをあらわしています. Antを実行するときに,ターゲットを指定して実行することができますが,指定 されたターゲットが依存しているターゲットが存在するときは,先に依存先のタ ーゲットが実行されることになるので,compileターゲットを指定してAntを実行 すると,必ずprepare → compileという順番で,ターゲットが実行されることに なります. -->
<target name="compile" depends="prepare"> <javac srcdir="${src.dir}" destdir="${build.classes}"/> </target>
<!-- apidocsターゲットの定義
Javadocドキュメントの出力を行います.ここでは,一番シンプルな指定で出力 させていますが,形式の詳細を設定することもできます. 詳しくは,Antのドキュメントを参照してください. -->
<target name="apidocs" depends="compile"> <javadoc sourcepath="${src.dir}" destdir="${build.apidocs}" packagenames="com.*"/> </target>

<!--
runtestsターゲットの定義

JUnitを利用した自動テストを実行し,テスト結果をHTML形式のレポートにまと
めます.

オプションのタスクである,JunitおよびJunitReportタスクを利用することで,
JUnitの自動実行だけにとどまらず,結果をHTML形式でまとめるところまでが,
自動でできるのです.
これで,また一歩eXtremeに近づきました!
-->
<target name="runtests" depends="compile">
<junit printsummary="yes" haltonfailure="no">
<classpath>
<pathelement location="${build.classes}"/>
<pathelement path="${java.class.path}"/>
</classpath>

<formatter type="xml"/>

<batchtest fork="yes" todir="${reports.dir}">
<fileset dir="${src.dir}">
<include name="**/*Test.java"/>
</fileset>
</batchtest>
</junit>

<junitreport todir="${reports.dir}">
<fileset dir="${reports.dir}">
<include name="TEST-*.xml"/> </fileset>

<report format="frames" todir="${correctreports.dir}"/>
</junitreport> </target>
    <!--
        jarターゲットの定義
JARアーカイブを作成します.リリース時のアーカイブには,ユニットテストの クラスファイルは,必要ありませんので,jarタスクのexcludes属性を使用して, ユニットテストファイルを除外してJARアーカイブを作成しています.
このように,ユニットテストを含めない形でのアーカイブ作成が簡単にできるの で,ユニットテストのコードをテストパッケージとして,ソースコードから遠い 場所に配置しなくて済みますね. -->
<target name="jar" depends="apidocs"> <jar jarfile="${build.sample.jar}" basedir="${build.classes}" excludes="**/test/**"/> </target>
</project>

このビルドファイルを先ほどのプロジェクトに適用すると,以下のようなディレクトリ構成,生成物が自動生成されます.

/PROJECT_HOME(プロジェクトのホームディレクトリ)
  /src
    ・・・(ソースファイル)
  /build
    sample.jar(テストクラスの除いたクラスのJARアーカイブ)
    /apidocs
      ・・・(Javadocドキュメント)
    /classes
      ・・・(クラスファイル)
  /reports
    TEST-com.mamezou.sample.model.test.SampleModelTest.xml(テスト結果)
    TEST-com.mamezou.sample.view.test.SampleViewTest.xml(テスト結果)
    TESTS-TestSuites.xml(集計用Suites)
    /html
      ・・・(集計ファイル)

テスト結果の集計を参照するには,/PROJECT_HOME/reports/html/index.htmlにアクセスしてみてください.先ほどのサンプルと同じ結果が出力されることを確認することができます.

このように,テスト&テスト結果の集計の自動化を,Antを使うことによって簡単に行うことができることが分かっていただけたと思います.
また,自動化することで,ビルド時の手違いが入り込む余地を最小に抑え,リリース時のコストも抑えることができるができるようになります.

XPのプラクティスとして,テスト(Testing)の重要さについては,かなり広まってきた印象がありますが,次のステップとして,継続的インテグレーション(Continuous Integration)も取り入れてみてはいかがでしょうか?
退屈な作業を自動化することで,プログラミングを存分に楽しむことができるようになるでしょう!


謝辞

ドキュメントをまとめるにあたって,XP-jpメーリングリストのメンバである,懸田さんに有益なアドバイスを頂きました.
ありがとうございました.


最終更新日: 04/03/2001 13:02
株式会社 豆蔵
KURIHARA Tetsuya <kuri-t@mamezou.com>