zoukankan      html  css  js  c++  java
  • 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>
  • 相关阅读:
    WHERE col1=val1 AND col2=val2;index exists on col1 and col2, the appropriate rows can be fetched directly
    MySQL 交集 实现方法
    MBProgressHUD的使用
    Xcode4 使用 Organizer 分析 Crash logs(转)
    SimpleXML 使用详细例子
    PHP的XML Parser(转)
    iPhone,iPhone4,iPad程序启动画面的总结 (转)
    Pop3得到的Email 信件格式介绍
    yii总结
    隐藏Tabbar的一些方法
  • 原文地址:https://www.cnblogs.com/yefengmeander/p/2950648.html
Copyright © 2011-2022 走看看