ANT资料翻译-home/manual/developing_with_ant/tutorials/writing task

Tutorial: Writing Tasks

编写task

This document provides a step by step tutorial for writing tasks.

一步步教你如何编写tasks

Content

Set up the build environment

设置build环境

Apache Ant builds itself, we are using Ant too (why we would write a task if not? :-) therefore we should use Ant for our build.

We choose a directory as root directory. All things will be done here if I say nothing different. I will reference this directory as root-directory of our project. In this root-directory we create a text file names build.xml. What should Ant do for us?

Apache的ant可以自构建,为什么我们还需要写task?原因是我们需要构建我们自己项目的task。我们选择一个目录作为根目录,通常情况下所有的事情都会在该目录下进行。我将会使用这个目录作为项目根目录。我们要创建一个叫做build.xml的文件,那么ant应该为我们做什么了?

  • compiles my stuff // 编译代码
  • make the jar, so that I can deploy it // 打包成jar
  • clean up everything // 清除所有

So the buildfile contains three targets.

因此build文件将会包含三个目标。

<?xml version="1.0" encoding="ISO-8859-1"?>
<project name="MyTask" basedir="." default="jar">

    <target name="clean" description="Delete all generated files">
        <delete dir="classes"/>
        <delete file="MyTasks.jar"/>
    </target>

    <target name="compile" description="Compiles the Task">
        <javac srcdir="src" destdir="classes"/>
    </target>

    <target name="jar" description="JARs the Task">
        <jar destfile="MyTask.jar" basedir="classes"/>
    </target>

</project>

This buildfile uses often the same value (src, classes, MyTask.jar), so we should rewrite that using <property>s. On second there are some handicaps: <javac> requires that the destination directory exists; a call of "clean" with a non existing classes directory will fail; "jar" requires the execution of some steps before. So the refactored code is:

这个build文件经常会用到同样的值(src,classes,MyTask.jar),因此我们应该使用property标签重写它们。另外,有一些隐藏缺陷:javac标签要求目标目录存在;clean task如果作用于一个不存在的目录将会失败;jar task 需要别的task做支持。所以我们要重构代码:


<?xml version="1.0" encoding="ISO-8859-1"?>
<project name="MyTask" basedir="." default="jar">

    <property name="src.dir" value="src"/>
    <property name="classes.dir" value="classes"/>

    <target name="clean" description="Delete all generated files">
        <delete dir="${classes.dir}" failοnerrοr="false"/>
        <delete file="${ant.project.name}.jar"/>
    </target>

    <target name="compile" description="Compiles the Task">
        <mkdir dir="${classes.dir}"/>
        <javac srcdir="${src.dir}" destdir="${classes.dir}"/>
    </target>

    <target name="jar" description="JARs the Task" depends="compile">
        <jar destfile="${ant.project.name}.jar" basedir="${classes.dir}"/>
    </target>

</project>

ant.project.name is one of the build-in properties [1] of Ant.

ant.project.name是ant的内建属性。

Write the Task // 写任务

Now we write the simplest Task - a HelloWorld-Task (what else?). Create a text file HelloWorld.java in the src-directory with:

写一个最简单的HelloWorld Task

public class HelloWorld {
    public void execute() {
        System.out.println("Hello World");
    }
}

and we can compile and jar it with ant (default target is "jar" and via its depends-clause the "compile" is executed before).

我们可以使用ant编译或者打包它

Use the Task // 使用task

But after creating the jar we want to use our new Task. Therefore we need a new target "use". Before we can use our new task we have to declare it with <taskdef> [2]. And for easier process we change the default clause:

在创建jar之后,我们想要使用新的task。因此我们需要一个新的名为use的target。在使用新的task之前,我们需要使用taskdef标签声明它。为了更好的处理我们改变了默认子句。

<?xml version="1.0" encoding="ISO-8859-1"?>
<project name="MyTask" basedir="." default="use">

    ...

    <target name="use" description="Use the Task" depends="jar">
        <taskdef name="helloworld" classname="HelloWorld" classpath="${ant.project.name}.jar"/>
        <helloworld/>
    </target>

</project>

Important is the classpath-attribute. Ant searches in its /lib directory for tasks and our task isn't there. So we have to provide the right location.

最重要的是classpath属性。ant会在它自己的lib文件夹下查找task,但我们的task不在那里面。所以必须提供准确的位置。

Now we can type in ant and all should work ...

使用ant运行它

Buildfile: build.xml

compile:
    [mkdir] Created dir: C:\tmp\anttests\MyFirstTask\classes
    [javac] Compiling 1 source file to C:\tmp\anttests\MyFirstTask\classes

jar:
      [jar] Building jar: C:\tmp\anttests\MyFirstTask\MyTask.jar

use:
[helloworld] Hello World

BUILD SUCCESSFUL
Total time: 3 seconds

Integration with TaskAdapter // 与task适配器的整合

Our class has nothing to do with Ant. It extends no superclass and implements no interface. How does Ant know to integrate? Via name convention: our class provides a method with signature public void execute(). This class is wrapped by Ant's org.apache.tools.ant.TaskAdapter which is a task and uses reflection for setting a reference to the project and calling the execute() method.

我们的类没有做任何事情。它没有继承超类,也没有实现接口。那ant是怎么知道整合这个类的?答案是通过命名约定:我们的类有个public void execute()方法。我们的类被包装在了ant的TaskAdapter类里面,这是个task类并且通过反射引用到了我们的工程,因此ant就可以调用到execute()方法。

Setting a reference to the project? Could be interesting. The Project class gives us some nice abilities: access to Ant's logging facilities getting and setting properties and much more. So we try to use that class:

设置工程的引用?这个很有趣。这个Project类给了我们很多有用的功能,比如处理ant的日志工具,get和set属性,还有更多。我们可以如下使用:

import org.apache.tools.ant.Project;

public class HelloWorld {

    private Project project;

    public void setProject(Project proj) {
        project = proj;
    }

    public void execute() {
        String message = project.getProperty("ant.project.name");
        project.log("Here is project '" + message + "'.", Project.MSG_INFO);
    }
}

and the execution with ant will show us the expected

结果如下

use:
Here is project 'MyTask'.

Deriving from Ant's Task // 源自Task

Ok, that works ... But usually you will extend org.apache.tools.ant.Task. That class is integrated in Ant, get's the project-reference, provides documentation fields, provides easier access to the logging facility and (very useful) gives you the exact location where in the buildfile this task instance is used.

好的,上面那种是对的。。。但是通常你会继承于org.apache.tools.ant.Task。这个类是ant提供的,可以得到project引用,提供文档域,更容易获取到日志工具,并且获取到包含这个task的build.xlm文件的路径。

Oki-doki - let's us use some of these:

import org.apache.tools.ant.Task;

public class HelloWorld extends Task {
    public void execute() {
        // use of the reference to Project-instance
        String message = getProject().getProperty("ant.project.name");

        // Task's log method
        log("Here is project '" + message + "'.");

        // where this task is used?
        log("I am used in: " +  getLocation() );
    }
}

which gives us when running

use:
[helloworld] Here is project 'MyTask'.
[helloworld] I am used in: C:\tmp\anttests\MyFirstTask\build.xml:23:

Accessing the Task's Project // 处理Task的工程

The parent project of your custom task may be accessed through method getProject(). However, do not call this from the custom task constructor, as the return value will be null. Later, when node attributes or text are set, or methodexecute() is called, the Project object is available.

也许我们可以使用getProject()来获取到当前任务的父工程。然而,不要在当前任务类的构造器里面调用该方法,否则你只会得到null值。之后,当节点属性或文本被设置,或者已调用execute()方法,这个工程对象才是可用的。

Here are two useful methods from class Project:

这里有两个Project方法

  • String getProperty(String propertyName)
  • String replaceProperties(String value)

The method replaceProperties() is discussed further in section Nested Text.

方法replaceProperties()将在下面讨论。

Attributes //属性

Now we want to specify the text of our message (it seems that we are rewriting the <echo/> task :-). First we well do that with an attribute. It is very easy - for each attribute provide a public void set<attributename>(<type> newValue)method and Ant will do the rest via reflection.

现在我们想要指定文本内容了。首先我们可以用一个属性做到它。这很简单,为每个属性设置一个set方法就可以了,ant会反射调用。

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

public class HelloWorld extends Task {

    String message;
    public void setMessage(String msg) {
        message = msg;
    }

    public void execute() {
        if (message==null) {
            throw new BuildException("No message set.");
        }
        log(message);
    }

}

Oh, what's that in execute()? Throw a BuildException? Yes, that's the usual way to show Ant that something important is missed and complete build should fail. The string provided there is written as build-failes-message. Here it's necessary because the log() method can't handle a null value as parameter and throws a NullPointerException. (Of course you can initialize the message with a default string.)

在execute方法里头是什么?抛出BuildException?是的,通常这是表示重要参数丢失最有效的方式。文本内容则告诉我们构建失败了。这是必须的,因为Log()方法不能传入null参数,否则会抛出空指针异常。当然,你也可以初始化message。

After that we have to modify our buildfile:

修改如下

    <target name="use" description="Use the Task" depends="jar">
        <taskdef name="helloworld"
                 classname="HelloWorld"
                 classpath="${ant.project.name}.jar"/>
        <helloworld message="Hello World"/>
    </target>

That's all.

Some background for working with attributes: Ant supports any of these datatypes as arguments of the set-method:

// 对于set方法,ant支持下面这些类型

  • elementary data type like intlong, ... // 基本类型
  • its wrapper classes like java.lang.Integerjava.lang.Long, ...// 基本类型的封装类
  • java.lang.String
  • some more classes (e.g. java.io.File; see Manual 'Writing Your Own Task' [3])
  • Any Java Object parsed from Ant 1.8's Property Helper

Before calling the set-method all properties are resolved. So a <helloworld message="${msg}"/> would not set the message string to "${msg}" if there is a property "msg" with a set value.

在调用set方法前,所有的属性值都是被处理过的。所有,对于<helloworld message="${msg}"/>,如果存在一个名为msg的属性,那么message属性值不会是${msg}。


Nested Text //内嵌文本

Maybe you have used the <echo> task in a way like <echo>Hello World</echo>. For that you have to provide a public void addText(String text) method.

也许你已经使用过echo标签了,比如<echo>Hello World</echo>. 为了能够像echo一样,在自定义的task里面使用内嵌文本,我们还需要一个addText方法。

...
public class HelloWorld extends Task {
    private String message;
    ...
    public void addText(String text) {
        message = text;
    }
    ...
}

But here properties are not resolved! For resolving properties we have to use Project's replaceProperties(String propname) : String method which takes the property name as argument and returns its value (or ${propname} if not set).

但是这个属性值是不会被解析的!如果要解析内嵌文本,我们需要使用replaceProperties方法。

Thus, to replace properties in the nested node text, our method addText() can be written as:

所以,为了能够在内嵌文本中替代属性,我们修改代码如下。

    public void addText(String text) {
        message = getProject().replaceProperties(text);
    }

Nested Elements //内嵌元素

There are several ways for inserting the ability of handling nested elements. See the Manual [4] for other. We use the first way of the three described ways. There are several steps for that:

有很多种方法可以处理子元素。我们使用其中一种,下面是步骤。

  1. We create a class for collecting all the info the nested element should contain. This class is created by the same rules for attributes and nested elements as for the task (set<attributename>() methods).// 我们为子元素创建一个类。这个类的创建如同一般的Task那样。
  2. The task holds multiple instances of this class in a list. // 这个task使用一个集合保存这个类的多个实例的引用。
  3. A factory method instantiates an object, saves the reference in the list and returns it to Ant Core.// 一个工厂方法,用于实例化一个子元素,在集合中保存引用并返回。
  4. The execute() method iterates over the list and evaluates its values.// 在execute方法中迭代集合并计算。
import java.util.Vector;
import java.util.Iterator;
...
    public void execute() {
        if (message!=null) log(message);
        for (Iterator it=messages.iterator(); it.hasNext(); ) {      // 4
            Message msg = (Message)it.next();
            log(msg.getMsg());
        }
    }


    Vector messages = new Vector();                                  // 2

    public Message createMessage() {                                 // 3
        Message msg = new Message();
        messages.add(msg);
        return msg;
    }

    public class Message {                                           // 1
        public Message() {}

        String msg;
        public void setMsg(String msg) { this.msg = msg; }
        public String getMsg() { return msg; }
    }
...

Then we can use the new nested element. But where is xml-name for that defined? The mapping XML-name : classname is defined in the factory method: public classname createXML-name(). Therefore we write in the buildfile

然后我们可以使用新的内嵌元素了。但是谁能告诉我内嵌元素的名称是哪里定义的?是这样的,我们使用了工厂方法来定义内嵌原色的名称:约定的工厂方法标签卫 public classname createXML-name(). 案例如下。

        <helloworld message="pro">
            <message msg="Nested Element 1"/>
            <message msg="Nested Element 2"/>nest text
        </helloworld>
作者补充:关于上面那段代码的默认约定,原文讲的很简单,我感觉有很多约定:1.工厂方法的约定 2. 子元素集合名称的约定 3. 子元素名称的约定
作者补充:关于这个嵌套元素的执行顺序是这样的:
  1.构造helloWord元素
  2.设置pro属性
  3.嵌入文本nest test
  4.调用createMessage方法
  5.设置message属性msg
  6.当所有子元素构建完了,再向上调用execute方法。
概括来说就是先构建好父元素,然后调用父元素与子元素的关联方法,接着构建好子元素。

Note that if you choose to use methods 2 or 3, the class that represents the nested element must be declared as static

Our task in a little more complex version

稍微复杂点的版本的task

For recapitulation now a little refactored buildfile:

为了突出重点,我们重构下build.xml

<?xml version="1.0" encoding="ISO-8859-1"?>
<project name="MyTask" basedir="." default="use">

    <property name="src.dir" value="src"/>
    <property name="classes.dir" value="classes"/>

    <target name="clean" description="Delete all generated files">
        <delete dir="${classes.dir}" failοnerrοr="false"/>
        <delete file="${ant.project.name}.jar"/>
    </target>

    <target name="compile" description="Compiles the Task">
        <mkdir dir="${classes.dir}"/>
        <javac srcdir="${src.dir}" destdir="${classes.dir}"/>
    </target>

    <target name="jar" description="JARs the Task" depends="compile">
        <jar destfile="${ant.project.name}.jar" basedir="${classes.dir}"/>
    </target>


    <target name="use.init"
            description="Taskdef the HelloWorld-Task"
            depends="jar">
        <taskdef name="helloworld"
                 classname="HelloWorld"
                 classpath="${ant.project.name}.jar"/>
    </target>


    <target name="use.without"
            description="Use without any"
            depends="use.init">
        <helloworld/>
    </target>

    <target name="use.message"
            description="Use with attribute 'message'"
            depends="use.init">
        <helloworld message="attribute-text"/>
    </target>

    <target name="use.fail"
            description="Use with attribute 'fail'"
            depends="use.init">
        <helloworld fail="true"/>
    </target>

    <target name="use.nestedText"
            description="Use with nested text"
            depends="use.init">
        <helloworld>nested-text</helloworld>
    </target>

    <target name="use.nestedElement"
            description="Use with nested 'message'"
            depends="use.init">
        <helloworld>
            <message msg="Nested Element 1"/>
            <message msg="Nested Element 2"/>
        </helloworld>
    </target>


    <target name="use"
            description="Try all (w/out use.fail)"
            depends="use.without,use.message,use.nestedText,use.nestedElement"
    />

</project>
And the code of the task:
import org.apache.tools.ant.Task;
import org.apache.tools.ant.BuildException;
import java.util.Vector;
import java.util.Iterator;

/**
 * The task of the tutorial.
 * Print a message or let the build fail.
 * @since 2003-08-19
 */
public class HelloWorld extends Task {


    /** The message to print. As attribute. */
    String message;
    public void setMessage(String msg) {
        message = msg;
    }

    /** Should the build fail? Defaults to false. As attribute. */
    boolean fail = false;
    public void setFail(boolean b) {
        fail = b;
    }

    /** Support for nested text. */
    public void addText(String text) {
        message = text;
    }


    /** Do the work. */
    public void execute() {
        // handle attribute 'fail'
        if (fail) throw new BuildException("Fail requested.");

        // handle attribute 'message' and nested text
        if (message!=null) log(message);

        // handle nested elements
        for (Iterator it=messages.iterator(); it.hasNext(); ) {
            Message msg = (Message)it.next();
            log(msg.getMsg());
        }
    }


    /** Store nested 'message's. */
    Vector messages = new Vector();

    /** Factory method for creating nested 'message's. */
    public Message createMessage() {
        Message msg = new Message();
        messages.add(msg);
        return msg;
    }

    /** A nested 'message'. */
    public class Message {
        // Bean constructor
        public Message() {}

        /** Message to print. */
        String msg;
        public void setMsg(String msg) { this.msg = msg; }
        public String getMsg() { return msg; }
    }

}
And it works:
C:\tmp\anttests\MyFirstTask>ant
Buildfile: build.xml

compile:
    [mkdir] Created dir: C:\tmp\anttests\MyFirstTask\classes
    [javac] Compiling 1 source file to C:\tmp\anttests\MyFirstTask\classes

jar:
      [jar] Building jar: C:\tmp\anttests\MyFirstTask\MyTask.jar

use.init:

use.without:

use.message:
[helloworld] attribute-text

use.nestedText:
[helloworld] nested-text

use.nestedElement:
[helloworld]
[helloworld]
[helloworld]
[helloworld]
[helloworld] Nested Element 1
[helloworld] Nested Element 2

use:

BUILD SUCCESSFUL
Total time: 3 seconds
C:\tmp\anttests\MyFirstTask>ant use.fail
Buildfile: build.xml

compile:

jar:

use.init:

use.fail:

BUILD FAILED
C:\tmp\anttests\MyFirstTask\build.xml:36: Fail requested.

Total time: 1 second
C:\tmp\anttests\MyFirstTask>
Next step: test ...

Test the Task // 测试Task

We have written a test already: the use.* tasks in the buildfile. But its difficult to test that automatically. Common (and in Ant) used is JUnit for that. For testing tasks Ant provides a baseclassorg.apache.tools.ant.BuildFileTest. This class extends junit.framework.TestCase and can therefore be integrated into the unit tests. But this class provides some for testing tasks useful methods: initialize Ant, load a buildfile, execute targets, expecting BuildExceptions with a specified text, expect a special text in the output log ...

我们已经写过一个测试了:文件中的use.*。但是它不是自动的。通常我们使用junit来测试。ant提供了类BuildFileTest来测试Task。这个类继承自junit.framework.TestCase,所以能够整合进单元测试。这个类提供了很多有用的测试函数:比如初始化ant,载入build.xml,执行目标,抛出异常等。

In Ant it is usual that the testcase has the same name as the task with a prepending Test, therefore we will create a file HelloWorldTest.java. Because we have a very small project we can put this file into src directory (Ant's own testclasses are in /src/testcases/...). Because we have already written our tests for "hand-test" we can use that for automatic tests, too. But there is one little problem we have to solve: all test supporting classes are not part of the binary distribution of Ant. So you can build the special jar file from source distro with target "test-jar" or you can download a nightly build from http://gump.covalent.net/jars/latest/ant/ant-testutil.jar [5].

通常测试案例的名称是Task的名称后加“Test”,所以我们可以创建名为HelloWorldTest.java的测试类。由于我们的工程比较小,所以我们把测试类直接放入src文件夹下。因为我们已经按照“hand-test”来写我们的测试类了,所以我么可以实现自动测试。但有个小小的问题:所有的测试类都不会是ant二进制版本的一部分,因此你能够实用“test-jar”来构建原版本的指定jar文件。

For executing the test and creating a report we need the optional tasks <junit> and <junitreport>. So we add to the buildfile:

...
<project name="MyTask" basedir="." default="test">
...
    <property name="ant.test.lib" value="ant-testutil.jar"/>
    <property name="report.dir"   value="report"/>
    <property name="junit.out.dir.xml"  value="${report.dir}/junit/xml"/>
    <property name="junit.out.dir.html" value="${report.dir}/junit/html"/>

    <path id="classpath.run">
        <path path="${java.class.path}"/>
        <path location="${ant.project.name}.jar"/>
    </path>

    <path id="classpath.test">
        <path refid="classpath.run"/>
        <path location="${ant.test.lib}"/>
    </path>

    <target name="clean" description="Delete all generated files">
        <delete failοnerrοr="false" includeEmptyDirs="true">
            <fileset dir="." includes="${ant.project.name}.jar"/>
            <fileset dir="${classes.dir}"/>
            <fileset dir="${report.dir}"/>
        </delete>
    </target>

    <target name="compile" description="Compiles the Task">
        <mkdir dir="${classes.dir}"/>
        <javac srcdir="${src.dir}" destdir="${classes.dir}" classpath="${ant.test.lib}"/>
    </target>
...
    <target name="junit" description="Runs the unit tests" depends="jar">
        <delete dir="${junit.out.dir.xml}"/>
        <mkdir  dir="${junit.out.dir.xml}"/>
        <junit printsummary="yes" haltonfailure="no">
            <classpath refid="classpath.test"/>
            <formatter type="xml"/>
            <batchtest fork="yes" todir="${junit.out.dir.xml}">
                <fileset dir="${src.dir}" includes="**/*Test.java"/>
            </batchtest>
        </junit>
    </target>

    <target name="junitreport" description="Create a report for the rest result">
        <mkdir dir="${junit.out.dir.html}"/>
        <junitreport todir="${junit.out.dir.html}">
            <fileset dir="${junit.out.dir.xml}">
                <include name="*.xml"/>
            </fileset>
            <report format="frames" todir="${junit.out.dir.html}"/>
        </junitreport>
    </target>

    <target name="test"
            depends="junit,junitreport"
            description="Runs unit tests and creates a report"
    />
...

Back to the src/HelloWorldTest.java. We create a class extending BuildFileTest with String-constructor (JUnit-standard), a setUp() method initializing Ant and for each testcase (targets use.*) a testXX() method invoking that target.

import org.apache.tools.ant.BuildFileTest;

public class HelloWorldTest extends BuildFileTest {

    public HelloWorldTest(String s) {
        super(s);
    }

    public void setUp() {
        // initialize Ant
        configureProject("build.xml");
    }

    public void testWithout() {
        executeTarget("use.without");
        assertEquals("Message was logged but should not.", getLog(), "");
    }

    public void testMessage() {
        // execute target 'use.nestedText' and expect a message
        // 'attribute-text' in the log
        expectLog("use.message", "attribute-text");
    }

    public void testFail() {
        // execute target 'use.fail' and expect a BuildException
        // with text 'Fail requested.'
        expectBuildException("use.fail", "Fail requested.");
    }

    public void testNestedText() {
        expectLog("use.nestedText", "nested-text");
    }

    public void testNestedElement() {
        executeTarget("use.nestedElement");
        assertLogContaining("Nested Element 1");
        assertLogContaining("Nested Element 2");
    }
}

When starting ant we'll get a short message to STDOUT and a nice HTML-report.

C:\tmp\anttests\MyFirstTask>ant
Buildfile: build.xml

compile:
    [mkdir] Created dir: C:\tmp\anttests\MyFirstTask\classes
    [javac] Compiling 2 source files to C:\tmp\anttests\MyFirstTask\classes

jar:
      [jar] Building jar: C:\tmp\anttests\MyFirstTask\MyTask.jar

junit:
    [mkdir] Created dir: C:\tmp\anttests\MyFirstTask\report\junit\xml
    [junit] Running HelloWorldTest
    [junit] Tests run: 5, Failures: 0, Errors: 0, Time elapsed: 2,334 sec



junitreport:
    [mkdir] Created dir: C:\tmp\anttests\MyFirstTask\report\junit\html
[junitreport] Using Xalan version: Xalan Java 2.4.1
[junitreport] Transform time: 661ms

test:

BUILD SUCCESSFUL
Total time: 7 seconds
C:\tmp\anttests\MyFirstTask>

Debugging

Try running Ant with the flag -verbose. For more information, try flag -debug.

For deeper issues, you may need to run the custom task code in a Java debugger. First, get the source for Ant and build it with debugging information.

Since Ant is a large project, it can be a little tricky to set the right breakpoints. Here are two important breakpoints for version 1.8:

尽可能使用-verbose参数运行ant。可以使用-debug参数获取更多信息。对于那些很深层的问题,你也许需要在java调试机上运行当前Task代码。首先,获取源代码,然后带有调试信息构建项目。由于ANT是一个大的工程,在设置断点问题上比较智能。1.8版本的ant有两个重要的断点:

  • Initial main() function: com.apache.tools.ant.launch.Launcher.main()
  • 初始化的main函数
  • Task entry point: com.apache.tools.ant.UnknownElement.execute()
  • 任务入口点

If you need to debug when a task attribute or the text is set, begin by debugging into method execute() of your custom task. Then set breakpoints in other methods. This will ensure the class byte-code has been loaded by the Java VM.

如果你需要在Task属性或者文本内容被设置时调试代码,你可以从当前Task的execute方法开始,然后在其他的方法中设置断点。这么做是为了确保java虚拟机已经载入了字节码。

Resources

This tutorial and its resources are available via BugZilla [6]. The ZIP provided there contains

  • this initial version of this tutorial
  • the buildfile (last version)
  • the source of the task (last version)
  • the source of the unit test (last version)
  • the ant-testutil.jar (nightly build of 2003-08-18)
  • generated classes
  • generated jar
  • generated reports

The last sources and the buildfile are also available here [7] inside the manual.

Used Links:

  [1] http://ant.apache.org/manual/properties.html#built-in-props

  [2] http://ant.apache.org/manual/Tasks/taskdef.html

  [3] http://ant.apache.org/manual/develop.html#set-magic

  [4] http://ant.apache.org/manual/develop.html#nested-elements

  [5] http://gump.covalent.net/jars/latest/ant/ant-testutil.jar

  [6] http://issues.apache.org/bugzilla/show_bug.cgi?id=22570

  [7] tutorial-writing-tasks-src.zip


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值