Apache Ant を使用した開発

独自のタスクの作成

独自のタスクを作成するのは非常に簡単です。

  1. org.apache.tools.ant.Task または拡張用に設計された別のクラスを拡張する Java クラスを作成します。
  2. 各属性に対して、セッターメソッドを作成します。セッターメソッドは、単一の引数を取る public void メソッドでなければなりません。メソッド名は、set で始まり、それに属性名が続きます。属性名の最初の文字は大文字、残りは小文字にします*。つまり、file という名前の属性をサポートするには、setFile メソッドを作成します。引数の型に応じて、Ant は自動的にいくつかの変換を実行します。下記参照
  3. タスクにネストされた要素として他のタスクを含める場合(parallel など)、クラスは org.apache.tools.ant.TaskContainer インターフェースを実装する必要があります。この場合、タスクは他のネストされた要素をサポートできません。下記参照
  4. タスクが文字データ(開始タグと終了タグの間にネストされたテキスト)をサポートする必要がある場合は、public void addText(String) メソッドを作成します。Ant は、タスクに渡すテキストのプロパティを**展開しません**ことに注意してください。
  5. ネストされた要素ごとに、作成追加、またはaddConfigured メソッドを作成します。作成メソッドは、引数を取らず、Object 型を返す public メソッドでなければなりません。作成メソッドの名前は、create で始まり、それに要素名が続きます。追加(または addConfigured)メソッドは、引数なしのコンストラクタを持つ Object 型の単一引数を取る public void メソッドでなければなりません。追加(addConfigured)メソッドの名前は、addaddConfigured)で始まり、それに要素名が続きます。詳細については 下記参照
  6. BuildException をスローする引数のない public void execute() メソッドを作成します。このメソッドはタスク自体を実装します。

* 実際には、最初の文字以降の文字の大文字と小文字は Ant にとっては問題ではありませんが、すべて小文字を使用することが良い慣例です。

タスクのライフサイクル

  1. タスクに対応するタグを含む XML 要素は、解析時に UnknownElement に変換されます。この UnknownElement は、ターゲットオブジェクト内のリストに配置されるか、または別の UnknownElement 内に再帰的に配置されます。
  2. ターゲットが実行されると、各 UnknownElementperform() メソッドを使用して呼び出されます。これにより、タスクがインスタンス化されます。つまり、タスクは実行時にのみインスタンス化されます。
  3. タスクは、継承された project および location 変数を通じて、プロジェクトとビルドファイル内の場所への参照を取得します。
  4. ユーザーがこのタスクに id 属性を指定した場合、プロジェクトは実行時にこの新しく作成されたタスクへの参照を登録します。
  5. タスクは、継承された target 変数を通じて、属するターゲットへの参照を取得します。
  6. init() は実行時に呼び出されます。
  7. このタスクに対応する XML 要素の子要素はすべて、このタスクの createXXX() メソッドを使用して作成されるか、またはインスタンス化されて addXXX() メソッドを介してこのタスクに追加されます(実行時)。addConfiguredXXX() に対応する子要素はこの時点で作成されますが、実際のaddConfiguredメソッドは呼び出されません。
  8. このタスクのすべての属性は、対応する setXXX() メソッドを介して設定されます(実行時)。
  9. このタスクに対応する XML 要素内のコンテンツ文字データセクションは、その addText() メソッドを介してタスクに追加されます(実行時)。
  10. すべての子要素のすべての属性は、対応する setXXX() メソッドを介して設定されます(実行時)。
  11. このタスクに対応する XML 要素の子要素が addConfiguredXXX() メソッドに対して作成されている場合、これらのメソッドがここで呼び出されます。
  12. execute() は実行時に呼び出されます。「target1」と「target2」の両方が「target3」に依存する場合、ant target1 target2 を実行すると、「target3」内のすべてのタスクが 2 回実行されます。

Ant が実行する属性の変換

Ant は、属性の値を対応するセッターメソッドに渡す前に、常にプロパティを展開します。Ant 1.8 以降、単一のプロパティ参照を含む文字列の評価の結果として、文字列以外のオブジェクトになるように、Ant のプロパティ処理を拡張することができます。これらは、一致する型のセッターメソッドを介して直接割り当てられます。この動作を有効にするには、基本を超えた介入が必要なため、この使用パラダイムを許可する属性にフラグを付けることをお勧めします。

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

特定の属性に対して複数のセッターメソッドが存在する場合、String 引数を取るメソッドは、より具体的なメソッドに対して常に優先順位が低くなります。依然として Ant が選択できるセッターが複数ある場合、それらのうち 1 つだけが呼び出されますが、どれが呼び出されるかはわかりません。これは、Java 仮想マシンの実装によって異なります。

ネストされた要素のサポート

タスクが inner という名前のネストされた要素をサポートすると仮定しましょう。まず、このネストされた要素を表すクラスが必要です。多くの場合、ネストされた fileset 要素をサポートするために、org.apache.tools.ant.types.FileSet のような Ant のクラスのいずれかを使用するだけです。

ネストされた要素またはそのネストされた子要素の属性は、タスクに使用されるのと同じメカニズム(つまり、属性のセッターメソッド、ネストされたテキストの addText()、子要素の作成/追加/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 クラスを引数として取る public の 1 つの引数コンストラクタが必要です。これがオプション 1 と 2 の唯一の違いです。

2 と 3 の違いは、Ant がメソッドに渡す前にオブジェクトに何をしたかです。addInner() はコンストラクタが呼び出された直後のオブジェクトを受け取りますが、addConfiguredInner() は、この新しいオブジェクトの属性とネストされた子が処理されたのオブジェクトを受け取ります。

複数のオプションを使用した場合、メソッドの 1 つだけが呼び出されますが、どれが呼び出されるかはわかりません。これは、JVM の実装によって異なります。

ネストされた型

タスクが <typedef> を使用して定義された任意の型をネストする必要がある場合、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) {}
    }
}

このクラスは、PathMyFileSelector、および MyInterface を実装/拡張するいくつかの静的クラスを定義します。これらは次のように定義および使用できます。

<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 は、基本的にネストされた要素の追加メソッドと同じ addTask メソッド 1 つで構成されています。タスクインスタンスは、タスクの execute メソッドが呼び出されたときに(それ以前ではありません)、構成されます(属性とネストされた要素が処理されます)。

execute が呼び出される」とexecuteについて述べましたが、それは嘘でした ;-)。実際には、Ant はorg.apache.tools.ant.Task内のperformメソッドを呼び出し、それがexecuteを呼び出します。このメソッドは、ビルドイベントがトリガーされることを保証します。タスク内にネストされたタスクインスタンスを実行する場合は、executeではなく、これらのインスタンスでperformを呼び出す必要があります。

System.outストリームにメッセージを出力する独自のタスクを作成してみましょう。このタスクには、messageという属性が1つあります。

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.ant.BuildListenerインターフェースを実装する必要があります。リスナーは、次のイベントのビルドイベントを受信します。

<ant>または<subant>を介してビルドファイルが別のビルドファイルを呼び出すか、<antcall>を使用する場合、独自のターゲットおよびタスクレベルのイベントを送信する新しいAnt「プロジェクト」を作成しますが、ビルド開始/終了イベントは決して送信しません。Ant 1.6.2以降BuildListenerインターフェースにはSubBuildListenerという拡張機能があり、次の2つの新しいイベントを受信します。

これらのイベントに関心がある場合は、BuildListenerではなく新しいインターフェースを実装するだけです(そしてもちろん、リスナーを登録します)。

コマンドラインからリスナーをアタッチする場合は、-listenerオプションを使用できます。例えば

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

ビルドの進捗状況のXML表現を生成するリスナーを使用してAntを実行します。このリスナーは、標準出力へのログを生成するデフォルトリスナーと同様に、Antに含まれています。

注意:リスナーは、これらのストリームへの出力はAntのコアによってビルドイベントシステムにリダイレクトされるため、System.outSystem.errに直接アクセスしてはなりません。これらのストリームにアクセスすると、Antで無限ループが発生する可能性があります。Antのバージョンによっては、これによりビルドが終了するか、JVMがスタックスペースを使い果たすかのいずれかになります。同様に、ロガーはSystem.outSystem.errに直接アクセスしてはなりません。構成されたストリームを使用する必要があります。

注意:「ビルド開始」と「ビルド終了」イベントを除くBuildListenerのすべてのメソッドは、同時に複数のスレッドで発生する可能性があります。たとえば、Antが<parallel>タスクを実行している間などです。

お気に入りのログライブラリへのアダプターの作成は非常に簡単です。BuildListenerインターフェースを実装し、ロガーをインスタンス化し、そのインスタンスにメッセージを委任します。

ビルドを開始する際に、アダプタークラスとログライブラリをビルドクラスパスに提供し、上記のように-listenerオプションを使用してロガーをアクティブ化します。

public class MyLogAdapter implements BuildListener {

    private MyLogger getLogger() {
        final MyLogger log = MyLoggerFactory.getLogger(Project.class.getName());
        return log;
    }

    @Override
    public void buildStarted(final BuildEvent event) {
        final MyLogger log = getLogger();
        log.info("Build started.");
    }

    @Override
    public void buildFinished(final BuildEvent event) {
        final MyLogger logger = getLogger();
        MyLogLevelEnum loglevel = ... // map event.getPriority() to enum via Project.MSG_* constants
        boolean allOK = event.getException() == null;
        String logmessage = ... // create log message using data of the event and the message invoked
        logger.log(loglevel, logmessage);
    }

    // implement all methods in that way
}

ソースコード統合

Javaを介してAntを拡張するもう1つの方法は、既存のタスクに変更を加えることであり、これは積極的に推奨されています。既存のソースと新しいタスクの両方の変更をAntのコードベースに統合できます。これにより、すべてのユーザーがメリットを受け、メンテナンスの負荷が分散されます。

最新のソースを取得する方法と、ソースツリーへの再統合のための変更を送信する方法の詳細については、Apache Webサイトの参加方法ページを参照してください。

Antには、タスクの開発とテストを行う人々へのアドバイスを提供するタスクガイドラインもいくつかあります。タスクを自分自身で保持するつもりであっても、これは参考になるため、読むべきです。