独自のタスクを作成するのは非常に簡単です。
org.apache.tools.ant.Task または拡張用に設計された別のクラスを拡張する Java クラスを作成します。public void メソッドでなければなりません。メソッド名は、set で始まり、それに属性名が続きます。属性名の最初の文字は大文字、残りは小文字にします*。つまり、file という名前の属性をサポートするには、setFile メソッドを作成します。引数の型に応じて、Ant は自動的にいくつかの変換を実行します。下記参照。parallel など)、クラスは org.apache.tools.ant.TaskContainer インターフェースを実装する必要があります。この場合、タスクは他のネストされた要素をサポートできません。下記参照。public void addText(String) メソッドを作成します。Ant は、タスクに渡すテキストのプロパティを**展開しません**ことに注意してください。Object 型を返す public メソッドでなければなりません。作成メソッドの名前は、create で始まり、それに要素名が続きます。追加(または addConfigured)メソッドは、引数なしのコンストラクタを持つ Object 型の単一引数を取る public void メソッドでなければなりません。追加(addConfigured)メソッドの名前は、add(addConfigured)で始まり、それに要素名が続きます。詳細については 下記参照。BuildException をスローする引数のない public void execute() メソッドを作成します。このメソッドはタスク自体を実装します。* 実際には、最初の文字以降の文字の大文字と小文字は Ant にとっては問題ではありませんが、すべて小文字を使用することが良い慣例です。
UnknownElement に変換されます。この UnknownElement は、ターゲットオブジェクト内のリストに配置されるか、または別の UnknownElement 内に再帰的に配置されます。UnknownElement は perform() メソッドを使用して呼び出されます。これにより、タスクがインスタンス化されます。つまり、タスクは実行時にのみインスタンス化されます。project および location 変数を通じて、プロジェクトとビルドファイル内の場所への参照を取得します。target 変数を通じて、属するターゲットへの参照を取得します。init() は実行時に呼び出されます。createXXX() メソッドを使用して作成されるか、またはインスタンス化されて addXXX() メソッドを介してこのタスクに追加されます(実行時)。addConfiguredXXX() に対応する子要素はこの時点で作成されますが、実際のaddConfiguredメソッドは呼び出されません。setXXX() メソッドを介して設定されます(実行時)。addText() メソッドを介してタスクに追加されます(実行時)。setXXX() メソッドを介して設定されます(実行時)。addConfiguredXXX() メソッドに対して作成されている場合、これらのメソッドがここで呼び出されます。execute() は実行時に呼び出されます。「target1」と「target2」の両方が「target3」に依存する場合、ant target1 target2 を実行すると、「target3」内のすべてのタスクが 2 回実行されます。Ant は、属性の値を対応するセッターメソッドに渡す前に、常にプロパティを展開します。Ant 1.8 以降、単一のプロパティ参照を含む文字列の評価の結果として、文字列以外のオブジェクトになるように、Ant のプロパティ処理を拡張することができます。これらは、一致する型のセッターメソッドを介して直接割り当てられます。この動作を有効にするには、基本を超えた介入が必要なため、この使用パラダイムを許可する属性にフラグを付けることをお勧めします。
属性セッターを作成する最も一般的な方法は、java.lang.String 引数を使用することです。この場合、Ant はリテラル値(プロパティ展開後)をタスクに渡します。しかし、もっとあります!セッターメソッドの引数が次の場合
boolean の場合、ビルドファイルで指定された値が「true」、「yes」、または「on」のいずれかの場合、メソッドには「true」が渡され、それ以外の場合は「false」が渡されます。char または java.lang.Character の場合、メソッドにはビルドファイルで指定された値の最初の文字が渡されます。int、short など)の場合、Ant は属性の値をこの型に変換するため、その属性に対して数値ではない入力を受信することはありません。java.io.File の場合、Ant はまず、ビルドファイルで指定された値が絶対パス名を表しているかどうかを判断します。絶対パス名ではない場合、Ant は値をプロジェクトの basedir を基準としたパス名として解釈します。org.apache.tools.ant.types.Resource の場合、Ant は文字列を上記の java.io.File として解決してから、org.apache.tools.ant.types.resources.FileResource として渡します。Ant 1.8 以降org.apache.tools.ant.types.Path の場合、Ant はビルドファイルで指定された値をトークン化し、「:」と「;」をパスセパレータとして受け入れます。相対パス名は、プロジェクトの basedir を基準とした相対パスとして解釈されます。java.lang.Class の場合、Ant はビルドファイルで指定された値を Java クラス名として解釈し、システムクラスローダーから指定されたクラスを読み込みます。String 引数を持つコンストラクタを持つ他の型の場合、Ant はこのコンストラクタを使用して、ビルドファイルで指定された値から新しいインスタンスを作成します。org.apache.tools.ant.types.EnumeratedAttribute のサブクラスの場合、Ant はこのクラスの setValue メソッドを呼び出します。タスクが列挙属性(事前に定義された値セットの一部でなければならない値を持つ属性)をサポートする必要がある場合に使用します。例として、org/apache/tools/ant/taskdefs/FixCRLF.java と setCrで使用される内部AddAsisRemoveクラスを参照してください。EnumeratedAttribute を使用するよりも簡単で、よりクリーンなコードになります。列挙型における toString() のオーバーライドは無視されます。ビルドファイルでは宣言された名前(Enum.getName() を参照)を使用する必要があります。ビルドファイルでより見栄えが良くなるように、通常の Java スタイルとは対照的に、小文字の列挙定数名を使用することを検討してください。Ant 1.7.0 以降特定の属性に対して複数のセッターメソッドが存在する場合、String 引数を取るメソッドは、より具体的なメソッドに対して常に優先順位が低くなります。依然として Ant が選択できるセッターが複数ある場合、それらのうち 1 つだけが呼び出されますが、どれが呼び出されるかはわかりません。これは、Java 仮想マシンの実装によって異なります。
タスクが inner という名前のネストされた要素をサポートすると仮定しましょう。まず、このネストされた要素を表すクラスが必要です。多くの場合、ネストされた fileset 要素をサポートするために、org.apache.tools.ant.types.FileSet のような Ant のクラスのいずれかを使用するだけです。
ネストされた要素またはそのネストされた子要素の属性は、タスクに使用されるのと同じメカニズム(つまり、属性のセッターメソッド、ネストされたテキストの addText()、子要素の作成/追加/addConfigured メソッド)を使用して処理されます。
これで、ネストされた <inner> 要素に使用される NestedElement クラスができました。3 つのオプションがあります。
public NestedElement createInner()public void addInner(NestedElement anInner)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 つのオプションがあります。
public void add(Type type)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 を実装/拡張するいくつかの静的クラスを定義します。これらは次のように定義および使用できます。
<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 は、基本的にネストされた要素の追加メソッドと同じ 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;
}
}
本当にこれだけです ;-)
システムへのタスクの追加も非常に簡単です。
<taskdef>要素を追加します。これにより、タスクがシステムに追加されます。<?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>
作成したビルドファイルから直接タスクを使用するには、コンパイル後にターゲット内に<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.outとSystem.errに直接アクセスしてはなりません。これらのストリームにアクセスすると、Antで無限ループが発生する可能性があります。Antのバージョンによっては、これによりビルドが終了するか、JVMがスタックスペースを使い果たすかのいずれかになります。同様に、ロガーはSystem.outとSystem.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には、タスクの開発とテストを行う人々へのアドバイスを提供するタスクガイドラインもいくつかあります。タスクを自分自身で保持するつもりであっても、これは参考になるため、読むべきです。