Ant Task
一 Task的命令行参数
有些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 。
二 时间戳<tstamp/>
在生成环境中使用当前时间和日期,以某种方式标记某个生成任务的输出,以便记录它是何时生成的,这经常是可取的。这可能涉及编辑一个文件,以便插入一个字符串来指定日期和时间,或将这个信息合并到 JAR 或 zip 文件的文件名中。
这种需要是通过tstamp 任务来解决的。这个任务通常在某次生成过程开始时调用,比如在一个 init 目标中。这个任务不需要属性,许多情况下只需 <tstamp/> 就足够了。
tstamp 不产生任何输出;相反,它根据当前系统时间和日期设置 Ant 属性。下面是 tstamp 设置的一些属性、对每个属性的说明,以及这些属性可被设置到的值的例子:
属性说明例子
DSTAMP 设置为当前日期,默认格式为yyyymmdd 20031217
TSTAMP 设置为当前时间,默认格式为 hhmm 1603
TODAY 设置为当前日期,带完整的月份2003 年 12 月 17 日
例如,在前一小节中,我们按如下方式创建了一个 JAR 文件:
<jar destfile="package.jar" basedir="classes"/>
在调用 tstamp 任务之后,我们能够根据日期命名该 JAR 文件,如下所示:
<jar destfile="package-{DSTAMP}.jar" basedir="classes"/>
因此,如果这个任务在 2003 年 12 月 17 日调用,该 JAR 文件将被命名为 package-20031217.jar。
还可以配置 tstamp 任务来设置不同的属性,应用一个当前时间之前或之后的时间偏移,或以不同的方式格式化该字符串。所有这些都是使用一个嵌套的 format 元素来完成的,如下所示:
<tstamp>
<format property="OFFSET_TIME"
pattern="HH:mm:ss"
offset="10" unit="minute"/>
</tstamp>
上面的清单将 OFFSET_TIME 属性设置为距离当前时间 10 分钟之后的小时数、分钟数和秒数。
用于定义格式字符串的字符与 java.text.SimpleDateFormat 类所定义的那些格式字符相同 。
三 发送email的task
使用SMTP服务器发送邮件
例子:
<mail mailhost="smtp.myisp.com" mailport="1025" subject="Test build">
<from address="me@myisp.com"/>
<to address="all@xyz.com"/>
<message>The {buildname} nightly build has completed</message>
<fileset dir="dist">
<includes name="**/*.zip"/>
</fileset>
</mail>
四 ssh和scp
在ant中,使用ssh命令远程启动和停止另外一台机器上的tomcat
下面是远程停止192.168.0.2这台机器上的tomcat:
<target name="sshexecstop" >
< sshexec host="192.168.0.2" username="root" password="123456" trust="true" command="cd /usr/local/tomcat/bin;./shutdown.sh" />
< /target>
下面是远程启动192.168.0.2这台机器上的tomcat:
<target name="sshexecstart">
<sshexec host="192.168.0.2" username="root" password="123456" trust="true" command="cd /usr/local/tomcat/bin;./startup.sh" />
</target>
在ant中,使用scp将本地的文件(appwar目录下的文件)远程拷贝到另外一台机器的tomcat下:
<target name="copy" >
< scp todir="root:123456@192.168.0.2:/usr/local/tomcat/webapps/" trust="true">
< fileset dir="/home/cruisecontrol/projects/eholderWeb/build/appwar" />
< /scp>
< /target>
六 exec
执行其他进程的task
<exec executable="cmd.exe">
<arg line="/c start http://localhost:8080>
</exec>
七其他的task
if task
ant原来可以在target级进行if判断(unless,if 属性),但实在太不方便了。
Conditions
但Ant预先封装的一堆condition很是很方便的。这些condition完全从实际出发,包括文件是否存在,http://localhost:8080是否连通都可以作为条件,见Ant的参考手册。
For task
支持"a,b,c,d" 字符串数组的循环与文件目录,Fileset的循环。
Parallel task
Parallel非常有用,比如我想一边开tomcat,一边做别的,就需要使用它,否则就只有用spawn=true属性把tomcat放在后台运行。spawn有很多不好的地方,比如不能即时在console看到信息,停止ant运行不能把tomcat关掉等。
Parallel相当于一个容器,放在里面的每个task都会被并行执行。如果想把某几个task顺序执行,用相当于()的Sequential task 包起来。
Waitfor task
暂停ant执行直到条件符合,比如<waitfor><http url=http://localhost:8080/></waitfor>就会等待tomcat启动后才会继续往下执行。
Ant流程控制 if elseif else
使用循环,判断等控制时需添加ant-contrib包。
taskdef定义第三方的任务,主要包含ant的处理流程逻辑
if elseif else格式如下
<property name="hello" value="false"/>
<taskdef resource="net/sf/antcontrib/antcontrib.properties" classpath="D:\Program Files\apache-ant-1.8.4\lib\ant-contrib-1.0b3.jar" />
<target name="run" depends="compile">
<java classname="HelloWorld">
<classpath path="${classdir}"/>
</java>
<if>
<equals arg1="${hello}" arg2="true"/>
<then>
<echo>${hello} is true</echo>
</then>
<elseif>
<equals arg1="${hello}" arg2="false"/>
<then>
<echo>${hello} is false</echo>
</then>
</elseif>
<else>
<echo>${hello}</echo>
</else>
</if>
</target>八 antcall 与ant
depends:depends中的targets在本target执行前按照从左到右的定义顺序调用。
antcall : 用来调用同一个build.xml中的其他的target,相当于高级语言中的函数调用。
ant: 调用其他的build.xml中的target。
九 自定义TASK的使用
ANT已经内置了不少task,像copy、replace、javac等等,但是有时候还需要一些特定的任务来完成操作,比如在生成JAD文件 时,需要一个Midlet-Jar-Size的参数,得到JAR文件的大小,但是通过内部的task无法实现,因此可以自己编写类来实现此功能,但必须保 证该类是从Task类继承过来的。
例:
<taskdef name="filesize" classname="ant.FileSizeTask" classpath="${LIB_PATH}/FileSizeTask.jar" />
<filesize file="${Base}/Demo_Build/${jarName}" property="size" />
<replace dir="store" includes="${jadName}" encoding="UTF-8">
<replacefilter token="@FILESIZE@" value="${size}" />
</replace>
设置Property
<!--设置属性name-value--> <property name="foo.dist" value="dist"/> <!--读取属性文件中的属性配置--> <property file="foo.properties"/> <!--读取网络中的property-set--> <property url="http://www.mysite/props/foo.properties"/> <!--读取文件中的属性配置--> <property resource="foo.properties"/> <!--读取环境变量--> <property environment="env"/>
Using Length
<!--把字符串foo的长度保存到属性length.foo中--> <length string="foo" property="length.foo"/> <!--把文件bar的长度保存到属性length.bar--> <length file="bar" property="length.bar"/>
输出信息:
<echo message="XXX"/> <echo>XXX</echo>
引入另一个xml文件
<import file="../common-targets.xml"/>
显示错误信息
Fail task 退出当前构建,抛出BuildException,打印错误信息。
message:A message giving further information on why the build exited
if:Only fail if a property of the given name exists in the current project
unless:Only fail if a property of the given name doesn't exist in the current project
status:Exit using the specified status code; assuming the generated Exception is not caught, the JVM will exit with this status.Since Apache Ant 1.6.2
<fail>Something wrong here.</fail>
<fail message="${属性}"/>
<!--如果这个属性不存在,显示错误-->
<fail unless="failCondition" message="unless Condition"/>
<!--如果这个属性存在,显示错误-->
<fail if="failCondition" message="if Condition"/>
<!--如果符合条件,显示错误-->
<fial message="tag condition">
<condition>
<not>
<isset property="failCondition"/>
</not>
</condition>
</fail>文件和目录处理
拷贝文件
<copy file="myfile.txt" tofile="mycopy.txt"/>
<copy file="myfile.txt" todir="../otherdir"/>
<copy todir="..//newdir">
<fileset dir="src_dir"/>
</copy>
<copy todir="../destdir">
<fileset dir="src_dir">
<exclude name="**/*.java"/>
</fileset>
</copy>
<copy todir="../destdir">
<fileset dir="src_dir" excludes="**/*.java"/>
</copy>
<!--拷贝一个文件集合到一个目录,同时建立备份文件-->
<copy todir="../backup/dir">
<fileset dir="src_dir"/>
<globmapper from="*" to="*bak"/>
</copy>
<!--拷贝一个集合的文件到指定目录,并替换掉TITLE-->
<copy todir="../backup/dir">
<fileset dir="src_dir"/>
<filterset>
<filter token="TITLE" value="Foo Bar"/>
</filterset>
</copy>
<copydir src="${src}/resources" dest="${dest}" includes="**/*.java" excludes="**/Test.java"/>
<copyfile src="test.java" dest="subdir/test.java"/>删除文件,目录
<delete file="/lib/ant.jar"/>
<delete dir="/lib"/>
<delete>
<fileset dir="." includes="**/*.bak"/>
</delete>
<!--删除当前目录下所有的文件和目录,包括当前目录-->
<delete includeEmptyDirs="true">
<fileset dir="build"/>
</delete>
<!--删除当前目录下所有的文件和目录,不包括当前目录-->
<delete includeEmptyDirs="true">
<fileset dir="build" includes="**/*"/>
</delete>
<!--删除当前目录下所有的svn相关文件(因为SVN文件默认是excludes的,所以这里说明一下)-->
<delete defaultExcludes="false">
<fileset dir="${src}" includes="**/*.svn"/>
</delete>
<!--删除文件目录树-->
<deltree dir="dist"/>建立目录
<mkdir dir="${dist}/lib"/>剪切文件
<move todir="some/new/dir">
<fileset dir="my/src/dir">
<include name="**/*.jar"/>
<exclude name="**/ant.jar"/>
</fileset>
</move>重命名
<rename src="foo.jar" dest="ant-${version}.jar"/>建立临时文件
<!--在目录build下,建立文件名为temp.file,后缀名为xml的文件--> <tempfile property="temp.file" destDir="build" suffix=".xml"/>
Touch使用
<!--如果文件不存在,创建文件,如果存在,更改最后访问时间为当前系统时间-->
<touch file="myfile"/>
<!--如果文件不存在,创建文件,更改最后访问时间-->
<touch file="myfile" datetime="06/28/2000 2:02 pm"/>
<!--更改目录下所有文件的最后访问时间-->
<touch datetime="09/10/1974 4:30 pm">
<fileset dir="src_dir"/>
</touch>Ant中使用文件的压缩
<?xml version="1.0"?>
<project name="access" default="show" basedir=".">
<buildnumber/>
<target name="show">
<echo message="${build.number}"/>
<tar destfile="${build.number}.tar.bz2" compression="bzip2">
<fileset dir=".">
<include name="*.xml"/>
</fileset>
</tar>
<untar src="${build.number}.tar.bz2" compression="bzip2" dest="dest" />
</target>
</project>buildnumber: 每次运行的时候,ant都会将该值进行增加并写到一个build.number文档中(在basedir下面).当然,你也可以使用file属性指定到某个文件中.如下
<buildnumber file="my.properties"/>
如果在运行时,ant在指定(或者默认)的文件中找不到build.number值,它会重新生成该值,并从0开始增长.
tar是Linux平台上的打包命令. 在Linux上,文件压缩命令如zip,gzip,bzip2等,都只能针对单独的文件进行,你不能对文件或多个文件进行压缩. 为了让多个文件形成一个整体,就需要使用tar命令. tar会将零散的多个文件连接成一个整体,但是默认的情况下,它是不会压缩这些零散的文件的。也就是说tar命令的产生就是为也形成一个整体文件(名副其实的打包命令),然后再供gzip、bzip2之类的文件压缩命令使用。在tar后来的发展过程中,为了方便,tar有了一些文件压缩的属性选项。ant的tar就是模仿linux上tar命令。你可以加一个compression属性对文件进行压缩。具体的打包和解包如上。
<!--解压缩zip文件-->
<unzip src="apache-ant-bin.zip" dest="${tools.home}">
<patternset>
<include name="apache-ant/lib/ant.jar"/>
</patternset>
<mapper type="flatten"/>
</unzip>
<!--压缩zip文件-->
<zip destfile="${dist}/manual.zip" basedir="htdoc/manual" includes="api/**/*.html" excludes="**/todo.html"/>
<!--打tar包-->
<tar destfile="/Users/antoine/dev/asf/ant-core/docs.tar">
<tarfileset dir="${dir.src}/doc" fullpath="/usr/doc/ant/README" preserveLeadingSlashes="true">
<include name="readme.txt"/>
<tarfileset>
<tarfileset dir="${dir.src}/docs" prefix="/usr/doc/ant" preserveLeadingSlashes="true">
<include name="*.html"/>
</tarfileset>
</tar>
<!--解压tar包-->
<untar src="tools.tar" dest="${tools.home}"/>Ant中过滤器的使用
<?xml version="1.0"?>
<project name="access" default="show" basedir=".">
<!--
将一组需要过滤的值写入一个过滤文件,过滤文件的
格式与一般的属性文件相同,如下:
month=12
year=2008
-->
<filter filtersfile="filter.properties"/>
<!--
定义一个过滤器
-->
<filter token="time" value="14时7分"/>
<target name="show">
<mkdir dir="dest"/>
<!-- 在copy中添加filtering属性启动过滤器 -->
<copy todir="dest" filtering="true">
<fileset dir="src"/>
</copy>
</target>
</project>该过滤器的作用就是将src下面的文件复制到dest目录下面.而且将src下面,凡是包含@year@,@month@,@time@的字符进行替换(是指src文件夹下面文件中的内容,不是指文件名)。另外还可以使用filterset标签,它也可以引用外部filter文件,也可以在内容指定一组filter,如下:
与单纯地使用filter相比,filterset的功能要强大一点,使用得被替换的字符不仅限制于以@开始和结束的变量了,你可以自己定义(使用begintoken和endtoken)。
<copy file="${build.dir}/version.txt" toFile="${dist.dir}/version.txt">
<filterset>
<filter token="DATE" value="${TODAY}"/>
</filterset>
</copy>
<!-- 自定义变量的格式 -->
<copy file="${build.dir}/version.txt" toFile="${dist.dir}/version.txt">
<filterset begintoken="%" endtoken="*">
<filter token="DATE" value="${TODAY}"/>
</filterset>
</copy>
<!-- 使用外部的过滤定义文件 -->
<copy toDir="${dist.dir}/docs">
<fileset dir="${build.dir}/docs">
<include name="**/*.html">
</fileset>
<filterset begintoken="%" endtoken="*">
<filtersfile file="${user.dir}/dist.properties"/>
</filterset>
</copy>
<!-- 使用引用方式,重复利用过滤集 -->
<filterset id="myFilterSet" begintoken="%" endtoken="*">
<filter token="DATE" value="${TODAY}"/>
</filterset>
<copy file="${build.dir}/version.txt" toFile="${dist.dir}/version.txt">
<filterset refid="myFilterSet"/>
</copy>Ant交互
Ant脚本在运行过程中可以与操作人员进行交互。交互时使用到的主要几个标签如下:
<input
message="All data is going to be deleted from DB continue (y/n)?"
validargs="y,n"
addproperty="do.delete" defaultvalue="n"
/><condition property="do.abort">
<equals arg1="n" arg2="${do.delete}"/> </condition>
<fail if="do.abort">Build aborted by user.</fail>input :是指输入,message - 显示给用户的提示;validargs - 只有在用户输入validargs指定的值时,输入才是有效的。addproperty - 用户在完成一次有效的输入后,会产生一个属性,该属性的名称为addproperty的值,属性的值则为用户的输入。如果用户不做输入操作(如直接回车),则属性的值将会是defaultvalue的值。
condition:是对条件进行判断,条件判断完成后,也会产生一个属性值。属性名称为property的值,而属性的值则由condition的内部标签返回值决定,默认为ture|false,但是不仅限于这两个值,如下情况:
<condition property="do.delete" value="yes" else="no">
<equals arg1="n" arg2="${input.value}"/>
</condition>
<!--
当用户的输入为n是,equals返回为ture,此是,do.delete的值会被设置为“yes”(该值由value属性指定),否则do.delete的值为被设置为no(由else属性的值指定)。也就是说,condition产生的属性的值并不仅限于true和false
-->equals:等于判断。它有两个属性,arg1和arg2,不用说,就是判断这两个属性的值是否相等了。
此外,condition还支持其它类似的判断操作符如and、or、available、isset、istrue、isfalse、contains等等(详见http://ant.apache.org/manual/CoreTasks/conditions.html)。其中有几个需要重点讲一下(个人觉得功能还是比较有用的):
正则表达式的应用(具体如何使用正则表达式这里就不说了,强大但复杂,可以写一部新华字典出来)
Condition的使用 <and> <or> <not>等tag
peoperty: The name of the property to set
value: The value to set the property to.
else: The value to set the property to if the codition evaluates to false.
<condition property="isMacOsButNotMacOsX">
<and>
<os family="mac">
<not>
<os family="unix">
</not>
</and>
</condition>1.matches:先看个例子
<condition property="legal-password">
<matches pattern="[1-9]" string="${user-input}"/>
</condition>
<fail message="Your password should at least contain one number"
unless="legal-password"/>其中呢,pattern就是正则表达式了,而string就是需要进行匹配的字符窜。而它还有几个有用的属性
casesensitive(true|false):是否区分大小写(默认为true);
singleline(true|false):单行模式,此时“.”将可以匹配换行符(默认为false);
multiline(true|false):多行模式(默认为false);
另外,在使用正则表达式时,ant还支持复用(可省很多事)。如下:
<regexp id="date.pattern" pattern="^[0123]\d-[01]\d-[12]\d\d\d$"/>
<condition property="is_expected">
<matches string="${today}">
<regexp refid="date.pattern"/>
</matches>
</condition>
<!-- 如上,我们就可以重复地利用一段正式表达式了,就像声明了一个变量一样 -->2.antversion
这个标签是指定ant的使用版本。为也避免使用过程中产生的不兼容性(如果把jdk1.6的代码在jdk1.4上运行时没有任何异常提示,也没有任何运行结果,你肯定很不爽),所以大家写脚本时,也尽量指定一个ant的版本需求。
antversion有两个属性:
atleast(major.minor.point):在某个版本以上(如1.7.0,通常应该是你当前脚本的运行版本)都可以正常使用;
exactly(major.minor.point):只能在这个指定的版本上运行(要求很苛刻呀,可能是这个版本上有特别的功能,而刚好其前后的版本都没有)。
下面是一个例子:
<condition property="ant-is-exact-7">
<antversion exactly="1.7.0"/>
</condition>
<fail unless="${ant-is-exact-7}" message="您使用的Ant版本不符合要求"/>替换
<replace
file="configure.sh"
value="defaultvalue"
propertyFile="src/name.properties">
<replacefilter
token="@token1@"/>
<replacefilter
token="@token2@"
value="value2"/>
<replacefilter
token="@token3@"
property="property.key"/>
<replacefilter>
<replacetoken>@token4@</replacetoken>
<replacevalue>value4</replacevalue>
</replacefilter>
</replace>Ant中的classpath
<classpath>
<pathelement path="${classpath}"/>
<fileset dir="lib">
<include name="**/*.jar"/>
</fileset>
<pathelement location="classes"/>
<dirset dir="${build.dir}">
<include name="apps/**/classes"/>
<exclude name="apps/**/*Test*"/>
</dirset>
<filelist refid="third-party_jars"/>
</classpath>对于classpath标签的两个属性——path和location:path指定jar包,location指定包含jar包的路径
按照官网上的解释:
location的值可以是一个文件(file),也可以是一个相对于当前根目录(project的basedir)的文件夹(directory),或者是一个带有绝对路径的文件(文件夹)
path则表示的是一系列的用分号(“;”)或冒号(“:”)分隔开的location值。
当然,也可以如上所示,在classpath标签中使用fileset、dirset,这样省事不少。
使用classpath:
<target>
<javac>
<classpath refid="project.class.path"/>
</javac>
</target>在上面的代码中还提到了一个filelist标签。它代表了一组文件,相当于一个文件集合。但是这个文件集合中的文件并不是一定会存在。filelist的使用方式如下:
<filelist
id="docfiles"
dir="${doc.src}"
files="foo.xml,bar.xml"/>
<filelist
id="docfiles"
dir="${doc.src}">
<file name="foo.xml"/>
<file name="bar.xml"/>
</filelist>
<!-- 其中的${doc.src}/foo.xml,${doc.src}/bar.xml并不一定会存在地 -->编译和打包
package jar
<jar destfile="${dist}/lib/app.jar" basedir="${build}/classes"/>
<jar destfile="${build}/lib/app.jar" basedir="${build}/classes" includes="mypackage/test/**" excludes="**/Test.class"/>package ear
<ear destfile="build/myapp.ear" appxml="src/metadata/application.xml">
<fileset dir="build" includes="*.jar,*war"/>
</ear>compile source program
<javac srcdir="${src}" destdit="${build}" classpath="xyz.jar" debug="on" source="1.4"/>running jar
<java classname="test.Main" dir="${exec.dir}" jar="${exec.dir}/dist/test.jar" fork="true" failonerror="true" maxmemory="128m">
<arg value="-h"/>
<classpath>
<pathelement location="dist/test.jar"/>
<pathelement path="/Users/antoine/dev/asf/ant-core/bootstrap/lib/ant-launcher.jar"/>
</classpath>
</java>package war
<war destfile="myapp.war" webxml="src/metadata/myapp.xml">
<fileset dir="src/html/myapp"/>
<fileset dir="src/jsp/myapp"/>
<lib dir="thirdparty/libs">
<exclude name="jdbc1.jar">
</lib>
<classes dir="build/main"/>
<zipfileset dir="src/graphics/images/gifs" prefix="images"/>
</war>调用chmod
<chmod perm="go-rwx" type="file">
<fileset dir="/web">
<include name="**/*.cgi"/>
<include name="**/*.old"/>
</fileset>
<dirset dir="/web">
<include name="**/private_*"/>
</dirset>
</chmod>