Antでの開発

独自タスクの記述

独自のタスクを記述することは、以下に示すように非常に簡単です。

  1. org.apache.tools.ant.Taskクラスか 拡張可能に設計された他のクラスを 継承してJavaクラスを作成します。
  2. 各属性に対して、setterメソッドを記述します。 setterメソッドは、単一の引数を持つpublic voidメソッドでなくてはなりません。 メソッドの名前はsetで始まり、その後の最初の一文字を大文字に、 残りを小文字にした属性名が来なくてはなりません。* すなわち、fileという名前の属性をサポートするためには、 setFileメソッドを作成します。 引数の型に依存して、Antは以下のようないくつかの変換を行います。
  3. タスクが(parallelのような)ネストした要素として他のタスクを含む場合は、 インターフェイスorg.apache.tools.ant.TaskContainerを 実装しなければなりません。 そうすれば、作成したタスクは他のネストした要素をサポートする必要がなくなります。以下を見てください。
  4. テキストデータ(startとendタグで囲まれたテキスト)をサポートすべきタスクは、 public void addText(String)メソッドを記述します。 Antはタスクに渡されたテキストのプロパティーを展開しないことに注意してください。
  5. ネストした要素それぞれに対しては、createaddaddConfiguredメソッドを記述します。 createメソッドは、引数を持たずにObject型を返すpublicメソッドでなければなりません。 createメソッドの名前は、createで始まり、その後に要素名が来なくてはなりません。 add(または、addConfigured)メソッドは、引数なしのコンストラクターを持つObject型の単一の引数をとる、public voidでなければなりません。 add(または、addConfigured)メソッドの名前は、add (addConfigured)ではじまり、その後に要素名が来なくてはなりません。 詳細な記述は、以下を見てください。
  6. 引数を持たず、BuildExceptionをthrowする、public void executeメソッドを記述します。このメソッドはタスク自身を実装します。

* 実際、最初の一文字より以降を小文字にするのは、 Antにとっては、重要ではありませんが、全てを小文字にするのは、よい慣習です。

タスクのライフサイクル

  1. タスクは、解析時に引数を持たないコンストラクタを使ってインスタンス化されます。 これは、決して実行されないタスクであっても、インスタンス化されることを意味しています。
  2. タスクは継承したprojectlocation変数によって、そのプロジェクトに対する参照とビルドファイル内の位置を取得します。
  3. ユーザーが、タスクにid属性を指定した場合には、プロジェクトは解析時にこの新しく作成したタスクに対する参照を登録します。
  4. タスクは継承したtarget変数によって、属しているターゲットへの参照を取得します。
  5. 解析時にinit()が呼ばれます。
  6. 解析時に、このタスクに対応するすべてのXML要素の子要素は、createXXX()メソッドによって作成されるか、addXXX()メソッドによって、インスタンス化され追加されます。
  7. 実行時に、このタスクのすべての属性は、対応するsetXXXメソッドによってセットされます。
  8. 実行時に、このタスクに対応するXML要素内の、内容のテキストデータ部分は、 addTextメソッドによってタスクに追加されます。
  9. 実行時に、すべての子要素のすべての属性は、setXXXメソッドによってセットされます。
  10. 実行時に、execute()が呼び出されます。 上の中で、初期化ステップは1度だけ行われ、 タスクが 2回以上実行される場合は、execute()メソッドは 2回以上呼ばれます 例えば、target1target2の両方がtarget3に依存している場合、'ant target1 target2'を実行すると、target3のすべてのタスクは2回実行されます。

Antが行う属性の変換

Antは常に、属性の値を対応するセッタメソッドで渡す前に、プロパティーを展開します。

属性のセッタを記述する最も一般的な方法は、java.lang.String引数を使うことです。この場合、Antはあなたのタスクに(プロパティーを展開した後に)リテラルの値を渡します。しかし、あなたのセッタメソッドで渡すことのできる引数は、以下のようにもっとあります!

与えられた属性のための、2つ以上のセッタメソッドが提供されている場合、どうなるでしょうか?String引数をとるメソッドは、もっと明確な引数のメソッドに、常に負けます。Antが選択できるメソッドが、他にもあった場合、そのうちの1つだけが呼ばれます。しかし、私たちはどのメソッドが呼ばれるかは分かりません。これは、あなたのJava仮想マシンの実装に依存しています。

ネストした要素のサポート

innerという名前のネストした要素をサポートする場合を考えてみましょう。 まず最初に、このネストした要素を表すクラスが必要です。 多くの場合、単にネストしたfileset要素をサポートするために org.apache.tools.ant.types.FileSetのような、Antのクラスの一つを使いたいだけでしょう。

ネストした要素の属性やネストした子要素は、タスクに使われるのと同じ機構を 使って操作されます (例えば、属性のセッタメソッド、ネストしたテキストのためのaddText、や子要素のためのcreate/add/addConfiguredメソッドなど)。

現在、ネストした<inner>要素をサポートする NestedElementを作る場合、次の3つの選択肢があります。

  1. public NestedElement createInner()
  2. public void addInner(NestedElement anInner)
  3. public void addConfiguredInner(NestedElement anInner)

違いは何でしょう?

選択肢1はNestedElementのインスタンスを作成するタスクを作ります。この型に制限はありません。 選択肢2と3では、タスクに渡される前に AntはNestedInnerのインスタンスを作らなければなりません。 つまり、NestedInnerは、publicで引数のない コンストラクタか、Projectクラスだけを引数に持つコンストラクタを持たなければなりません。 これが選択肢1と2の唯一の違いです。

2と3の違いは、メソッドにオブジェクトが渡される前に、Antがオブジェクトに対して何を行うかです。 addInnerは、コンストラクタが呼ばれた後、オブジェクトを直接受け取ります。 addConfiguredInnerは、この新しいオブジェクトの 属性や子要素が操作されたに、オブジェクトを取得します。

この選択肢のうちの2つ以上を使った場合、どうなるでしょう? メソッドのうちの1つが呼ばれます。 しかし、どれが呼ばれるかはわかりません。 これは、あなたが使用するJava仮想マシンに依存します。

ネストしたタイプ

もし<taskdef>を使って宣言したタスクにネストしたタイプが必要であれば、 2つの選択肢があります。
  1. public void add(Type type)
  2. public void addConfigured(Type type)
1と2の違いは、前の節の2と3の違いと同じです。

例えば、org.apache.tools.ant.taskdefs.condition.Conditionタイプのオブジェクトを 取り扱い場合は、次のようなクラスになるでしょう。

public class MyTask extends Task {
    private List conditions = new ArrayList();
    public void add(Condition c) {
        conditions.add(c);
    }
    public void execute() {
     // iterator over the conditions
    }
}
  

このクラスは、次のように宣言されるでしょう。

<taskdef name="mytask" classname="MyTask" classpath="classes"/>
<typedef name="condition.equals"
         classname="org.apache.tools.ant.taskdefs.conditions.Equals"/>
<mytask>
    <condition.equals arg1="${debug}" arg2="true"/>
</mytask>
    

次は、もう少し複雑な例です。

public class Sample {
    public static class MyFileSelector implements FileSelector {
         public void setAttrA(int a) {}
         public void setAttrB(int b) {}
         public void add(Path path) {}
         public boolean isSelected(File basedir, String filename, File file) {
             return true;
         }
     }

    interface MyInterface {
        void setVerbose(boolean val);
    }        

    public static class BuildPath extends Path {
        public BuildPath(Project project) {
            super(project);
        }
        
        public void add(MyInterface inter) {}
        public void setUrl(String url) {}
    }

    public static class XInterface implements MyInterface {
        public void setVerbose(boolean x) {}
        public void setCount(int c) {}
    }
}
    

このクラスでは、Path、MyFileSelector、MyInterfaceを実装/継承した いくつかのstaticクラスが宣言されています。 これらは、次のように宣言され、利用されるでしょう。

    
<typedef name="myfileselector" classname="Sample$MyFileSelector" classpath="classes" loaderref="classes"/> <typedef name="buildpath" classname="Sample$BuildPath" classpath="classes" loaderref="classes"/> <typedef name="xinterface" classname="Sample$XInterface" classpath="classes" loaderref="classes"/> <copy todir="copy-classes"> <fileset dir="classes"> <myfileselector attra="10" attrB="-10"> <buildpath path="." url="abc"> <xinterface count="4"/> </buildpath> </myfileselector> </fileset> </copy>

TaskContainer

TaskContainerは、ネストした要素のためのadd methodと基本的に同じ、addTaskという単一のメソッドから成ります。あなたのタスクのexecuteメソッドが実行された時に、タスクのインスタンスは設定されます(属性やネストした要素が操作されます)。executeが実行される前ではありません。

executeが呼ばれたときと言いましたが、 うそをつきました;-)。 実際は、executeのあとに、 Antはorg.apache.tools.ant.Taskperformメソッドを呼ぶでしょう。 このメソッドは必ず、Build Eventsをトリガーとします。 あなたのタスクの中でネストしたタスクを実行するとき、 executeメソッドの代わりに、インスタンスの performも実行しなければなりません。

System.outストリームに、メッセージを出力する 独自のタスクを書いてみましょう。 このタスクはmessageという一つの属性を持ちます。

package com.mydomain;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;

public class MyVeryOwnTask extends Task {
    private String msg;

    // The method executing the task
    public void execute() throws BuildException {
        System.out.println(msg);
    }

    // The setter for the "message" attribute
    public void setMessage(String msg) {
        this.msg = msg;
    }
}

これは本当に簡単です ;-)

システムにあなたのタスクを追加することも、もっと簡単です。

  1. Antを開始するときに、あなたのタスクを実装したクラスが、クラスパスに含まれているか確認してください。
  2. <taskdef>要素をあなたのプロジェクトに追加してください。 これは実際、システムにあなたのタスクを追加します。
  3. ビルドファイルの残りの場所で、あなたのタスクを使ってください。

<?xml version="1.0"?>

<project name="OwnTaskExample" default="main" basedir=".">
  <taskdef name="mytask" classname="com.mydomain.MyVeryOwnTask"/>

  <target name="main">
    <mytask message="Hello World! MyVeryOwnTask works!"/>
  </target>
</project>

例 2

タスクを作ったビルドファイルから、直接タスクを使うには、 コンパイルより後のターゲットの内側に <taskdef>宣言をします。 コードがコンパイルされた場所を示すために <taskdef>classpath属性を使ってください。

<?xml version="1.0"?>

<project name="OwnTaskExample2" default="main" basedir=".">

  <target name="build" >
    <mkdir dir="build"/>
    <javac srcdir="source" destdir="build"/>
  </target>

  <target name="declare" depends="build">
    <taskdef name="mytask"
        classname="com.mydomain.MyVeryOwnTask"
        classpath="build"/>
  </target>

  <target name="main" depends="declare">
    <mytask message="Hello World! MyVeryOwnTask works!"/>
  </target>
</project>

タスクを追加するもう1つの(もっと永続的な)方法は、 タスクの名前と実装クラスの名前を org.apache.tools.ant.taskdefsパッケージの default.propertiesファイルに追加することです。 そうすれば、組み込みタスクのように、独自タスクを使うことができます。


ビルドイベント

Antは、プロジェクトのビルドに必要なタスクの実行のための、ビルドイベントを生成することができます。 イベントを受け取るリスナーをAntに取り付けることができます。 この機能は、例えば、AntをGUIに接続したり、AntをIDEに統合するときに使うことができます。

ビルドイベントを使うためには、AntのProjectオブジェクトを作成する必要があります。 そして、プロジェクトにあなたのリスナーを追加するために addBuildListenerを呼びます。 リスナーはorg.apache.tools.antBuildListenerインターフェイスを実装しなければなりません。 リスナーは次のようなビルドイベントを受け取ります。

コマンドラインから、リスナーを追加したい場合、 -listenerオプションを使うことができます。 例えば:

ant -listener org.apache.tools.ant.XmlLogger

これは、ビルドの進行を表示するXMLを生成するリスナーをつけて、Antを実行します。このリスナーはAntに含まれるデフォルトのリスナーで、標準出力へのログを生成します。

注意: リスナーは System.out や System.err に直接アクセスしてはいけません。 これらのストリームの出力は、Antコアによりビルドシステムへリダイレクトされるからです。 これらのストリームにアクセスすることは、Antの中で無限ループを引き起こします。 Antのバージョンによっては、ビルドの停止やJava VMのスタックオーバーを引き起こします。 ロガーもまた、System.out や System.err に直接アクセスしてはいけません。 これらのストリームは設定によって利用する必要があります。


ソースコードの統合

JavaでAntを拡張する他の方法は、既存のタスクを変更することで、それはとても推奨されます。 既存のソースに対する各変更や、新しいタスクは、Antのコードベースに組み入れることができます。それはすべてのユーザーの利益になり、全体の負担の量を拡散させます。

どうぞ、JakartaウェブサイトのGetting Involvedページを調べてみてください。 共同で利用されるソースツリーに対して、どのように最新のソースを取得するか、どのように変更を提出するかの詳細が書かれています。

Antはまた、いくつかのタスクのガイドラインをもっています。 ガイドラインは、タスクを開発、テストする人々にアドバイスを提供します。 あなたが、独自のタスクを自分だけで使おうとする場合でも、有益な情報が書かれているので、ガイドラインを是非読むべきです。


Copyright © 2000-2004 The Apache Software Foundation. All rights Reserved.

[訳注:これは風間一洋、宮本信二が翻訳しました。日本語訳に対するコメントがあれば report@jajakarta.orgに送ってください]