Java Build Practice 3:Ant Task

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>
原文地址:https://www.cnblogs.com/yefengmeander/p/2950648.html