说他无所不能,好像有点夸张,但是用过Ant之后,感觉真的是只有想不到没有作不到.Ant,原作者选择他作为软件名字的意思是指"令一个简洁的工具"(Another Neat Tool),而这个真正的名字现在去很少为人所知,但这丝毫不影响他成为最优秀的构建工具.
现在开始我将进入一个"蚂蚁"的世界,通过例子,真真正正去了解他!
文章参考资料可以到http://www./antbook去下载
Ant的最好学习资料<<使用Ant进行Java开发>>
Ant的官方网站: http://ant./
Ant的最新版本:Ant 1.6.5
本文所有的例子运行的环境:JDK1.4.2,Ant1.6.2,eclipse3.0
一.使用Ant运行Java程序
我们先从简单的Hello学起,目录结构如下
project--
|src--
| |--org.ant.chapter1.Hello
|bin
|build.xml
以后的例子大多采用此目录结构,特例会额外声明
build.xml文件
<?xml version="1.0"?> <project name="project" default="run"> <target name="compile"> <javac destdir="bin" srcdir="src"></javac> </target> <target name="run" depends="compile"> <java classname="org.ant.chapter1.Hello"> </java> </target> </project> |
从结构来看构建文件很简单,里面的内容大家也一定能够看得懂,可以看出Ant的核心任务就是target,一个Ant文件有多个target组成,而这些target之间,又有相互的依赖关系--depends,运行的时候默认运行project中指定的target.
javac--编译java文件 java--运行java文件
使用eclipse中集成的Ant运行build.xml文件(当然,也可以将ANT_HOME加到Path中,在命令行中运行)
Buildfile: D:\MyEclipse\workspace\sad\build.xml compile: run: [java] Working directory ignored when same JVM is used. [java] Could not find org.ant.chapter1.Hello. Make sure you have it in your classpath [java] at org.apache.tools.ant.taskdefs.ExecuteJava.execute(ExecuteJava.java:166) [java] at org.apache.tools.ant.taskdefs.Java.run(Java.java:705) [java] at org.apache.tools.ant.taskdefs.Java.executeJava(Java.java:177) [java] at org.apache.tools.ant.taskdefs.Java.execute(Java.java:83) [java] at org.apache.tools.ant.UnknownElement.execute(UnknownElement.java:275) [java] at org.apache.tools.ant.Task.perform(Task.java:364) [java] at org.apache.tools.ant.Target.execute(Target.java:341) [java] at org.apache.tools.ant.Target.performTasks(Target.java:369) [java] at org.apache.tools.ant.Project.executeTarget(Project.java:1214) [java] at org.eclipse.ant.internal.ui.antsupport.InternalAntRunner.run(InternalAntRunner.java:379) [java] at org.eclipse.ant.internal.ui.antsupport.InternalAntRunner.main(InternalAntRunner.java:135) BUILD SUCCESSFUL Total time: 703 milliseconds
|
,java入门的经典错误,ClassNotDefException,可见是classpath设置问题,而观察得到compile成功运行,所以我们在run-target里面加入classpath的配置
<?xml version="1.0"?> <project name="project" default="run"> <target name="compile"> <javac destdir="bin" srcdir="src"></javac> </target> <target name="run" depends="compile"> <java classname="org.ant.chapter1.Hello"> <classpath path="bin"></classpath> </java> </target> </project> |
运行
Buildfile: D:\MyEclipse\workspace\sad\build.xml compile: run: [java] Hello World! BUILD SUCCESSFUL Total time: 672 milliseconds |
成功!!第一个Ant应用完成,有人会说:用IDE运行岂不是更简单,但是你要知道运行java程序只是Ant的一个小小的功能,后面我们会看到Ant的更强大的功能!
下一篇文章将介绍java程序运行的扩展及用Ant运行tomcat!
java程序运行的扩展
1.带有参数的应用程序运行
Ant在<java>任务中提供了<arg>元素,<arg>有四种属性value,file,line,path
public class Hello { public static void main(String[] args) { System.out.println("Hello " + args[0]); File file = new File(args[1]); String[] filename = file.list(); for(int i = 0; i < filename.length; i++) { System.out.println(filename[i]); } } } |
build.xml
<?xml version="1.0"?> <project name="project" default="run"> <property name="run.classpath" value="bin"></property> <target name="compile"> <javac destdir="bin" srcdir="src"></javac> </target> <target name="run" depends="compile"> <java classname="org.ant.chapter1.Hello"> <classpath path="${run.classpath}"></classpath> <arg value="Ant"/> <arg file="D:\rag"/> </java> </target> </project> |
Buildfile: D:\MyEclipse\workspace\sad\build.xml compile: run: [java] Hello Ant [java] hello.dat BUILD SUCCESSFUL Total time: 734 milliseconds |
2.控制新的JVM
一般的<java>任务都运行在当前的JVM中,单一些特定的情况下将Ant运行在新的JVM下面,这时只需要将<java>中的一个属性fork设置为true就可以了.
我们知道,java命令提供了许多的运行参数,用于指定JVM的属性,同样在Ant也提供相应的属性,看例子:
<?xml version="1.0"?> <project name="project" default="run"> <property name="run.classpath" value="bin"></property> <property name="Search.JVM.extra.args" value="-Xincgc"></property> <target name="compile"> <javac destdir="bin" srcdir="src"></javac> </target> <target name="run" depends="compile"> <java classname="org.ant.chapter1.Hello" fork="true" maxmemory="64m"> <classpath path="${run.classpath}"></classpath> <jvmarg line="${Search.JVM.extra.args}"/> <arg value="Ant"/> <arg file="D:\rag"/> </java> </target> </project> |
3.运行jar文件,使用failonerror处理错误
<java>同样提供了运行jar文件的属性
MANIFEST.MF
Mainfest-Version: 1.0 Created-By: myth Sealed: false Main-Class: org.ant.chapter1.Hello
|
build.xml
<?xml version="1.0"?> <project name="project" default="run"> <property name="run.classpath" value="bin"></property> <property name="Search.JVM.extra.args" value="-Xincgc"></property> <target name="compile"> <javac destdir="bin" srcdir="src"></javac> </target> <target name="jar" depends="compile"> <jar destfile="test.jar" update="true" manifest="MANIFEST.MF"> <fileset dir="bin"> <include name="**/*.class"/> </fileset> </jar> </target> <target name="run" depends="jar"> <java fork="true" maxmemory="64m" jar="test.jar"> <classpath path="${run.classpath}"></classpath> <jvmarg line="${Search.JVM.extra.args}"/> <arg value="Ant"/> <arg file="D:\rag"/> </java> </target> </project> |
Buildfile: D:\MyEclipse\workspace\sad\build.xml compile: jar: [jar] Updating jar: D:\MyEclipse\workspace\sad\test.jar run: [java] Hello Ant [java] hello.dat BUILD SUCCESSFUL Total time: 875 milliseconds |
在某些情况下,我们不希望由于一些不重要的任务构建失败,而导致整个构建的失败,所以Ant提供了一个共同的属性-failonerror,多数任务的默认值为failonerror="true",既当此任务构建失败时,失败信息会传递给控制台,并导致build failed,failonerror只支持在新的JVM里运行.
<target name="run" depends="jar"> <java fork="true" maxmemory="64m" jar="test.jar" failonerror="false" > <classpath path="${run.classpath}"></classpath> <jvmarg line="${Search.JVM.extra.args}"/> <arg value="Ant"/> <arg file="D:\rag"/> </java> </target> |
Buildfile: D:\MyEclipse\workspace\sad\build.xml compile: jar: run: [java] java.lang.NullPointerException [java] at org.ant.chapter1.Hello.main(Hello.java:27) [java] Hello Ant [java] Exception in thread "main" [java] Java Result: 1 BUILD SUCCESSFUL Total time: 984 milliseconds |
可以看出虽然run构建失败,但是Ant还是执行了,原来的jar文件,并且BUILD SUCCESSFUL!!
使用Ant运行tomcat
Ant使用<exec>任务运行本地程序,先看一个例子:
<?xml version="1.0"?> <project name="project" default="run"> <target name="run"> <exec executable="cmd"> <arg value="/C a.bat"/> </exec> </target> </project> |
a.bat
@echo off echo Hello >> a.txt |
运行完后,会在根目录生成a.txt文件,里面内容为Hello
下面我们来运行tomcat
<?xml version="1.0"?> <project name="project" default="tomcat-start"> <property name="tomcat.dir" value="c:/Tomcat5"></property> <target name="tomcat-start"> <exec dir="${tomcat.dir}/bin" executable="cmd"> <env key="CATALINA_HOME" path="${tomcat.dir}"/> <arg value="/C startup.bat"/> </exec> </target> <target name="tomcat-stop"> <exec dir="${tomcat.dir}/bin" executable="cmd"> <env key="CATALINA_HOME" path="${tomcat.dir}"/> <arg value="/c shutdown.bat"/> </exec> </target> </project> |
四.使用Ant进行Junit测试
我们除了使用java来直接运行junit之外,我们还可以使用junit提供的junit task与ant结合来运行。涉及的几个主要的ant task如下:
l <junit>,定义一个junit task
l <batchtest>,位于<junit>中,运行多个TestCase
l <test>,位于<junit>中,运行单个TestCase
l <formatter>,位于<junit>中,定义一个测试结果输出格式
l <junitreport>,定义一个junitreport task
l <report>,位于<junitreport>中,输出一个junit report
运行Junit需要jakarta-ant-1.4-optional.jar和Junit.jar包,因为这两个包用于支持ant task--<junit>的,所以不能在build.xml文件中加载,需要将他们放到ANT_HOME中去.使用eclipse可以按照一下步骤加入:
Windows-Preference-Ant-Runtime-Ant Home Entries
下面看一个Junit测试的例子:
<?xml version="1.0"?> <project name="project" default="junit"> <property name="run.classpath" value="bin"></property> <property name="run.srcpath" value="src"></property> <property name="test.srcpath" value="test"></property> <property name="lib.dir" value="lib"/> <path id="compile.path"> <pathelement location="${lib.dir}/junit-3.8.1.jar"/> <pathelement location="${lib.dir}/log4j-1.2.8.jar"/> </path> <target name="compile"> <javac destdir="${run.classpath}" srcdir="${run.srcpath}" classpathref="compile.path"/> <javac destdir="${run.classpath}" srcdir="${test.srcpath}" classpathref="compile.path"/> </target> <target name="junit" depends="compile"> <junit printsummary="true"> <classpath path="${run.classpath}"></classpath> <test name="org.ant.test.Test1"></test> </junit> </target> </project>
|
可以看出Junit的使用基本和java差不多, printsummary允许输出junit信息,当然Ant提供formatter属性支持多样化的junit信息输出.Ant包含三种形式的formatter:
brief:以文本格式提供测试失败的详细内容;
plain:以文本格式提供测试失败的详细内容以及每个测试的运行统计;
xml:以xml格式提供扩展的详细内容,包括正在测试时的Ant特性,系统输出,以及每个测试用 例的系统错误.
使用formatter时建议将printsummary关闭,因为他可能对formatter的生成结果产生影响,并多生成一份同样的输出.当然我们可以使用formatter将输出结果显示在console中:
<formatter type="brief" usefile="false"/>
Junit支持多个formatter同时存在:
<formatter type="brief" usefile="false"/>
<formatter type="xml"/>
使用xml我们可以得到扩展性更强的信息输出,这时在<test>中要设定todir来指定xml的输出路径.
在通常情况下我们不可能一个一个来处理junit,所以Ant提供了<batchtest>,可以在他里面嵌套文件集(fileset)以包含全部的测试用例.
对于大量的用例,使用控制台输出,或者使用文件或xml文件来作为测试结果都是不合适的,Ant提供了<junitreport>任务使用XSLT将xml文件转换为HTML报告.该任务首先将生成的XML文件整合成单一的XML文件,然后再对他进行转换,这个整合的文件默认情况下被命名为:TESTS-TestSuites.xml.
<junitreport todir="${test.xml}"> <fileset dir="${test.xml}"> <include name="TEST-*.xml"/> </fileset> <report format="frames" todir="${test.report}"/> </junitreport>
|
<report>元素指示转换过程中生成有框架(frames)或者无框架的类似与javadoc格式的文件,并保存到todir所在的目录下面.(由于xalan对于JDK1.4以上的版本存在问题,所以要生成HTML文件需要以下步骤:现在最新的xalan,在%JAVA_HOME%/jre/lib中建立文件夹endorsed.将xalan中的jar文件copy到里面).
下面看一个完整的例子:
<?xml version="1.0"?> <project name="project" default="junit"> <property name="run.classpath" value="bin"></property> <property name="run.srcpath" value="src"></property> <property name="test.srcpath" value="test"></property> <property name="test.xml" value="xml"></property> <property name="test.report" value="report"></property> <property name="lib.dir" value="lib"/> <path id="compile.path"> <pathelement location="${lib.dir}/junit-3.8.1.jar"/> <pathelement location="${lib.dir}/log4j-1.2.8.jar"/> </path> <target name="init"> <delete dir="${test.report}"/> <mkdir dir="${test.report}"/> <delete dir="${test.xml}"/> <mkdir dir="${test.xml}"/> </target> <target name="compile" depends="init"> <javac destdir="${run.classpath}" srcdir="${run.srcpath}" classpathref="compile.path"/> <javac destdir="${run.classpath}" srcdir="${test.srcpath}" classpathref="compile.path"/> </target> <target name="junit" depends="compile"> <junit printsummary="false"> <classpath path="${run.classpath}"></classpath> <formatter type="xml"/> <batchtest todir="${test.xml}"> <fileset dir="${run.classpath}"> <include name="**/Test*.class"/> </fileset> </batchtest> </junit> <junitreport todir="${test.xml}"> <fileset dir="${test.xml}"> <include name="TEST-*.xml"/> </fileset> <report format="frames" todir="${test.report}"/> </junitreport> </target> </project> <?xml version="1.0"?> <project name="project" default="junit"> <property name="run.classpath" value="bin"></property> <property name="run.srcpath" value="src"></property> <property name="test.srcpath" value="test"></property> <property name="test.xml" value="xml"></property> <property name="test.report" value="report"></property> <property name="lib.dir" value="lib"/> <path id="compile.path"> <pathelement location="${lib.dir}/junit-3.8.1.jar"/> <pathelement location="${lib.dir}/log4j-1.2.8.jar"/> </path> <target name="init"> <delete dir="${test.report}"/> <mkdir dir="${test.report}"/> <delete dir="${test.xml}"/> <mkdir dir="${test.xml}"/> </target> <target name="compile" depends="init"> <javac destdir="${run.classpath}" srcdir="${run.srcpath}" classpathref="compile.path"/> <javac destdir="${run.classpath}" srcdir="${test.srcpath}" classpathref="compile.path"/> </target> <target name="junit" depends="compile"> <junit printsummary="false"> <classpath path="${run.classpath}"></classpath> <formatter type="xml"/> <batchtest todir="${test.xml}"> <fileset dir="${run.classpath}"> <include name="**/Test*.class"/> </fileset> </batchtest> </junit> <junitreport todir="${test.xml}"> <fileset dir="${test.xml}"> <include name="TEST-*.xml"/> </fileset> <report format="frames" todir="${test.report}"/> </junitreport> </target> </project>
|
生成的文档:
点击Properties超链接会弹出一个窗口显示在测试运行时全部的Ant特性,这对于跟踪由环境和配置造成的失败是非常便利的!
五.使用Ant运行本地程序
1.使用Ant运行windows的批处理文件
要在Ant内运行一个外部程序,应使用<exec>任务.它允许你执行下列操作:
l 指定程序名和要传入的参数.
l 命名运行目录.
l 使用failonerror标志来控制当应用程序失败时是否停止构建.
l 指定一个最大程序持续时间,时间超过则中止程序.任务在这时被认为是失败,但是至少构建会中止,而不是挂起,这对于自动构建是至关重要的.
l 将输出存到一个文件或特性.
l 指定java调用本地程序时需要预先设定的环境变量.
下面来看一个例子:
批处理文件:
Test.bat
@echo off echo Hello > test.txt
|
build.xml
<?xml version="1.0"?> <project name="batch" default="extract" basedir="."> <target name="extract"> <exec executable ="cmd"> <arg line="/c a.bat"/> </exec> </target> </project>
|
使用executable元素标记指定使用的命令,具体用法可以在命令行下面输入help cmd查看.如果你希望在运行批处理发生错误时中止构建需要设定failonerror="on".加入你的外部程序在某个时刻挂起,也许是在与远程站点对话,而你不希望构建永远被挂起,Ant提供了timeout这个属性,他是一个以毫秒为单位的数字.下面看一下如何使用Ant来运行tomcat.
启动tomcat需要两个环境变量CATALINA_HOME, JAVA_HOME,如果你在环境变量中已经设定,在Ant中就不需要进行处理,如果没有需要使用<env>属性来设定,你也可以使用<env>属性覆盖你以前的环境变量.
<?xml version="1.0"?> <project name="batch" default="tomcat-start" basedir="."> <property name="tomcat.dir" value="C:\Tomcat5"></property> <target name="tomcat-start"> <exec dir="${tomcat.dir}/bin" executable="cmd"> <env key="CATALINA_HOME" path="${tomcat.dir}"/> <arg value="/C startup.bat"/> </exec> </target> </project>
|
2.使用Ant运行shell文件
由于windowsXP的cmd默认没有安装ps,bash等命令,所以我们需要借助的三方的软件来实现这个功能,这里使用cgywin,将cgywin的bin目录加到环境变量的Path里面(下面使用Ant运行cvs也会用到).
<?xml version="1.0"?> <project name="batch" default="shell" basedir="."> <property name="tomcat.dir" value="C:\Tomcat5"></property> <target name="shell"> <exec dir="${tomcat.dir}/bin" executable="bash"> <env key="CATALINA_HOME" path="${tomcat.dir}"/> <arg value="startup.sh"/> </exec> </target> </project>
|
3.使用Ant运行cvs
Ant内置cvs属性,可以很方便的使用cvs:
<?xml version="1.0"?> <project name="batch" default="shell" basedir="."> <property name="cvs.root" value="..."></property> <target name="cvs"> <cvs cvsroot="cvs.root" command="checkout ../.."/> </target> </project>
|
如果你的Documents and Settings中有.cvspass文件,那么可以不用设定cvsroot,Ant会自动寻找.
六.工程的打包部署
工程的打包,主要就是文件的操作,下面通过例子简单介绍一下文件的移动,复制和删除.
<?xml version="1.0"?> <project name="project" default="jar"> <target name="copy"> <tstamp> <format property="time.format" pattern="yyyy-mm-dd‘T‘HH:mm:ss" locale="en"/> </tstamp> <copy tofile="dist/readme" file="test.txt"> <filterset> <filter token="TIME" value="${time.format}"/> </filterset> </copy> </target>
<target name="move"> <move todir="dist"> <fileset dir="lib"> <include name="*.jar"/> </fileset> </move> </target> <target name="delete" depends="copy,move"> <delete verbose="true" failonerror="false"> <fileset dir="dist"> <include name="*.jar"/> </fileset> </delete> </target> </project>
|
需要说明的是文件删除的时候可能这个文件正在被别人是用而无法删除,所以要用failonerror来标记,文件的复制是时间戳敏感的,如果拷贝的文件比原文件要老,那么Ant将不会执行copy,解决的办法是将overwrite属性设置为true,由于移动文件是复制文件的一个子类,所以它们的原理基本相同.
前面已经例举过一个jar文件打包的例子,下面主要介绍war文件的打包.Ant提供war文件打包的属性.<war>任务是<jar>任务的子类,但是他也提供了一些特有的属性:
<target name="deploy" depends="init"> <war destfile="${war.dir}/spring.war" webxml="${web.dir}/web.xml" > <classes dir="${web.dir}/classes"></classes> <fileset dir="WebContent" excludes="web.xml"></fileset> <lib dir="${web.dir}/lib"></lib> </war> </target>
|
可以看出war任务提供了许多WEB应用程序的特有属性,只要你指定了这些文件,war任务就会自动将他们放到正确的位置.
部署是项目发布的过程,Ant支持FTP,Email,本地和远程等几种部署模式,但是Ant并不内置对一些部署的支持,需要第三方的库.
optional.jar 也可能是这样的名字: jakarta-ant-1.4.1.optional.jar
netcomponents.jar <ftp>和<telnet>需要
activation.jar <mail>需要
mail.jar <mail>需要
下面只以本地部署为例,服务器为tomcat.
由于tomcat支持热部署,可以将webapp文件下的war文件自解压缩,所以最简单的部署方式是将工程打成war包后直接copy到webapp目录下面.另一种方法是使用tomcat的管理员身份,在manager页面装载和删除应用,这种方法比较复杂,也比较正规,他也是远程部署的基础.
<?xml version="1.0"?> <project name="project" default="deploy-local-catalina"> <property name="war.dir" value="dist"></property> <property name="web.dir" value="WebContent/WEB-INF"></property> <property name="webapp.name" value="spring"></property> <property name="catalina.port" value="8080"></property> <property name="catalina.username" value="admin"></property> <property name="catalina.password" value="admin"></property> <target name="init"> <mkdir dir="${war.dir}"/> </target>
<target name="mkwar" depends="init"> <war destfile="${war.dir}/spring.war" webxml="${web.dir}/web.xml" > <classes dir="${web.dir}/classes"></classes> <fileset dir="WebContent" excludes="web.xml"></fileset> <lib dir="${web.dir}/lib"></lib> </war> </target> <target name="remove-local-catalina"> <property name="deploy.local.remove.url" value="http://localhost:${catalina.port}/manager/remove"></property> <get dest="deploy.local.remove.txt" src="${deploy.local.remove.url}?path=/${webapp.name}" username="admin" password="admin"/> <loadfile property="depoly.local.remove.result" srcfile="depoly.local.remove.txt"></loadfile> </target> <target name="deploy-local-catalina" depends="mkwar, remove-local-catalina"> <property name="deploy.local.urlpath" value="file:///D:\MyEclipse\workspace\springstruts\dist\spring.war"></property> <property name="deploy.local.url.params" value="path=/${webapp.name}&war=${deploy.local.urlpath}"></property> <property name="deploy.local.url" value="http://localhost:${catalina.port}/manager/install"></property> <get src="${deploy.local.url}?${deploy.local.url.params}" dest="deploy-local.txt" username="admin" password="admin"/> <loadfile property="deploy.local.result" srcfile="deploy-local.txt"></loadfile> </target> </project>
|
可以看出只要将上面的localhost换成目标的ip地址就可以实现tomcat的远程部署.
终于完成了Ant初级的学习笔记,感觉到后来没有什么动力,越写越乱,很多地方解释的不清楚,只是给出build文件,然后就一笔带过,在以后的高级篇中,我会更加努力的!!
|