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" failοnerrοr="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>