UML软件工程组织

Ant实践
eric
目录
1. Ant是什么?
2. 安装Ant
3. 运行Ant
4. 编写build.xml
5. 内置task(internet)
6. EAR task(internet)
7. WAR task(internet)
8. JUnit task(internet)


1. Ant是什么?

Ant是一种基于Java的build工具。理论上来说,它有些类似于(Unix)C中的make ,但没有make的缺陷。

既然我们已经有了make, gnumake, nmake, jam以及其他的build工具为什么还要要一种新的build工具呢?因为Ant的原作者在多种(硬件)平台上开发软件时,无法忍受这些工具的限制和不便。类似于make的工具本质上是基于shell(语言)的:他们计算依赖关系,然后执行命令(这些命令与你在命令行敲的命令没太大区别)。这就意味着你可以很容易地通过使用OS特有的或编写新的(命令)程序扩展该工具;然而,这也意味着你将自己限制在了特定的OS,或特定的OS类型上,如Unix。

Makefile也很可恶。任何使用过他们的人都碰到过可恶的tab问题。Ant的原作者经常这样问自己:“是否我的命令不执行只是因为在我的tab前有一个空格?!!”。类似于jam的工具很好地处理了这类问题,但是(用户)必须记住和使用一种新的格式。

Ant就不同了。与基于shell命令的扩展模式不同,Ant用Java的类来扩展。(用户)不必编写shell命令,配置文件是基于XML的,通过调用target树,就可执行各种task。每个task由实现了一个实现了特定Task接口的对象来运行。(如果你对Ant一点概念都没有的话,可能看不懂这一节,没有关系,后面会对target,task做详细的介绍。你如果没有太多的时间甚至可以略过这一节,然后再回来浏览一下这里的介绍,那时你就会看懂了。同样,如果你对make之类的工具不熟悉也没关系,下面的介绍根本不会用到make中的概念。)

必须承认,这样做,在构造shell命令时会失去一些特有的表达能力。如`find . -name foo -exec rm {}`,但却给了你跨平台的能力-你可以在任何地方工作。如果你真的需要执行一些shell命令,Ant有一个<exec> task,这个task允许执行特定OS上的命令。

返回
2. 安装Ant
由于Ant是一个Open Source的软件,所以有两种安装Ant的方式,一种是用已编译好的binary 文件安装Ant,另一种是用源代码自己build Ant。

binary 形式的Ant可以从http://jakarta.apache.org/builds/ant/release/v1.4.1/bin下载。如果你希望你能自己编译Ant,则可从 http://jakarta.apache.org/builds/ant/release/v1.4.1/src。注意所列出的连接都是最新发行版的Ant。如果你读到此文时,发现已经有了更新的版本,那么请用新版本。如果你是一个疯狂的技术追求者,你也可以从Ant CVS repository下载最新版本的Ant。

系统需求

要想自己build Ant。你需要一个JAXP兼容的XML解析器(parser)放在你的CLASSPATH系统变量中。

binary 形式的Ant包括最新版的Apache Crimson XML解析器。你可以从http://java.sun.com/xml/ 得到更多的关于JAXP的信息。如果你希望使用其他的JAXP兼容的解析器。你要从Ant的lib目录中删掉jaxp.jar以及crimson.jar。然后你可将你心爱的解析器的jar文件放到Ant的lib目录中或放在你的CLASSPATH系统变量中。

对于当前版本的Ant,需要你的系统中有JDK,1.1版或更高。未来的Ant版本会要求使用JDK 1.2或更高版本。

安装Ant

binary 版的Ant包括三个目录:bin, docs 和lib。只有bin和lib目录是运行Ant所需的。要想安装Ant,选择一个目录并将发行版的文件拷贝到该目录下。这个目录被称作ANT_HOME。

在你运行Ant之前需要做一些配置工作。
  • 将bin目录加入PATH环境变量。
  • 设定ANT_HOME环境变量,指向你安装Ant的目录。在一些OS上,Ant的脚本可以猜测ANT_HOME(Unix和Windos NT/2000)-但最好不要依赖这一特性。
  • 可选地,设定JAVA_HOME环境变量(参考下面的高级小节),该变量应该指向你安装JDK的目录。
注意:不要将Ant的ant.jar文件放到JDK/JRE的lib/ext目录下。Ant是个应用程序,而lib/ext目录是为JDK扩展使用的(如JCE,JSSE扩展)。而且通过扩展装入的类会有安全方面的限制。

可选Task

Ant支持一些可选task。一个可选task一般需要额外的库才能工作。可选task与Ant的内置task分开,单独打包。这个可选包可以从你下载Ant的同一个地方下载。目前包含可选task的jar文件名叫jakarta-ant-1.4.1-optional.jar。这个jar文件应该放到Ant安装目录的lib目录下。

每个可选task所需的外部库可参看依赖库小节。这些外部库可以放到Ant的lib目录下,这样Ant就能自动装入,或者将其放入环境变量中。

Windows

假定Ant安装在c:\ant\目录下。下面是设定环境的命令:
set ANT_HOME=c:\ant
set JAVA_HOME=c:\jdk1.2.2
set PATH=%PATH%;%ANT_HOME%\bin
Unix (bash)

假定Ant安装在/usr/local/ant目录下。下面是设定环境的命令:
export ANT_HOME=/usr/local/ant
export JAVA_HOME=/usr/local/jdk-1.2.2
export PATH=${PATH}:${ANT_HOME}/bin
高级

要想运行Ant必须使用很多的变量。你至少参考需要下面的内容:
  • Ant的CLASSPATH必须包含ant.jar以及你所选的JAXP兼容的XML解析器的jar文件。
  • 当你需要JDK的功能(如javac或rmic task)时,对于JDK 1.1,JDK的classes.zip文件必须放入CLASSPATH中;对于JDK 1.2或JDK 1.3,则必须加入tools.jar。如果设定了正确的JAVA_HOME环境变量,Ant所带的脚本,在bin目录下,会自动加入所需的JDK类。
  • 当你执行特定平台的程序(如exec task或cvs task)时,必须设定ant.home属性指向Ant的安装目录。同样,Ant所带的脚本利用ANT_HOME环境变量自动设置该属性。
Building Ant

要想从源代码build Ant,你要先安装Ant源代码发行版或从CVS中checkout jakarta-ant模块。

安装好源代码后,进入安装目录。

设定JAVA_HOME环境变量指向JDK的安装目录。要想知道怎么做请参看安装Ant小节。

确保你已下载了任何辅助jar文件,以便build你所感兴趣的task。这些jar文件可以放在CLASSPATH中,也可以放在lib/optional目录下。参看依赖库小节可知不同的task需要那些jar文件。注意这些jar文件只是用作build Ant之用。要想运行Ant,你还要像安装Ant小节中所做的那样设定这些jar文件。

现在你可以build Ant了:
build -Ddist.dir=<directory_to_contain_Ant_distribution> dist (Windows)
build.sh -Ddist.dir=<directory_to_contain_Ant_distribution> dist (Unix)
这样就可你指定的目录中创建一个binary版本。

上面的命令执行下面的动作:
  • 如果有必要可以bootstrap Ant的代码。bootstrap 包括手工编辑一些Ant代码以便运行Ant。bootstrap 用于下面的build步骤。
  • 向build脚本传递参数以调用bootstrap Ant。参数定义了Ant的属性值并指定了Ant自己的build.xml文件的"dist" target。
大多数情况下,你不必直接bootstrap Ant,因为build脚本为你完成这一切。运行bootstrap.bat (Windows) 或 bootstrap.sh (UNIX) 可以build一个新的bootstrap版Ant。

如果你希望将Ant安装到ANT_HOME目录下,你可以使用:
build install (Windows)
build.sh install (Unix)
如果你希望跳过冗长的Javadoc步骤,可以用:
build install-lite (Windows)
build.sh install-lite (Unix)
这样就只会安装bin和lib目录。

注意install和install-lite都会覆盖ANT_HOME中的当前Ant版本。

依赖库

如果你需要执行特定的task,你需要将对应的库放入CLASSPATH或放到Ant安装目录的lib目录下。注意使用mapper时只需要一个regexp库。同时,你也要安装Ant的可选jar包,它包含了task的定义。参考上面的安装Ant小节。

Jar Name Needed For Available At
An XSL transformer like Xalan or XSL:P style task http://xml.apache.org/xalan-j/index.html or http://www.clc-marketing.com/xslp/
jakarta-regexp-1.2.jar regexp type with mappers jakarta.apache.org/regexp/
jakarta-oro-2.0.1.jar regexp type with mappers and the perforce tasks jakarta.apache.org/oro/
junit.jar junit tasks www.junit.org
stylebook.jar stylebook task CVS repository of xml.apache.org
testlet.jar test task java.apache.org/framework
antlr.jar antlr task www.antlr.org
bsf.jar script task oss.software.ibm.com/developerworks/projects/bsf
netrexx.jar netrexx task www2.hursley.ibm.com/netrexx
rhino.jar javascript with script task www.mozilla.org
jpython.jar python with script task www.jpython.org
netcomponents.jar ftp and telnet tasks www.savarese.org/oro/downloads

返回
3. 运行Ant
运行Ant非常简单,当你正确地安装Ant后,只要输入ant就可以了。

没有指定任何参数时,Ant会在当前目录下查询build.xml文件。如果找到了就用该文件作为buildfile。如果你用 -find 选项。Ant就会在上级目录中寻找buildfile,直至到达文件系统的根。要想让Ant使用其他的buildfile,可以用参数 -buildfile file,这里file指定了你想使用的buildfile。

你也可以设定一些属性,以覆盖buildfile中指定的属性值(参看property task)。可以用 -Dproperty=value 选项,这里property是指属性的名称,而value则是指属性的值。也可以用这种办法来指定一些环境变量的值。你也可以用property task来存取环境变量。只要将 -DMYVAR=%MYVAR% (Windows)-DMYVAR=$MYVAR (Unix) 传递给Ant -你就可以在你的buildfile中用${MYVAR}来存取这些环境变量。

还有两个选项 -quite,告诉Ant运行时只输出少量的必要信息。而 -verbose,告诉Ant运行时要输出更多的信息。

可以指定执行一个或多个target。当省略target时,Ant使用标签<project>的default属性所指定的target。

如果有的话,-projecthelp 选项输出项目的描述信息和项目target的列表。先列出那些有描述的,然后是没有描述的target。

命令行选项总结:
ant [options] [target [target2 [target3] ...]]
Options:
-help print this message
-projecthelp print project help information
-version print the version information and exit
-quiet be extra quiet
-verbose be extra verbose
-debug print debugging information
-emacs produce logging information without adornments
-logfile file use given file for log output
-logger classname the class that is to perform logging
-listener classname add an instance of class as a project listener
-buildfile file use specified buildfile
-find file search for buildfile towards the root of the filesystem and use the first one found
-Dproperty=value set property to value
例子
ant
使用当前目录下的build.xml运行Ant,执行缺省的target。
ant -buildfile test.xml
使用当前目录下的test.xml运行Ant,执行缺省的target。
ant -buildfile test.xml dist
使用当前目录下的test.xml运行Ant,执行一个叫做dist的target。
ant -buildfile test.xml -Dbuild=build/classes dist
使用当前目录下的test.xml运行Ant,执行一个叫做dist的target,并设定build属性的值为build/classes。

文件


在Unix上,Ant的执行脚本在做任何事之前都会source(读并计算值)~/.antrc 文件;在Windows上,Ant的批处理文件会在开始时调用%HOME%\antrc_pre.bat,在结束时调用%HOME%\antrc_post.bat。你可以用这些文件配置或取消一些只有在运行Ant时才需要的环境变量。看下面的例子。

环境变量

包裹脚本(wrapper scripts)使用下面的环境变量(如果有的话):
  • JAVACMD Java可执行文件的绝对路径。用这个值可以指定一个不同于JAVA_HOME/bin/java(.exe)的JVM。
  • ANT_OPTS 传递给JVM的命令行变量-例如,你可以定义属性或设定Java堆的最大值
手工运行Ant

如果你自己动手安装(DIY)Ant,你可以用下面的命令启动Ant:
java -Dant.home=c:\ant org.apache.tools.ant.Main [options] [target]
这个命令与前面的ant命令一样。选项和target也和用ant命令时一样。这个例子假定你的CLASSPATH包含:
  • ant.jar
  • jars/classes for your XML parser
  • the JDK's required jar/zip files
返回
4. 编写build.xml
Ant的buildfile是用XML写的。每个buildfile含有一个project。

buildfile中每个task元素可以有一个id属性,可以用这个id值引用指定的任务。这个值必须是唯一的。(详情请参考下面的Task小节)

Projects

project有下面的属性:

Attribute Description Required
name 项目名称. No
default 当没有指定target时使用的缺省target Yes
basedir 用于计算所有其他路径的基路径。该属性可以被basedir property覆盖。当覆盖时,该属性被忽略。如果属性和basedir property都没有设定,就使用buildfile文件的父目录。 No
项目的描述以一个顶级的<description>元素的形式出现(参看description小节)。

一个项目可以定义一个或多个target。一个target是一系列你想要执行的。执行Ant时,你可以选择执行那个target。当没有给定target时,使用project的default属性所确定的target。

Targets

一个target可以依赖于其他的target。例如,你可能会有一个target用于编译程序,一个target用于生成可执行文件。你在生成可执行文件之前必须先编译通过,所以生成可执行文件的target依赖于编译target。Ant会处理这种依赖关系。

然而,应当注意到,Ant的depends属性只指定了target应该被执行的顺序-如果被依赖的target无法运行,这种depends对于指定了依赖关系的target就没有影响。

Ant会依照depends属性中target出现的顺序(从左到右)依次执行每个target。然而,要记住的是只要某个target依赖于一个target,后者就会被先执行。
<target name="A"/>
<target name="B" depends="A"/>
<target name="C" depends="B"/>
<target name="D" depends="C,B,A"/>
假定我们要执行target D。从它的依赖属性来看,你可能认为先执行C,然后B,最后A被执行。错了,C依赖于B,B依赖于A,所以先执行A,然后B,然后C,最后D被执行。

一个target只能被执行一次,即时有多个target依赖于它(看上面的例子)。

如果(或如果不)某些属性被设定,才执行某个target。这样,允许根据系统的状态(java version, OS, 命令行属性定义等等)来更好地控制build的过程。要想让一个target这样做,你就应该在target元素中,加入if(或unless)属性,带上target因该有所判断的属性。例如:
<target name="build-module-A" if="module-A-present"/>
<target name="build-own-fake-module-A" unless="module-A-present"/>
如果没有if或unless属性,target总会被执行。

可选的description属性可用来提供关于target的一行描述,这些描述可由-projecthelp命令行选项输出。

将你的tstamp task在一个所谓的初始化target是很好的做法,其他的target依赖这个初始化target。要确保初始化target是出现在其他target依赖表中的第一个target。在本手册中大多数的初始化target的名字是"init"。

target有下面的属性:

Attribute Description Required
name target的名字 Yes
depends 用逗号分隔的target的名字列表,也就是依赖表。 No
if 执行target所需要设定的属性名。 No
unless 执行target需要清除设定的属性名。 No
description 关于target功能的简短描述。 No

Tasks

一个task是一段可执行的代码。

一个task可以有多个属性(如果你愿意的话,可以将其称之为变量)。属性只可能包含对property的引用。这些引用会在task执行前被解析。

下面是Task的一般构造形式:
<name attribute1="value1" attribute2="value2" ... />
这里name是task的名字,attributeN是属性名,valueN是属性值。

有一套内置的(built-in)task,以及一些可选task,但你也可以编写自己的task。

所有的task都有一个task名字属性。Ant用属性值来产生日志信息。

可以给task赋一个id属性:
<taskname id="taskID" ... />
这里taskname是task的名字,而taskID是这个task的唯一标识符。通过这个标识符,你可以在脚本中引用相应的task。例如,在脚本中你可以这样:
<script ... >
task1.setFoo("bar");
</script>
设定某个task实例的foo属性。在另一个task中(用java编写),你可以利用下面的语句存取相应的实例。
project.getReference("task1").
注意1:如果task1还没有运行,就不会被生效(例如:不设定属性),如果你在随后配置它,你所作的一切都会被覆盖。

注意2:未来的Ant版本可能不会兼容这里所提的属性,因为很有可能根本没有task实例,只有proxies。

Properties

一个project可以有很多的properties。可以在buildfile中用property task来设定,或在Ant之外设定。一个property有一个名字和一个值。property可用于task的属性值。这是通过将属性名放在"${"和"}"之间并放在属性值的位置来实现的。例如如果有一个property builddir的值是"build",这个property就可用于属性值:${builddir}/classes。这个值就可被解析为build/classes。

内置属性

如果你使用了<property> task 定义了所有的系统属性,Ant允许你使用这些属性。例如,${os.name}对应操作系统的名字。

要想得到系统属性的列表可参考the Javadoc of System.getProperties

除了Java的系统属性,Ant还定义了一些自己的内置属性:
	basedir 		project基目录的绝对路径 (与<project>的basedir属性一样)。
	ant.file 		buildfile的绝对路径。
	ant.version 	Ant的版本。
	ant.project.name 	当前执行的project的名字;由<project>的name属性设定.
	ant.java.version 	Ant检测到的JVM的版本; 目前的值有"1.1", "1.2", "1.3" and "1.4".
    
例子
<project name="MyProject" default="dist" basedir="."> 

	<!-- set global properties for this build -->
	<property name="src" value="."/>
	<property name="build" value="build"/>
	<property name="dist" value="dist"/> 
    
	<target name="init">
		<!-- Create the time stamp -->
		<tstamp/>
		<!-- Create the build directory structure used by compile -->
		<mkdir dir="${build}"/>
	</target>
	  
	<target name="compile" depends="init">
		<!-- Compile the java code from ${src} into ${build} -->
		<javac srcdir="${src}" destdir="${build}"/>
	</target>
	
	<target name="dist" depends="compile">
		<!-- Create the distribution directory -->
		<mkdir dir="${dist}/lib"/>
		<!-- Put everything in ${build} into the MyProject-${DSTAMP}.jar file -->
		<jar jarfile="${dist}/lib/MyProject-${DSTAMP}.jar" basedir="${build}"/>
	</target>
	
	<target name="clean">
		<!-- Delete the ${build} and ${dist} directory trees -->
		<delete dir="${build}"/>
		<delete dir="${dist}"/>
	</target>
	
</project>
Token Filters

一个project可以有很多tokens,这些tokens在文件拷贝时会被自动扩展,这要求在支持这一行为的task中选择过滤拷贝功能。这一功能可用filter task在buildfile中设定。

既然这很可能是一个有危害的行为,文件中的tokens必须采取@token@的形式,这里token是filter task中设定的token名。这种token语法与其他build系统执行类似filtering的语法相同,而且与大多数的编程和脚本语言以及文档系统并不冲突,

注意:如果在一个文件中发现了一个@token@形式的token,但没有filter与这个token关连,则不会发生任何事;因此,没有转义方法-但只要你为token选择合适的名字,就不会产生问题。

警告:如果你在拷贝binary文件时打开filtering功能,你有可能破坏文件。这个功能只针对文本文件。

Path-like Structures
你可以用":"和";"作为分隔符,指定类似PATH和CLASSPATH的引用。Ant会把分隔符转换为当前系统所用的分隔符。

当需要指定类似路径的值时,可以使用嵌套元素。一般的形式是
	<classpath>
		<pathelement path="${classpath}"/>
		<pathelement location="lib/helper.jar"/>
	</classpath>
location属性指定了相对于project基目录的一个文件和目录,而path属性接受逗号或分号分隔的一个位置列表。path属性一般用作预定义的路径--其他情况下,应该用多个location属性。

为简洁起见,classpath标签支持自己的path和location属性。所以:
	<classpath>
		<pathelement path="${classpath}"/>
	</classpath>
可以被简写作:
	<classpath path="${classpath}"/>
也可通过<fileset>元素指定路径。构成一个fileset的多个文件加入path-like structure的顺序是未定的。
	<classpath>
		<pathelement path="${classpath}"/>
		<fileset dir="lib">
			<include name="**/*.jar"/>
		</fileset>
		<pathelement location="classes"/>
	</classpath>
上面的例子构造了一个路径值包括:${classpath}的路径,跟着lib目录下的所有jar文件,接着是classes目录。

如果你想在多个task中使用相同的path-like structure,你可以用<path>元素定义他们(与target同级),然后通过id属性引用--参考Referencs例子。

path-like structure可能包括对另一个path-like structurede的引用(通过嵌套<path>元素):
	<path id="base.path">
		<pathelement path="${classpath}"/>
		<fileset dir="lib">
			<include name="**/*.jar"/>
		</fileset>
	<pathelement location="classes"/>
	</path>
		<path id="tests.path">
		<path refid="base.path"/>
		<pathelement location="testclasses"/>
	</path>
前面所提的关于<classpath>的简洁写法对于<path>也是有效的,如:
	<path id="tests.path">
  		<path refid="base.path"/>
		<pathelement location="testclasses"/>
	</path>
可写成:
	<path id="base.path" path="${classpath}"/>
命令行变量

有些task可接受参数,并将其传递给另一个进程。为了能在变量中包含空格字符,可使用嵌套的arg元素。

Attribute Description Required
value 一个命令行变量;可包含空格字符。 只能用一个
line 空格分隔的命令行变量列表。
file 作为命令行变量的文件名;会被文件的绝对名替代。
path 一个作为单个命令行变量的path-like的字符串;或作为分隔符,Ant会将其转变为特定平台的分隔符。

例子
	<arg value="-l -a"/>
是一个含有空格的单个的命令行变量。
	<arg line="-l -a"/>
是两个空格分隔的命令行变量。
	<arg path="/dir;/dir2:\dir3"/>
是一个命令行变量,其值在DOS系统上为\dir;\dir2;\dir3;在Unix系统上为/dir:/dir2:/dir3 。

References

buildfile元素的id属性可用来引用这些元素。如果你需要一遍遍的复制相同的XML代码块,这一属性就很有用--如多次使用<classpath>结构。

下面的例子:
	<project ... >
		<target ... >    
			<rmic ...>      
				<classpath>        
					<pathelement location="lib/"/>        
					<pathelement path="${java.class.path}/"/>        
					<pathelement path="${additional.path}"/>      
				</classpath>    
			</rmic>  
		</target>
		<target ... >
			<javac ...>
				<classpath>
					<pathelement location="lib/"/>
					<pathelement path="${java.class.path}/"/>
					<pathelement path="${additional.path}"/>
				</classpath>
			</javac>
		</target>
	</project>
可以写成如下形式:
	<project ... > 
		<path id="project.class.path">  
			<pathelement location="lib/"/>
			<pathelement path="${java.class.path}/"/>   
			<pathelement path="${additional.path}"/> 
		</path>
		<target ... >
			<rmic ...>
				<classpath refid="project.class.path"/>
			</rmic>
		</target>
		<target ... > 
			<javac ...>
				<classpath refid="project.class.path"/>
			</javac>
		</target>
	</project>
所有使用PatternSets, FileSets 或 path-like structures嵌套元素的task也接受这种类型的引用。

返回

 

版权所有:UML软件工程组织