使用Ant执行程序

    之前我们讲解了一个构建过程来编译和测试JAVA源代码,在测试表明代码是正确的之后,我们就该运行它了。这意味着应该探索Ant执行外部程序的能力的时候了。这些可外部程序可以是JAVA程序也可以是本地程序。

1.       使用Ant执行外部程序的原因:
由于Ant得益于其内置的任务,所以可以自己完成很多工作而不需要求助于外部代码。但是在大型工程中,很快就会发现它们需要使用外部程序,既可能使用本地程序,又可能使用JAVA应用程序。
      实际的构建或测试应用程序是最常见的Ant内部运行的程序,这些程序的任务就是在主程序中进行单元测试、系统测试和载入测试。其他常见的外部程序就是“遗留构建步骤”:软件的某些部分需要一个本地编译器、Perl脚本或仅仅是一些用在构建中的本地工具程序。
      从Ant内部运行程序时,有两个方案:
l         编写一个定制的Ant任务来调用程序,适用于需要在很多构建文件中使用同一个外部程序的情况。(适用于长期项目,经常工程编写Ant任务包装器)。
l         编写一个新的Ant任务,这个任务只是从构建文件中调用程序。从构建文件中直接调用程序是编写定制任务的基础。
Ant可以包装本地应用程序,而JAVA应用程序可以在Ant的JVM中运行,也可以在其外部运行。无论执行什么类型的应用程序,也无论怎样执行它,Ant都会挂起构建本身直至程序结束。程序的所有控制台输出都会加入到Ant的日志工具里,通常显示到屏幕里。被包装的程序无法读取控制台的输出,因此需要用户输入的程序无法运行。
2.       运行JAVA程序:
启动JAVA程序是Ant的拿手好戏。这种方法最好的一个特性就是可以轻而易举的指定classpath。它比在编写批处理或者是SHELL脚本时手工指定每个类库要容易得多;它可以将classpath中的lib/**/*.jar拥有的 所有文件统统包含近来。
Ant执行JAVA程序另一个好处是它可以在当前的JVM中运行程序。即使指定的classpath是由定制的类载入器提供的,也可以做到。在当前的JVM内运行的程序可以减少启动延迟;它只在载入新类时消耗时间,因此有助于构建保持快速。然而,在新的JVM中执行代码也有很多理由,”forking”(建立新进程),它在一些情形下更适用:
l         如果不建立新进程,就无法指定一个新的工作路径。
l         当你在建立新进程时,如果遇到与类载入器有关的奇怪的错误或者安全冲突,这很可能是因为在两个载入器里载入了同一个类:原先Ant中的类载入器和一个新的类载入器。在父类载入器或子类载入器中建立新进程或者追踪错误的JAR,然后移除它。
l         不能在同一个的JVM中执行JAR;而必须建立新的进程。
l         需要大量内存或leaky的JAVA程序应当运行在它们自己的JVM里,并给这个JVM分配适当的内存空间。
l         建立一个新进程也允许在另一个版本的虚拟机中运行代码。这个版本可以不同于启动ANT的虚拟机版本。
a)       首先介绍一下<java>任务:
建立一个JAVA类,然后打印入口参数,为了讲解classpath,这里我们引入一个记录日志的软件包,log4j来记录日志:
工程目录如下:


类代码如下:
package com.neusoft.test;

import org.apache.log4j.Logger;

publicclass TestJava {
      privatestatic Logger logger = Logger.getLogger(TestJava.class);

      /**
       *enterpointmethod
       *
       *@paramargs
       *            enterpoingarguments
       */
      publicstaticvoid main(String[] args) {
           if (args.length <= 0) {
                 logger.error("Bad arguments!");
                 System.out.println("Usage:\n\tjava TestJava arg1 args2 ...");
                 System.exit(-1);
           }
           for (int i = 0; i < args.length; i++) {
                 logger.info("arg" + i + "=" + args[i]);
                 System.out.println("arg" + i + "=" + args[i]);
           }
      }
}
在不输入入口参数的情况下,打印帮助信息,并退出程序,在输入入口参数的情况下,执行程序,并打印参数。
其中log4j需要一个配置文件,示例如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
      <appender name="error" class="org.apache.log4j.FileAppender">
           <param name="File" value="${user.dir}/logs/error.log" />
           <layout class="org.apache.log4j.PatternLayout">
                 <param name="ConversionPattern"
                      value="%d{yyyy/MM/dd HH:mm:ss},%p,%C.%M,%m%n" />
           </layout>
      </appender>
      <root>
           <priority value="DEBUG" />
           <appender-ref ref="error" />
      </root>
</log4j:configuration>
这里设置了一个文件类型的日志输出器,所有日志输出都将被输出到用户目录的logs/error.log文件中,关于log4j的知识,请参考本blog其他文章。运行完成后的工程目录如下图所示:

error.log中将记录日志信息。

下面我们来创建构建文件:
<target name="run " depends="compile">
<echo>running a search</echo>
<java classname="com.neusoft.test.TestJava">
<arg value="The"/>
<arg value="current"/>
<arg value="dir"/>
<arg value="is"/>
<arg file="."/>
</java>
</target>

这样设置还不够,还需要指定classpath,否则可能会出现如下错误:
Buildfile: C:\eclipse\workspace\Ant_Chapater5\build.xml
init:
    [mkdir] Created dir: C:\eclipse\workspace\Ant_Chapater5\build
    [mkdir] Created dir: C:\eclipse\workspace\Ant_Chapater5\build\classes
compile:
    [javac] Compiling 1 source file to C:\eclipse\workspace\Ant_Chapater5\build\classes
run:
     [java] Could not find com.neusoft.test.TestJava. Make sure you have it in your classpath
      ……
BUILD SUCCESSFUL
Total time: 4 seconds

因为我们没有配置classpath。当任何指定的classpath都不存在时,<java>任务就使用Ant自身的classpath:诸如ant.jar以及其他任何位于ANT_HOME/lib目录下的类库,再加上系统CLASSPATH环境变量的内容。就几乎所有<java>任务而言,应该另外指定一个classpath。当这样做的时候,已经存在的所有非java和javax包的classpath中的内容就不再可用了。这种方式与<javac>不同,在<javac>中如果够文件不指定其他值,则自动包含Ant运行时classpath。

添加classpath很容易:在<classpath>元素中写路径或者提供classpath属性一个字符串形式的路径。如果想做多个不同的地方使用classpath,最好先设置好classpath,然后再通过classpathref属性引用它。一个常见的惯例是:
用第二个包含有新的生成类的classpath来扩展第一个编译时classpath,而不管这个新的生成类是档案文件形式还是.class文件的目录树形式。
我们也采用这种办法,一个用于编译,一个用于执行:
<path id="compile.classpath">
<pathelement path="src/log4j-1.2.13.jar"/>
</path>
<path id="run.classpath">
<path refid="compile.classpath"/>
<pathelement location="${build.dir}/classes"/>
</path>
第一个classpath包含编译时所需要的类库,第二个添加我们刚刚编写的代码。易于维护,任何编译时所需要新的类库都会自动传递到运行时classpath。
      此时可以修改<java>任务如下:
<java classname=" com.neusoft.test.TestJava"
classpathref="run.classpath">
<arg value="The"/>
<arg value="current"/>
<arg value="dir"/>
<arg value="is"/>
<arg file="."/>
</java>

现在构建文件如下:
<?xml version="1.0" encoding="UTF-8" ?>
<projectname="run-java-application"default="run">

      <propertyname="build.dir"value="build"/>
      <pathid="compile.classpath">
           <pathelementlocation="src/log4j-1.2.13.jar"/>
      </path>
      <pathid="run.classpath">
           <pathrefid="compile.classpath"/>
           <pathelementlocation="${build.dir}/classes"/>
      </path>
      <targetname="init">
           <deletedir="${build.dir}"/>
           <deletedir="logs"/>
           <mkdirdir="${build.dir}"/>
           <mkdirdir="${build.dir}/classes"/>
      </target>
      <targetname="compile"depends="init">
           <javacsrcdir="src"destdir="${build.dir}/classes">
                 <classpathrefid="compile.classpath"/>
           </javac>
      </target>
      <targetname="run"depends="compile">
           <javaclassname="com.neusoft.test.TestJava">
                 <classpathrefid="run.classpath"/>
                 <argvalue="The"/>
                 <argvalue="current"/>
                 <argvalue="dir"/>
                 <argvalue="is"/>
                 <argfile="."/>
           </java>
      </target>
</project>

构建过程运行如下:
Buildfile: C:\eclipse\workspace\Ant_Chapater5\build.xml
init:
    [mkdir] Created dir: C:\eclipse\workspace\Ant_Chapater5\build
    [mkdir] Created dir: C:\eclipse\workspace\Ant_Chapater5\build\classes
compile:
    [javac] Compiling 1 source file to C:\eclipse\workspace\Ant_Chapater5\build\classes
run:
     [java] log4j:WARN No appenders could be found for logger (com.neusoft.test.TestJava).
     [java] log4j:WARN Please initialize the log4j system properly.
     [java] arg0=The
     [java] arg1=current
     [java] arg2=dir
     [java] arg3=is
     [java] arg4=C:\eclipse\workspace\Ant_Chapater5
BUILD SUCCESSFUL
Total time: 4 seconds

执行完成后工程目录如下:


但是,可以看到,由于构建过程中不能找到log4j的配置文件,所以有如下警告:
[java] log4j:WARN No appenders could be found for logger (com.neusoft.test.TestJava).
[java] log4j:WARN Please initialize the log4j system properly.

也就没有生成logs/error.log日志文件。这个原因本人也在探索中,试图将log4j.xml添加到classpath中,但是还是出问题。如果有遇到类似问题的朋友,希望帮忙解决。
个人初步分析原因是:不能在同一个的JVM中执行JAR;而必须建立新的进程。因为写日志相当于执行了log4j档案文件中的类,所以不能在同一个JVM中运行。

3.       参数:
在<java>任务中,最重要的可选参数是嵌套参数列表。可以通过一个简单值,一行文本,一个用于参数列表优先级解析的文件,或者一个路径来命名一个参数。可以在任务的<arg>元素里指定它们,<arg>元素支持以下四个属性,Ant按这些参数声明的顺序将它们传递给JAVA程序:
value:字符串值。(对XML的特殊记号要进行转义:>变为&gt;   &#x0a;代表换行)
file:文件或目录,在使用之前解析为绝对地址。然后再传递。
line:传递给程序的整个行
path:一个字符串,包含有用冒号或分号隔开的文件或目录。

4.       定义系统特性:
系统是那些要传给JAVA命令行(如:-Dproperty=value)的参数的定义。嵌套的<sysproperty>元素可以用来定义要传递的特性。
<sysproperty key="socksProxyHost" value="socks-server"/>
<sysproperty key="socksProxyPort" value="1080"/>
有两种可选方法能代替value参数:file和path。就如带参数一样,file属性为文件命名;Ant对相对引用进行解析并传入绝对文件名,并使用本地平台的文件分隔符。path属性与之类似,只是允许列入多个文件:
<sysproperty key="configuration.file" file="./config.properties"/>
<sysproperty key="searchpath" path="build/classes:lib/j2ee.jar" />

5.       在新的JVM中运行程序
<java>任务将运行在当前的JVM中,除非fork属性被设置为true,这将减少程序启动的时间。做为实验,我们可以将TestJava放在一个新的JVM中运行。
<target name="run-search-fork" depends="create-jar">
<echo>running a search</echo>
<java classname="com.neusoft.test.TestJava"
classpathref="run.classpath"
fork="true">
<argvalue="The"/>
                 <argvalue="current"/>
                 <argvalue="dir"/>
                 <argvalue="is"/>
<argfile="."/>
</java>
</target>

6.       设置环境变量:
你可以在一个建立新进程的JVM里使用嵌套元素<env>来设置环境变量。这个元素的语法与<sysproperty>元素是一样的。

7.       控制新的JVM

可以选择不同于Ant的JAVA运行时环境,只要通过jvm属性设置JVM命令就可以了,这对与从一个旧的JVM中运行程序时是非常有用的,诸如运行在JAVA1.1系统里的测试,或者可能在JAVA的一个未来版本的BETA版。可以给这个JVM指定参数来控制它,最常用的选项就是设置内存数量,属性maxmemory,这个属性具有一些幕后只能 可以区分JAVA 1.1和1.2系统,生成合适的命令。
可以通过<java>嵌套的<jvmarg>元素提供通用的 JVM参数。这些参数的确切用法与<arg>元素相同。
<target name="run-search-jvmargs" depends="create-jar">
<property name="Search.JVM.extra.args" value="-Xincgc"/>
<java classname=" com.neusoft.test.TestJava"
classpathref="run.classpath"
fork="true"
maxmemory="64m">
<jvmarg line="${Search.JVM.extra.args}"/>
<argvalue="The"/>
                 <argvalue="current"/>
                 <argvalue="dir"/>
                 <argvalue="is"/>
<argfile="."/>
</java>
</target>

当fork=”false”时,所有的JVM选项都不起作用;仅仅显示一行警告信息。
8.       使用failonerror处理错误
尽管核心的构建步骤(compile和JAR)必须完成后才可以认为整个构建是成功的,但是一些其他的构建过程中的任务,它们的失败并不重要。例如发送进度报告的电子邮件并不因为邮件服务器确实而终止构建,同样配置过程中的很多方面,诸如停止一个WEB服务器等,并不会因失败而终止构建。
一些Ant任务有一个共同的属性,failonerror,它可以控制当任务失败时是否终止构建。多数任务的默认值是”true”,意思是任何构建过程中任务的失败都会导致构建失败,并导致ANT的BUILD FAILED信息。
<java>任务支持这个属性,但仅在新的JVM中才支持,如果JAVA程序的返回值不等于0,则终止构建。当JVM内部调用System.exit()时,整个构建过程会因为JAVA停止运行而突然停止并且不显示BUILD FAILED信息:这个调用退出了ANT以及程序。
例如我们将failonerror设置为true,并且不传递任何参数给程序,将调用System.exit(-1),此时JAVA程序的输出为:

<target name="run-search-invalid" depends="compile">
<echo>running a search</echo>
<java classname="com.neusoft.test.TestMain"
classpathref="run.classpath"
failonerror="true"
fork="true">
<argvalue="The"/>
                 <argvalue="current"/>
                 <argvalue="dir"/>
                 <argvalue="is"/>
<argfile="."/>
</java>
</target>
构建输出如下:
run-search-invalid:
[echo] running a search
[java] Usage:
java TestJava arg1 args2 ...
BUILD FAILED
C:\AntBook\app\tools\build.xml:532: Java returned: -1

9.       执行JAR文件:
当一个JAR文件由命令行中的java –jar命令启动时,该JAR文件可以在清单(manifest)上列出一个类的名字做为执行入口,ANT也可以类似地运行JAR文件,但只能在建立新进程的JVM中。这是因为执行JAR文件的过程会载入classpath列表中所列的文件,而其他细节则与JAVA的“扩展”有关。通过一个jar属性设置为文件的文职可以运行JAR文件:
<target name="run-search-jar" depends="create-jar">
<echo>running a search</echo>
<java
jar="${jarfile.path}"
classpathref="run.classpath"
failonerror="true"
fork="true">
<arg value="The" />
                 <arg value="current" />
                 <arg value="dir" />
                 <arg value="is" />
<arg file="." />
</java>
</target>

这个目标并不能实际工作,因为还没有设置清单(manifest),这些内容等到下一章再讲解。

10.       调用第三方程序:
可以使用任务运行由第三方提供的程序,假设我们部署过程的一个环节中包含了停止WEB SERVER的操作,比如TOMCAT 5.5,这在部署中是个常见的动作;为了从构建中进行部署,必须实现自动化每个步骤。幸运的是多数WEB SERVER都提供了这样或那样的方法来做到这一点:从TOMCAT的启动脚本中提取命令来完成<javac>任务:
<property environment="env"/> //获取环境变量
<target name="stop-tomcat"
description="stop tomcat if it is running">
<java classname="org.apache.tomcat.startup.Tomcat">
<classpath>
<fileset dir="${env.TOMCAT_HOME}/lib">
<include name="**/*.jar"/>
</fileset>
</classpath>
<arg value="-stop"/>
<sysproperty key="tomcat.home"
value="${env.TOMCAT_HOME}"/> //将TOMCAT的HOME目录向下传递
</java>
</target>
执行该目标有以下几种结果:
l         TOMCAT存在而且库文件位于预先假设的位置,成功停止TOMCAT
l         本地目前没有任何版本的TOMCAT在运行,无法停止TOMCAT
l         即使已经设置classpath,但是因为TOMCAT未安装或者环境变量未正确设置,因此造成库目录确实而无法创建classpath
这时我们需要预先判断。

11.       在调用之前探察JAVA程序:
在准备调用一个JAVA类之前,在classpath上查找它是很容易的,这样做使得显示警告信息成为可能。对于TOMCAT问题,可以使用<available>任务,或者采用<condition>任务会更好,后者能够通过检测环境变量组合<available>测试:
<target name="validate-tomcat"
<condition property="tomcat.available">
<and>
<isset property="env.TOMCAT_HOME"/>
<available classname="org.apache.tomcat.startup.Tomcat">
<classpath>
<fileset dir="${env.TOMCAT_HOME}/lib">
<include name="**/*.jar"/>
</fileset>
</classpath>
</available>
</and>
</condition>
<echo>tomcat.available=${tomcat.available}</echo>
</target>

这里声明了,当且仅当env.TOMCAT_HOME已经定义,且要调用的类org.apache.tomcat.startup.Tomcat位于TOMCAT目录下的classpath时,特性tomcat.available才会被设置成true。由于<and>的捷径效应,第一个测试失败时不运行第二个,因为第一个失败时,classpath是不合法的。
      这个测试可以用语带条件的任务,或者当一个程序必须存在时,带条件的<fail>任务将被用于立即终止构建。当找不到TOMCAT时,通过使用将目标依赖于检验目标,且将条件设置为tomcat.available特性而选择了简单的略过测试:
<target name="stop-tomcat"
if="tomcat.available"
depends="validate-tomcat"
description="stop tomcat if it is running">
<java classname="org.apache.tomcat.startup.Tomcat">
<classpath>
<fileset dir="${env.TOMCAT_HOME}/lib">
<include name="**/*.jar"/>
</fileset>
</classpath>
<arg value="-stop"/>
<!— 传递给JAVA命令行的参数的定义 -->
<sysproperty key="tomcat.home"
value="${env.TOMCAT_HOME}"/>
</java>
</target>

12.       设置超时
Ant1.5为<java>任务扩展了timeout属性,允许以毫秒的方式指定一个JAVA应用程序最大的运行时间。只能在建立新进程的JVM中使用这一个特性。

第二部分:使用<exec>启动本地程序
      JAVA执行并不能给予构建文件访问底层操作系统的全部能力,或者访问本地平台的构建步骤,除非JAVA程序调用本地程序。由于本地程序的移植性很差,因此为了实现以跨平台的方式定制任务,应提供可移植性的包装方式。
      在运行程序之前让构建文件去探测这些程序存在与否(用上一节的知识)。这是个功能强大的技巧,就如同多数与维护相关的编码,通过不懈的努力能满足绝大部分的情形。
      要在Ant内运行一个外部程序,应该使用<exec>任务,它可以执行以下操作:
l         指定程序名称和要传入的参数
l         命名运行目录
l         使用failonerror标志来控制当应用程序失败时是否终止构建
l         指定一个最大程序持续时间,时间超过则终止程序。任务在这时被认为是失败,但是至少构建会终止,而不是挂起,这对于自动构建是很重要的。
l         将输出存入一个文件或特性
l         指定JAVA调用本地程序时需要预先设定的环境变量
有一件事是它所做不到的,那就是使用一个OsFamily标志将操作限制到操作系统家族,诸如Windows或者Unix,否则将是非常方便的。<condition>任务确实拥有一个OsFamily测试可用于具有条理性的操作系统测试,但是这样的话整个目标都变成有条件的。
将<exec>与特定的操作系统绑定在一起是不好的做法,除非这个调用直接对应操作系统的底层功能。而检测相应程序,如果存在就调用它则好的多。
例子:
<exec executable="ln">
<arg value="-s"/>
<arg location="execution.xml"/>
<arg location="symlink.xml"/>
</exec>
这个任务的用途是给文件创建符号连接,因为内置JAVA命令没有这个功能。如果可执行程序在当前路径或者在系统path目录中,则无需指定其完整路径。
1.       设置环境变量:
正如<java>任务支持将系统属性作为嵌套元素一样,<exec>任务允许<env>子元素设置环境变量。它与<sysproperty>元素具有相同的语法,以不同的元素名分隔。<exec>的一个特别的功能,是你可以选择程序是否继承当前的环境。通常情况下继承当前的所有设置是有意义的,诸如PATH和TEMP,但是有时候可能希望通过参数进行绝对控制:
<exec executable="preprocess"
newenvironment="true" >
<env key="PATH" path="${dist.dir}/win32;${env.PATH}"/>
<env key="TEMPLATE" file="${src.dir}/include/template.html"/>
<env key="USER" value="self"/>
</exec>

即使通过newenvironment=”false”(默认值)继承已有的环境,任何显示定义的环境变量也将覆盖所传入环境变量的值。

2.       处理错误
<exec>属于这样一类Ant任务:这类任务默认情况下failonerror=”false”。这是有原因的:最初没有返回值校验,因此当它被实现时,校验将被设为false以避免与已有构建冲突。即使JAVA任务与其他多数任务不同,但它至少和本地执行任务有统一的默认值。
      声明failonerror为true和false,而忽略其默认值,这样使构建文件可读性好。”什么情况下出错就终止”/”什么情况下出错但不终止”。
      failonerror并不控制在程序运行时系统对失败如何反映,在Ant 1.5中,<exec>加入了第二个失败检查,failIfExecuteFails,它控制了实际的执行失败是否被忽略。
3.       处理超时
运行超时杀死任务,以免陷入挂起。<exec>支持一个timeout属性,以毫秒为单位。如果设置了该属性则一个监视计时器就会启动运行。当外部程序占用时间超时后就“杀死”它。当超时发生时,监视器不会明确告诉你超时发生了,但是执行文件的返回代码被设为”1”。如果failonerror被设置,则将会终止构建。如果没有设置,那么它就会悄悄的不被忽略。
<target name="sleep-fifteen-seconds" >
<echo message="sleeping for 15 seconds" />
<exec executable="sleep"
failonerror="true"
timeout="2000">
<arg value="15" />
</exec>
</target>
运行该目标,当遇到超时就会产生一个错误:
sleep-fifteen-seconds:
[echo] sleeping for 15 seconds
[exec] Timeout: killed the sub-process
BUILD FAILED
execution.xml:18: exec returned: 1

如果外部程序设置成将结果传入一个特性,并将failonerror设置为off,那么就没办法将为1的结果和超时区分开。
需要在构建中插入一个暂停时,可以使用<sleep>任务,该任务可以工作在所有平台上。

4.       运行shell命令
使用:
<exec executable="cmd" failonerror="true"/>
<arg line="/c echo hello &gt; hello.txt"/>
</exec>
而不是
<exec command="/c echo hello &gt; hello.txt" failonerror="true"/>
</exec>

在shell中也是一样:应该使用
<exec command="ps -ef | grep java &gt; processes.txt"
failonerror="false"/>

而不是
<exec executable="sh" failonerror="true"/>
<arg line="-c 'ps -ef | grep java &gt; processes.txt'"/>
</exec>

因为整个一行的内容需要shell来解释。

5.       在程序被调用之前进行探测
有时候当程序不可用时,可以略过一个构建步骤,或者提供有帮助的错误并终止。如果你知道程序所在的位置,就可以调用<available>任务对其进行测试,但是假如只是要求该程序在指定目录中怎么办呢?<available>任务可以在整个文件路径下搜索指定的文件,因此探测一个程序是否存在就是个简单的事情了,只要沿着环境变量PATH查找程序名字就可以了,当然,在跨平台的方式下,这并不简单:MS-DOC和UNIX系统以不同的方式命名可执行文件,有时甚至连路径变量也不同。将这些纳入考虑之中,对文件的探测就变成了一个多情况的测试,测试必须以带.exe和不带.exe扩展名两种方式来寻找可执行文件,并且MS-DOC/Windows可执行文件必须同时通过环境变量的两个选项进行搜索。Path和PATH:
<target name="probe_for_gcc" >
<condition property="found.gcc">
<or>
<available file="gcc" filepath="${env.PATH}" />
<available file="gcc.exe" filepath="${env.PATH}" />
<available file="gcc.exe" filepath="${env.Path}" />
</or>
</condition>
</target>

可以通过编写依赖的目标,如果程序不存在,要么使用<fail>任务,要么仅仅跳出某个执行步骤:
<target name="compile_cpp" depends="verify_gcc" if="found.gcc">
<exec executable="gcc" ... />
</target>

第三部分: 使用<apply>进行批量运行
将一个文件列表传递给外部可执行文件程序的特性问题,可以使用<apply>任务来解决,这个任务接受一个文件集并将其传递给指定的应用程序,既可以一次传完,也可以逐个传递。
      <apply>是作为<exec>的一个子类而被实现,所以<exec>任务的所有属性,都可以用于<apply>;除此之外,它还包括一个批处理的附加功能。
例如有一个本地程序,可以将XML文件转换为PDF,它有两个参数:XML文件的路径,以及对应的PDF文件的路径:
<apply executable="cmd" dest="docs">
<arg line="/c echo"/>
<arg value="convert"/>
<srcfile/>
<targetfile/>
<fileset dir ="." includes="*.xml"/>
<mapper type="glob" from="*.xml" to="*.pdf"/>
</apply>

在Windows平台上运行,并使用内置echo命令,必须将可执行文件设置为cmd以便echo可以正常工作,打开/c开关使得命令行shell在echo完成后自动退出。对于同一个目录下数个xml文件的执行,输出如下:
[apply] convert C:\AntBook\Sections\Learning\callingotherprograms\apply.xml
C:\AntBook\Sections\Learning\callingotherprograms\docs\apply.pdf
[apply] convert C:\AntBook\Sections\Learning\callingotherprograms\execution.
xml C:\AntBook\Sections\Learning\callingotherprograms\docs\execution.pdf
[apply] convert C:\AntBook\Sections\Learning\callingotherprograms\java.xml C
:\AntBook\Sections\Learning\callingotherprograms\docs\java.pdf
[apply] convert C:\AntBook\Sections\Learning\callingotherprograms\probes.xml
C:\AntBook\Sections\Learning\callingotherprograms\docs\probes.pdf
[apply] convert C:\AntBook\Sections\Learning\callingotherprograms\shells.xml
C:\AntBook\Sections\Learning\callingotherprograms\docs\shells.pdf

到目前为止,它所做的全部工作就是显示我们要执行的命令,但是并没有实际执行它。
<apply>隐含依赖关系检查。目标文件新于源文件,它就被忽略。

一旦对echo的输出结果满意,看到它为每个文件将执行预期的命令行,就可以把属性值由convert修改为executable,去掉/c echo参数,然后再回到正题。

该任务的parallel选项的意思是”一次传入全部的文件”,而不是”并行执行该任务多次”。

第四部分:处理输出:
以上介绍的三个任务:<java>,<exec>和<apply>这三个任务,都允许你使用output参数将输出结果保存到文件中,可以将这个文件传入别的程序,或者一个ant任务,其中的两个任务<exec>和<apply>,还可以将调用的值存入特性,该特性接下来可以被扩展应用为其他任务的参数。例如:可以将构建阶段的结果发email给其他人:
<exec executable="unregbean" output="beans.txt" >
<arg value="-d"/>
</exec>
<mail from="build" tolist="operations"
subject="list of installed beans for ${user.name}"
failonerror="false"
files="beans.txt"/>
这种将生成的文件和报告用email发出去的办法,在自动化的构建和测试系统中是常见的功能。

你可能感兴趣的:(eclipse,jvm,tomcat,log4j,ant)