专题:Ant-1.7.x

专题:Ant-1.7.x

——本文是基于Ant-1.7.1版。

 

Ant是什么?

Apache Ant是一个基于Java的构建工具。据最初的创始人James Duncan Davidson介绍,这个工具的名称是another neat tool(另一个整洁的工具)的首字母缩写。随着应用程序的生成过程变得更加复杂,确保在每次生成期间都使用精确相同的生成步骤,同时实现尽可能多的自动化,以便及时产生一致的生成版本,这就变得更加重要了。C或C++中的传统项目经常使用make工具来做这件事情,其中生成任务是通过调用shell命令来执行的,而依赖关系定义在每个生成文件之间,以便它们总是以必需的顺序执行。Ant与make类似,它也定义生成文件之间的依赖关系;然而,与使用特定于平台的shell命令来实现生成过程所不同的是,它使用跨平台的Java类。使用Ant,您能够编写单个生成文件,这个生成文件在任何Java平台上都一致地操作(因为Ant本身也是使用Java语言来实现的);这就是Ant最大的优势。Ant的其他关键优势包括其突出的简单性和无缝地使用自定义功能来扩展它的能力。

 

Ant生成文件剖析

     Ant没有定义它自己的自定义语法;相反,它的生成文件是用XML编写的,每个生成文件(通常为build.xml,也可为任意xml文件)由单个project元素组成,该元素包含一个或多个target元素。一个目标(target)是生成过程中已定义的一个步骤,它执行任意数量的操作,比如编译一组源文件。这些操作本身是由其他专用任务标签执行的。然后这些任务将根据需要被分组到各个target元素中。一次生成过程所必需的所有操作可以放入单个target元素中,但是那样会降低灵活性。将那些操作划分为逻辑生成步骤,每个步骤包含在它自己的target元素中,这样通常更为可取。这样可以执行整体生成过程的单独部分,却不一定要执行其他部分。例如,通过仅调用某些目标,您可以编译项目的源代码,却不必创建可安装的项目映像。顶级project元素需要包含一个default属性,如果在Ant被调用时没有指定目标,这个属性将指定要执行的目标。然后需要使用target元素来定义该目标本身。下面是一个最基本的生成文件:

<?xml version="1.0"?>

<projectdefault="init">

    <targetname="init"/>

</project>

 

添加描述:

      通常用以下两种方式来使待构建的项目更具描述性,同时无需改变其功能。

1.   XML注释可以使用在整个生成文件中以提高清晰性。

2.   Ant自己定义的description元素和description属性,它们用于提供更结构化的注释。

例如:

<?xml version="1.0"?>

<projectdefault="init"name="ProjectArgon">

    <description>

A simple project introducing the use of descriptive tags in Ant buildfiles.

</description>

    <!-- XML comments can also be used -->

    <targetname="init"description="Initialize Argondatabase">

       <!-- perform initialization stepshere -->

    </target>

</project>

 

属性

     Ant中的属性类似编程语言中的变量,它们都具有名称和值。然而与通常的变量不同,一经设置,Ant中的属性就不可更改;如果尝试给某个现有属性赋予一个新的值,这不会被看作是一个错误,但是该属性仍然会保留其现有值。在Ant中设置属性,如下:

<propertyname="metal"value="beryllium"/> 

为了在生成文件的其他部分引用这个属性,使用语法:${metal} 

例如,为了使用这样一个值,它是另一个属性的值的组成部分,将标签写为下面这样:

<propertyname="metal-database" value="${metal}.db"/>

 

Ant中的预定义的属性

1.   Java环境设置用于运行Ant的所有系统属性,均可作为Ant属性使用,比如${user.home}。

2.   Ant定义的自己的一小组属性:

1)    ${ant.version},这个属性包含Ant的版本;

2)    ${basedir},这个属性是项目目录的绝对路径(由包含生成文件的目录所定义,或者由project元素的可选basedir属性所定义)。

3)    Ant的location属性。属性经常用于引用文件系统上的文件或目录,但是对于使用不同路径分隔符(例如,/与\)的平台来说,这样可能在跨越不同平台时导致问题。Ant的location属性专门设计用于以平台无关的方式包含文件系统路径。像下面这样使用location来代替value:

<property name="database-file"location="archive/databases/${metal}.db"/>

     用于location属性的路径分隔字符将被转换为当前平台的正确格式;而且由于文件名是相对的,它被认为是相对于项目的基目录。我们同样可以容易地写为下面这样:

<property name="database-file"location="archive\databases\${metal}.db"/>

这个标签的两个版本都会在不同的平台具有相同的行为。尽量使用相对路径名称而不是绝对路径名称,这样还会更加灵活。

 

定义依赖关系

     使用target元素的depends属性来实现的依赖关系。

     生成一个项目一般需要许多步骤,例如首先要编译源代码,然后将它打包为Java归档文件(JavaArchive File,JAR)。这其中的步骤都具有清楚定义的顺序,例如,在编译器从源代码生成类文件之前,您不能打包类文件。与顺序指定target所不同的是,Ant采用一种更灵活的方法来定义依赖关系,就像make和类似的生成工具所做的那样。每个目标的定义依据的是在它在能够执行之前必须完成的其他所有目标。例如:

<targetname="init"/>

<targetname="preprocess"depends="init"/>

<targetname="compile"depends="init,preprocess"/>

<targetname="package"depends="compile"/>

     这种方法允许您执行项目任何阶段的生成过程;Ant会首先执行已定义的先决阶段。在上面的例子中,如果让Ant完成compile步骤,它将判断出需要首先执行init和preprocess这两个目标。init目标不依赖其他任何目标,因此它将首先被执行。然后Ant检查preprocesstarget,发现它依赖init目标;由于已经执行了init,Ant不会再次执行它,因而开始执行preprocess目标。最后可以执行compile任务本身。注意目标出现在生成文件中的顺序并不重要:执行顺序是由depends属性唯一确定的。

 

运行Ant

1、从命令行运行Ant

     从命令提示符调用Ant可以简单得只需键入单独的ant命令。如果您这样做,Ant将使用默认的生成文件(build.xml);该生成文件中指定的默认目标就是Ant尝试要生成的目标。还可以指定许多命令行选项,后面跟着任意数量的生成目标,Ant将按顺序生成这其中的每个目标,并在此过程中解决所有依赖关系。

下面是从命令行执行的Ant生成任务的一些典型输出:

Buildfile:build.xml

init:

[mkdir]Createddir:E:\tutorials\ant\example\build

[mkdir]Createddir:E:\tutorials\ant\example\dist

compile:

[javac]Compiling8 source files to E:\tutorials\ant\example\build

dist:

[jar]Buildingjar:E:\tutorials\ant\example\dist\example.jar

BUILDSUCCESSFUL

Totaltime:2 seconds

 

命令行选项

     就像make工具默认情况下寻找一个名为makefile的生成文件一样,Ant寻找一个名为build.xml的文件。因此,如果您的生成文件使用这个名称,就不需要在命令行指定它。当然,有时使用具有其他名称的生成文件更方便,在那样的情况下,您需要对Ant使用-buildfile<file>参数(-f<file>是其简写形式)。

     另一个有用的选项是-D,它用于设置随后可以在生成文件中使用的属性。这对于配置您想要以某种方式开始的生成过程是非常有用的。例如,为了将name属性设置为某个特定的值,您会使用一个类似下面这样的选项:

-Dmetal=beryllium

这个功能可用于覆盖生成文件中的初始属性设置。正如前面指出过的,属性的值一经设置就不能改变。-D标志在读取生成文件中的任何信息之前设置某个属性;由于生成文件中的指派落在这个初始指派之后,因此它不会改变其值。

 

2、从Eclipse内运行Ant生成任务

     名为build.xml的文件在Eclipse的导航程序视图中使用一个Ant图标来标识和装饰。右键单击这些文件会提供一个RunAnt...菜单选项。

 

生成一个简单的JAVA项目

编译源代码 (<javac …/>)

     通过属性javac编译Java代码的任务的编写方式:

<javacsrcdir="src"destdir="build"debug="on"/>

     这个标签寻找src目录中以 .java为扩展名的所有文件,并对它们调用javac编译器,在build目录中生成类文件。如果没有指定destdir属性则在srcdir指定的目录中生成类文件。其他有用的属性包括:

classpath:等价于javac的-classpath选项。

debug="true":指示编译器应该带调试信息编译源文件。(debug的值可取true/false/on/off/yes/no)

 

javac任务的一个重要特点:

     javac仅编译那些它认为需要编译的源文件。如果某个类文件已经存在,并且对应的源文件自从该类文件生成以来还没有改变过,那么该源文件就不会被重新编译。javac任务的输出显示了实际被编译的源文件的数目。

编写一个clean目标来从目标目录移除生成的任何类文件是个很好的习惯。如果想要确保所有源文件都已编译,就可以使用这个任务。

 

编译器选项

     Ant的javac任务的默认行为是调用运行Ant本身的任何JVM的标准编译器。然而,可能想要单独地调用编译器。例如当希望指定编译器的某些内存选项,或者需要使用一种不同级别的编译器的时候。为实现这个目的,只需将javac的fork属性设置为true,比如:

<javacsrcdir="src"fork="true"/>

如果想要指定一个不同的javac可执行文件,并向它传递一个最大内存设置,您可以像下面这样做:

<javacsrcdir="src"fork="true"executable="d:\javase6\bin\javac"memorymaximumsize="128m"/>

     甚至可以将Ant配置为使用某种不同的编译器。受支持的编译器包括开放源代码的Jikes编译器和来自GNU编译器集(GNUCompiler Collection,GCC)的GCI编译器。可以通过两种方式指定这些编译器:1)可以设置build.compiler属性,这将应用于使用javac任务的所有场合;2)或根据需要设置每个javac任务中的compiler属性。

javac任务还支持其他许多选项。请参考Ant手册以了解更多细节(请参阅参考资料)。

 

创建JAR文件 (<jar…/>)

     在编译Java源文件之后,结果类文件通常被打包到一个JAR文件中,这个文件类似zip归档文件。每个JAR文件都包含一个清单文件,它可以指定该JAR文件的属性。下面是Ant中jar任务的一个简单使用例子:

<jardestfile="package.jar"basedir="classes"/>

这将创建一个名为package.jar的JAR文件,并把classes目录中的所有文件添加到其中(JAR文件能够包含任意类型的文件,而不只是类文件)。此处没有指定清单文件,因此Ant将提供一个基本的清单文件。manifest属性允许指定一个用作该JAR文件的清单的文件。清单文件的内容还可以使用manifest任务在生成文件中指定。这个任务能够像文件系统写入一个清单文件,或者能够实际嵌套在jar之内,以便一次性地创建清单文件和JAR文件。例如:

<jardestfile="package.jar"basedir="classes">

           <manifest>

              <attributename="Built-By"value="${user.name}"/>

              <attributename="Main-class"value="package.Main"/>

           </manifest>

    </jar>

 

创建javadoc文件 (<javadoc…/>)

     在编译Java源文件之后,结果类文件通常被打包到一个JAR文件中,这个文件类似zip归档文件。每个JAR文件都包含一个清单文件,它可以指定该JAR文件的属性。下面是Ant中jar任务的一个简单使用例子:

<javadocdestdir="${distDir}/docs/api"encoding="GBK"docencoding="GBK">

    <packagesetdir="${srcDir}">

        <includename="**"/>

    </packageset>

</javadoc>

 

时间戳生成 (<tstamp/>)

     在生成环境中使用当前时间和日期,以某种方式标记某个生成任务的输出,以便记录它是何时生成的,这经常是可取的。这可能涉及编辑一个文件,以便插入一个字符串来指定日期和时间,或将这个信息合并到JAR或zip文件的文件名中。

这种需要是通过简单但是非常有用的tstamp任务来解决的。这个任务通常在某次生成过程开始时调用,比如在一个init目标中。这个任务不需要属性,许多情况下只需<tstamp/>就足够了。tstamp不产生任何输出;相反,它根据当前系统时间和日期设置Ant属性。下面是tstamp设置的一些属性、对每个属性的说明,以及这些属性可被设置到的值的例子:

属性

说明

例子

DSTAMP

设置为当前日期,默认格式为yyyymmdd

20100307

TSTAMP

设置为当前时间,默认格式为hhmm

1603

TODAY

设置为当前日期,带完整的月份

2010年12月17日

例如,按如下方式创建了一个JAR文件:

<jardestfile="package.jar" basedir="classes"/>

在调用tstamp任务之后,我们能够根据日期命名该JAR文件,如下所示:

<jardestfile="package-${DSTAMP}.jar" basedir="classes"/>

因此,如果这个任务在2010年03月07日调用,该JAR文件将被命名为

package-20100307.jar。    

示例:

<targetname="package">

    <tstamp/>

    <jardestfile="package-${DSTAMP}.jar"basedir="D:\workspace\ejb3\ejb01\src"/>

</target>

     还可以配置tstamp任务来设置不同的属性,应用一个当前时间之前或之后的时间偏移,或以不同的方式格式化该字符串。所有这些都是使用一个嵌套的format元素来完成的,如下所示:

<tstamp>

    <formatproperty="OFFSET_TIME"pattern="HH:mm:ss"offset="10"unit="minute"/>

</tstamp>

     上面的清单将OFFSET_TIME属性设置为距离当前时间10分钟之后的小时数、分钟数和秒数。用于定义格式字符串的字符与java.text.SimpleDateFormat类所定义的那些格式字符相同。

综合示例

生成简单 Java 项目,ant将编译 src 目录下的所有源代码,将结果类文件放在 build 目录下,然后把所有类文件打包到 dist 目录中的一个 JAR 文件中。要自己试验这个生成文件,您所需要的就是包含一个或多个 Java 源代码文件的 src 目录,这个目录可以包含从简单的“Hello World”程序到来自某个现有项目的大量源文件的任何内容。如果需要向 Java classpath 添加 JAR 文件或其他任何内容,以便成功地编译源代码,只需在 javac 任务中为其添加一个 classpath 属性。

// build.xml

<?xml version="1.0"?>

<projectdefault="dist"name="HelloWorld"basedir=".">

    <description>ASimple Java Project</description>

    <propertyenvironment="evn"/>

    <propertyname="srcDir"location="src"/>

    <propertyname="buildDir"location="build"/>

    <propertyname="distDir"location="dist"/>

    <targetname="init"depends="clean">

       <tstamp/>

       <mkdirdir="${buildDir}"/>

       <mkdirdir="${distDir}"/>

    </target>

    <targetname="compile"depends="init"description="编译">

       <javacsrcdir="${srcDir}"destdir="${buildDir}"/>

    </target>

    <targetname="dist"depends="compile"description="打包">

       <jardestfile="${distDir}/package-${DSTAMP}.jar"basedir="${buildDir}">

           <manifest>

              <attributename="Built-By"value="${user.name}"/>

              <attributename="Main-Class"value="package.Main"/>

           </manifest>

       </jar>

       <jardestfile="${distDir}/package-src-${DSTAMP}.jar"basedir="${srcDir}"/>

    </target>

    <targetname="clean">

       <deletedir="${buildDir}"/>

       <deletedir="${distDir}"/>

    </target>

</project>

 

     下面是使用该文件执行的某次生成过程的示例输出(得到的输出可能不一样,具体取决于src目录的内容):

Buildfile: D:\workspace\ejb3\HelloWorld\buildfile.xml

clean:

   [delete] Deleting directoryD:\workspace\ejb3\HelloWorld\build

   [delete] Deleting directoryD:\workspace\ejb3\HelloWorld\dist

init:

    [mkdir] Created dir:D:\workspace\ejb3\HelloWorld\build

    [mkdir] Created dir:D:\workspace\ejb3\HelloWorld\dist

compile:

    [javac] Compiling 2 source files toD:\workspace\ejb3\HelloWorld\build

dist:

      [jar] Building jar: D:\workspace\ejb3\HelloWorld\dist\package-20100422.jar

      [jar] Building jar:D:\workspace\ejb3\HelloWorld\dist\package-src-20100422.jar

BUILD SUCCESSFUL

Total time: 1 second

 

文件系统操作

创建目录 (<mkdir…/>)

     最基本的文件系统操作之一就是创建目录或文件夹。做这项工作的任务名为mkdir,毫不奇怪,它非常类似于具有相同名称的Windows和UNIX/Linux命令。

<mkdirdir="archive/metals/zinc"/>

     mkdir任务的另一个有用特性是它的如下能力:在父目录还不存在时创建它们。考虑一下上面的清单,设想archive目录存在,但是metals目录不存在。如果使用底层平台的mkdir命令,您需要首先显式地创建metals目录,然后第二次调用mkdir命令来创建zinc目录。但是Ant任务比这更加智能,它能够一次性创建这两个目录。类似地,如果目标目录已经存在,mkdir任务不会发出错误消息,而只是假设它的工作已经完成,从而什么也不做。

 

删除目录 (<delete…/>)

<deletedir="archive/metals/zinc"/>

这将删除指定的目录连同它包含的所有文件以及子目录。使用file属性而不是dir属性可以指定要删除的单个文件。

 

复制和移动文件及目录 (<copy…/>, <move…/>)

     在Ant中制作文件的一份拷贝很简单。例如:

<copyfile="src/Test.java"tofile="src/TestCopy.java"/>

     还可以使用move来执行重命名操作而不是拷贝文件:

<movefile="src/Test.java"tofile="src/TestCopy.java"/>

     另一个常用的文件系统操作是将文件复制或移动到另一个目录。做这项工作的Ant语法同样也很简单:

<copyfile="src/Test.java"todir="archive"/>

<movefile="src/Test.java"todir="archive"/>

     默认情况下,Ant仅输出它执行的移动和复制操作的摘要,包括诸如已移动或复制的文件的数量等信息。如果想看到更详细的信息,包括涉及的文件名称等,您可以将verbose属性设置为true。

 

创建和解压缩zip及tar文件 (<zip…/>, <gzip…/>)

在前一节中,我们看到了如何创建JAR文件。创建其他归档文件的过程几乎完全相同。下面是创建zip文件的Ant任务:

<zipdestfile="output.zip"basedir="output"/>

相同的语法也可用于创建tar文件。还可以使用GZip和BZip任务来压缩文件。例如:

<gzipsrc="output.tar"zipfile="output.tar.gz"/>

解压缩和提取文件 (<unzip…/>)

<unzipsrc="output.zip"dest="extractDir"/>

还可以包括overwrite属性来控制覆盖行为。默认设置是覆盖与正在被提取的归档文件中的条目相匹配的所有现有文件。相关的任务名称是untar、unjar、gunzip和bunzip2。

<unzipsrc="output.zip"dest="extractDir"/>

<unjarsrc="output.jar"dest="extractJar"/>

<untarsrc="output.tar"dest="extractJar"/>

 

替换文件中的标记 (<replace…/>)

     文件系统操作是replace任务,它执行文件中的查找和替换操作。token属性指定要查找的字符串,value属性指定一个新的字符串,查找到的标记字符串的所有实例都被替换为这个新的字符串。例如:

<replacefile="input.txt"token="old-string"value="new-string"/>

     替换操作将在文件本身之内的适当位置进行。为了提供更详细的输出,可把summary属性设置为true。这将导致该任务输出找到和替换的标记字符串实例的数目。

 

其它有用的任务和技术

模式匹配

     使用通配符字符:”*”,它匹配零个或多个字符;以及”?”,它仅匹配一个字符。

     注意:模式匹配只能在文件集中使用,如在fileset,zipfileset中使用,而像copy,zip等属性中不能使用。

u  对文件执行模式匹配。匹配给定目录中以.java结尾的所有文件的模式是:”*.java”。

u  对目录执行模式匹配。匹配带src前缀的任何目录中的所有Java文件的模式是:“src*/*.java”。

u  匹配任意数量的目录的模式是:”**”,它匹配任意数量的目录。例如,模式”**/*.java”将匹配当前目录结构下的所有Java文件。

     以相当一致的方式对文件系统任务使用模式,比如嵌套的fileset元素。先前,我们使用这个任务来复制单个文件:

<copyfile="src/Test.java"todir="archive"/>

     如果我们想要使用一个模式,可以将file属性替换为一个fileset元素,如下所示:

<copytodir="archive">

    <filesetdir="src">

       <includename="**/*.java"/>

    </fileset>

</copy>

fileset默认情况下包含指定src目录下的所有文件,因此为了仅选择Java文件,我们对模式使用一个include元素。类似地,我们可以对另一个模式添加一个exclude元素,从而潜在地排除include指定的匹配项。甚至可以指定多个include和exclude元素;这样将得到一组文件和目录,它们包含include模式的所有匹配项的并集,但排除了exclude模式的所有匹配项。注意还有一个通常很有用的文件集特性,但是对于没有意识到它的人来说,这个特性偶尔会产生混淆。这个特性称为默认排除:即自动从文件集内容中排除的内置模式列表。该列表包括与名为CVS的目录相匹配的条目,以及以~字符结尾的文件,它们可能是备份文件。您通常不想在文件系统操作中包括这类文件和目录,因此排除这些文件是默认行为。然而,如果确实想无例外地选择所有文件和目录,可以将文件集的defaultexcludes属性设置为no。

 

使用选择器

     文件集用于指定一组文件,并且这个组的内容可以使用include和exclude模式来指定。也可以结合称为选择器的特殊元素使用include和exclude来选择文件。下面是Ant可用的核心选择器的列表:

size:这个选择器用于根据文件的字节大小选择文件(除非使用units属性来指定了不同的单位)。when属性用于设置比较的性质(less、more或者equal),value属性定义每个文件将与之作比较的目标大小。

contains:只有包含给定文本字符串(由text属性指定)的文件才匹配这个选择器。默认情况下,查找操作是大小写敏感的;添加casesensitive="no"可以改变默认设置。

filename:name属性指定文件名要与之匹配的模式。它本质上与include元素相同,以及与指定了negate="yes"时的exclude元素相同。

present:从当前目录结构中选择如下文件:它们与指定的targetdir目录中的文件具有相同的名称和相对目录结构。

depend:这个选择器与present选择器具有相同的效果,只不过匹配的文件被限制到相对于targetdir位置中的对应文件来说,最近已修改过的那些文件。

date:这个选择器基于其最后修改日期选择文件。when属性指定作比较的性质是before、after还是equal,datetime属性指定与之作比较的日期和时间,这个日期和时间具有给定的固定格式MM/DD/YYYYHH:MM AM_or_PM。注意Windows平台上有一个内置的2秒偏移,以允许底层文件系统的不精确性,这可能导致匹配的文件数量超过预期。允许的回旋时间量可以使用granularity属性来更改(以毫秒为单位来指定)。

depth:这个选择器检查每个文件的目录结构层次数目。min和/或max属性用于选择具有想要的目录层次数目的的文件。

     还可以通过在一个选择器容器内嵌套一个或多个选择器来组合选择器。最常用的选择器容器and仅选择它包含的所有选择器都选择了的文件。其他选择其容器包括or、not、none和majority。

下面是一个文件集的例子,它仅选择那些大于512字节并且包含字符串“hello”的文件。

<copytodir="dist">

    <filesetdir="src">

       <and>

           <containstext="hello"/>

           <sizevalue="512"when="more"/>

       </and>

    </fileset>

</copy>

 

将生成文件链接起来

     有两种生成大型项目的不同方法。一种是让一个单一的生成文件做所有事情;另一种是让高级别的生成文件调用其它生成文件以执行特定任务,从而将生成过程划分为许多较小的部分。在简单的情况下,您可以使用antfile属性,仅指定那些要使用的生成文件,Ant将生成该生成文件中的默认目标。例如:

<antantfile="sub-build.xml"/>

     在父生成文件中定义的任何属性默认将传递给子生成文件,虽然这可以通过指定inheritAll="false"来避免。通过使用property元素来传入显式的属性也是可以做到的,即使将inheritAll设置为false,这些属性也仍然适用于子生成文件。这个功能很适合用于给子生成文件传入参数。

 

     让我们来考虑一个例子。下面是我们想要调用的一个生成文件:

// sub.xml

<?xml version="1.0"?>

<projectdefault="showMessage">

    <targetname="showMessage">

       <echomessage="Message=${message}"/>

    </target>

</project>

注:echo任务,它简单地输出给定的消息。

下面是调用第一个生成文件的第二生成文件,它还给第一个生成文件传入message属性:

// build.xml

<?xml version="1.0"?>

<projectdefault="callSub">

    <targetname="callSub">

       <antantfile="sub.xml"target="showMessage"inheritAll="false">

           <propertyname="message"value="Hello fromparent build"/>

       </ant>

    </target>

</project>

     运行第二个生成文件所得到的输出如下:

Buildfile: D:\workspace\ejb3\HelloWorld\build4.xml

callSub:

showMessage:

     [echo] Message=Hello from parent build

BUILD SUCCESSFUL

Total time: 328 milliseconds

 

使用CVS知识库

     CVS是concurrent versionssystem(并发版本控制系统)的缩写。它是一个源代码控制系统,设计用于跟踪许多不同开发人员做出的更改。注意,为了利用Ant中的cvs任务,您需要在机器上安装cvs命令,并使其从命令行可用。

下面是从CVS知识库提取模块的一个例子生成文件:

<?xml version="1.0"?>

<projectname="CVSExtract"default="extract"basedir=".">

    <propertyname="cvsRoot"value=":pserver:[email protected]:/home/eclipse"/>

    <targetname="extract">

       <cvscvsRoot="${cvsRoot}"package="org.eclipse.swt.examples"dest="${basedir}"/>

    </target>

</project>

cvs任务的主要属性是cvsRoot,它是对CVS知识库的完整引用,包括连接方法和用户详细信息。这个参数的格式如下:

[:method:][[user][:password]@]hostname[:[port]]/path/to/repository

在上面的例子中,我们作为匿名用户连接到Eclipse项目的中央知识库。然后其他属性指定了我们希望提取的模块以及放置提取文件的目的地。提取是CVS任务的默认操作;其他操作可通过使用command属性来指定。请参阅参考资料以了解关于CVS的更多信息。

 

使用自定义任务来扩展Ant

创建自定义的任务

     为实现一个简单的自定义任务,我们所需做的就是扩展org.apache.tools.ant.Task类,并重写execute()方法。因此,作为这个文件排序自定义任务的框架,我们将编写如下代码:

importorg.apache.tools.ant.BuildException;

importorg.apache.tools.ant.Task;

publicclass FileSorter extends Task {

//Themethod executing the task

   public void execute()throws BuildException{}

}

注意我们声明execute()方法抛出一个BuildException异常。如果这个任务出了任何错误,我们将抛出这个异常以便向Ant指出故障。大多数任务,不管是核心任务还是自定义任务,都利用属性来控制它们的行为。对于这个简单任务,我们需要一个属性来指定要排序的文件,需要另一个属性来指定排序内容的输出。我们把这两个属性分别叫做file和tofile。Ant使得支持自定义任务中的属性非常容易。为此,我们只需实现一个具有特别格式化的名称的方法,Ant能够使用生成文件中指定的对应属性的值来调用这个方法。这个方法的名称需要是set加上属性的名称,因此在这个例子中,我们需要名为setFile()和setTofile()的方法。当Ant遇到生成文件中的一个属性设置时,它会寻找相关任务中具有适当名称的方法(称为setter方法)。生成文件中的属性是作为字符串来指定的,因此我们的setter方法的参数可以是一个字符串。在这样的情况下,Ant将在展开值所引用的任何属性之后,使用该属性的字符串值来调用我们的方法。但有时我们想把属性的值看作是一种不同的类型。这里的示例任务就是这种情况,其中的属性值引用文件系统上的文件,而不只是引用任意的字符串。可以通过将方法参数声明为java.io.File类型来容易地做到这点。Ant将接受属性的字符串值,并把它解释为一个文件,然后传递给我们的方法。如果文件是使用相对路径名称来指定的,则会被转换为相对于项目基目录的绝对路径。Ant能够对其他类型执行类似的转换,比如boolean和int类型。如果您提供具有相同名称但是具有不同参数的两个方法,Ant将使用更明确的那一个方法,因此文件类型将优先于字符串类型。这个自定义任务需要的两个setter方法类似如下:

//Thesetter for the"file"attribute

publicvoid setFile(File file){}

//Thesetter for the"tofile"attribute

publicvoid setTofile(File tofile){}

 

实现自定义的任务

使用前一小节开发的框架,现在我们能够完成这个简单的文件排序任务的实现:

import java.io.*;

import java.util.*;

import org.apache.tools.ant.BuildException;

import org.apache.tools.ant.Task;

/**

 *Asimpleexampletasktosortafile

 */

publicclass FileSorterextends Task {

    private Filefile,tofile;

    // The method executing the task

    publicvoid execute()throws BuildException {

       System.out.println("Sortingfile=" + file);

       try {

           BufferedReader from =new BufferedReader(new FileReader(file));

           BufferedWriter to =new BufferedWriter(new FileWriter(tofile));

           List allLines =newArrayList();

           // read in the input file

           String line =from.readLine();

           while (line !=null) {

              allLines.add(line);

              line = from.readLine();

           }

           from.close();

           // sort the list

           Collections.sort(allLines);

           // write out the sorted list

       for (ListIterator i =allLines.listIterator(); i.hasNext();) {

              String s = (String)i.next();

              to.write(s);

              to.newLine();

           }

           to.close();

       } catch (FileNotFoundException e) {

           thrownew BuildException(e);

       } catch (IOException e) {

           thrownew BuildException(e);

       }

    }

    // The setter forthe"file"attribute

    publicvoid setFile(File file) {

       this.file = file;

    }

    // The setter for the"tofile"attribute

    publicvoid setTofile(Filetofile) {

       this.tofile = tofile;

    }

}

 

     两个setter方法简单地对属性的值排序,以便这些值能够在execute()方法中使用。这里,输入文件被逐行地读入一个列表中,然后被排序并逐行地输出到输出文件。注意,为简单起见,我们很少执行错误检查——例如,我们甚至没有检查生成文件是否设置了必需的属性。不过我们的确至少捕捉了所执行的操作抛出的I/O异常,并将这些异常作为BuildExceptions重新抛出。现在可以使用javac编译器或从某个IDE内编译这个自定义的任务。为了解决所使用的Ant类的引用问题,您需要把ant.jar文件的位置添加到classpath中。这个文件应该在Ant安装路径下的lib目录。

 

使用自定义的任务

     现在我们已经开发和编译了这个自定义的任务,下面可以从生成文件中利用它了。在能够调用自定义的任务之前,我们需要给它指定一个名称来定义它,并告诉Ant关于实现这个任务的类文件的信息,以及定位该类文件所必需的任何classpath设置。这是使用taskdef任务来完成的,如下所示:

<taskdefname="filesorter"classname="FileSorter"classpath="bin"/>

     大功告成!现在可以像使用Ant的核心任务一样使用这个自定义的任务了。下面是一个完整的生成文件,它显示了这个自定义任务的定义和用法:

<?xml version="1.0"?>

<projectname="CustomTaskExample"default="main"basedir=".">

    <taskdefname="filesorter"classname="FileSorter"classpath="bin"/>

    <targetname="main">

       <filesorterfile="input.txt"tofile="output.txt"/>

    </target>

</project>

 

现在在当前工作目录中创建一个input.txt文件来测试这个自定义的任务。例如:

//input.txt

Hellothere

Thisis a line

Andhere is another one

下面是运行上面的生成文件之后产生的控制台输出:

Buildfile: D:\workspace\ejb3\HelloWorld\build6.xml

main:

[filesorter] Sortingfile=D:\workspace\ejb3\HelloWorld\input.txt

BUILD SUCCESSFUL

Total time: 360 milliseconds

 

     注意input.txt的相对路径名称被转换成了当前目录中的一个绝对路径名称。这是因为我们将setter方法的参数指定为java.io.File类型而不是java.lang.String类型。

     您可以尝试指定一个不存在的输入文件,以确定该任务是如何向Ant报告“filenot found”异常的。

参考资料

1.   访问Apache Ant主页。这是一个极好的信息源,它包括下载、Ant手册以及指向其他参考资料的链接。

2.   查找关于Ant的IDE和编辑器集成的更多信息。

3.   developerWorks开放源代码项目专区包含丰富的基于Eclipse的内容。

4.   如果您是XML新手,请访问developerWorksXML专区。

5.   developerWorks特别贡献了许多关于Ant的文章:

      (1)“Incrementaldevelopment with Ant and JUnit”,Malcolm Davis撰稿,(2000年11月)。

      (2)“ExtendingAnt to support interactive builds”,Anthony Young-Garner撰稿,(2001年11月)。

      (3)“EnhanceAnt with XSL transformations”,Jim Creasman撰稿(2003年9月)。

6.   在developerWorks Java技术专区可以找到关于Java编程的方方面面的数百篇文章。

7.    参阅Java技术专区教程主页,从developerWorks获得免费的Java相关教程的完整列表。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章