用Ant构建Java项目
Ant是一个开放源代码的构建工具,专门用于构建Java项目。
Ant入门的最容易的方法就是编写一个构建文件------build.xml。当Ant运行时,会在当前目录下寻找名为build.xml的文件。
编写Ant构建文件:
第一步:定义项目(project)
在项目根目录下创建build.xml文件。第一行指明其为XML格式,然后在第二行定义项目。
<?xml version="1.0"?>
<project name="dms" default="compile" basedir=".">
一个Ant构建文件定义一个项目,使用<project>元素的name属性命名项目,将default属性设为compile,告诉Ant当你在命令行键入ant而无其他参数时,将执行构建过程的compile步骤。我们稍候将定义此步骤;将basedir属性设为".",告诉Ant,该构建文件内的全部其他路径都是相对于build.xml文件所在路径而言的。
第二步:定义性质(property)
下一步,为项目的每一个目录定义性质,以便让Ant了解项目的目录结构。
<property name="build.dir" location="build"/>
<property name="build.prod.dir" location="${build.dir}/prod"/>
<property name="build.test.dir" location="${build.dir}/test"/>
<property name="doc.dir" location="doc"/>
<property name="index.dir" location="index"/>
<property name="src.dir" location="src"/>
<property name="test.dir" location="test"/>
<property name="vendor.lib.dir" location="vendor/lib"/>
location属性所指的目录相对于<project>元素定义的basedir属性值(基目录)。
使用Ant性质命名相对目录有两个优点。首先,当Ant运行构建文件时,会定位build.xml文件所在目录的全部相对目录。第二个好处是,性质可以使构建文件易于维护。例如用build.dir性质指代build目录,当以后决定更改build目录为classes时,只要修改build.dir性质的location属性值即可。在构建文件中所有引用build.dir性质的地方,都将会自动指向新的classes目录。
第三步:定义路径(path)
下面定义表示项目Java类路径的路径。
<path id="project.classpath">
<pathelement location="${build.prod.dir}" />
<pathelement location="${build.test.dir}" />
<fileset dir="${vendor.lib.dir}">
<include name="*.jar"/>
</fileset>
</path>
<path>元素创建路径project.classpath。我们将在构建过程的编译步骤用到此路径。
首先,用<pathelement>元素向路径加入两个构建目录,以便Java编译器可以找到所有的类文件,然后使用<fileset>元素,加入所有厂家.jar文件至此路径,使Java编译器可以找到我们用到的厂家类。
第四步:定义目标(target)
接着我们定义首个构建步骤------称为“目标”。该目标创建编译步骤将要存放Java类文件的目录。
<target name="prepare">
<mkdir dir="${build.prod.dir}"/>
<mkdir dir="${build.test.dir}"/>
</target>
目标只是定义了一系列任务按照指定的顺序执行的有名字的构建步骤。prepare目标使用<mkdir>任务创建构建输出目录,这与在命令行上执行mkdir是等效的。但前者可以工作于任何操作系统下,不会因为目录已经存在而失败,而且必要时会创建父目录。
第五步:定义编译步骤
终于我们可以定义编译步骤了。他将编译所有的产品源文件,将结果类文件放到prepare目标所生成的目录下。
<target name="compile" depends="prepare">
<javac srcdir="${src.dir}" destdir="${build.prod.dir}">
<classpath refid="project.classpath" />
</javac>
</target>
通过目标的depends属性,可以生成目标间的依存关系,从而指定目标顺序。该属性值是一个目标名间用逗号隔开的清单。
例如,compile目标使用depends属性来声明与prepare目标的依存关系,这意味着要运行compile目标必须首先运行prepare目标,以在编译源文件前生成要求的目录。与shell脚本或批处理文件总是顺序执行命令相比,这是一个很大的优点。
prepare目标运行后,compile目标运行<javac>任务,从而调用Java编译器。编译器处理src.dir性质所指目录下的全部Java源文件,在build.prod.dir性质所指目录下生成对应的类文件。
<classpath>元素告诉编译器使用project.classpath代表的值作为类路径。
下面是完整的构建文件:
<? xml version="1.0" ?>
<!--
Excerpted from the book, "Pragmatic Project Automation"
ISBN 0-9745140-3-9
Copyright 2004 The Pragmatic Programmers, LLC. All Rights Reserved.
Visit www.PragmaticProgrammer.com
-->
< project name ="dms" default ="compile" basedir ="." >
< property name ="build.dir" location ="build" />
< property name ="build.prod.dir" location ="${build.dir}/prod" />
< property name ="build.test.dir" location ="${build.dir}/test" />
< property name ="doc.dir" location ="doc" />
< property name ="index.dir" location ="index" />
< property name ="src.dir" location ="src" />
< property name ="test.dir" location ="test" />
< property name ="vendor.lib.dir" location ="vendor/lib" />
< path id ="project.classpath" >
< pathelement location ="${build.prod.dir}" />
< pathelement location ="${build.test.dir}" />
< fileset dir ="${vendor.lib.dir}" >
< include name ="*.jar" />
</ fileset >
</ path >
< target name ="prepare" >
< mkdir dir ="${build.prod.dir}" />
< mkdir dir ="${build.test.dir}" />
</ target >
< target name ="compile" depends ="prepare" >
< javac srcdir ="${src.dir}" destdir ="${build.prod.dir}" >
< classpath refid ="project.classpath" />
</ javac >
</ target >
</ project >
运行构建:
在命令行环境中,切换至build.xml所在的目录,运行Ant:
D:\MyProject> ant
运行Ant前,请确认ANT_HOME环境变量指向你的Ant安装目录,且PATH环境变量中加入了%ANT_HOME%\bin。
Ant读取build.xml,以预先定义的顺序执行构建步骤(目标)。
--------------------------------------------------------------------续------------------------------
这一篇讲述如何自动运行JUnit测试进行自动化测试。我们将JUnit测试放在test目录。
第一步:编译测试
首先,在build.xml文件里定义一个单独的构建目标,以便编译测试源文件。
<target name="compile-tests" depends="compile">
<javac srcdir="${test.dir}" destdir="${build.test.dir}">
<classpath refid="project.classpath" />
</javac>
</target>
compile-tests目标用<javac>任务在test.dir目录编译测试源文件,并将结果类文件放入build.test.dir目录,用project.classpath路径作为测试代码的类路径。
第二步:运行测试
在build.xml文件里定义构建目标,使所有的JUnit测试自动运行。
<target name="test" depends="compile-tests">
<junit haltonfailure="true">
<classpath refid="project.classpath" />
<formatter type="brief" usefile="false" />
<batchtest>
<fileset dir="${build.test.dir}"
includes="**/*Test.class" />
</batchtest>
<sysproperty key="doc.dir" value="${doc.dir}" />
<sysproperty key="index.dir" value="${index.dir}" />
</junit>
</target>
test目标的depends属性生成与刚定义的compile-tests目标的依存关系。
如果构建成功,意味着所有的东西不仅都编译过,还都通过了测试。
将haltonfailure属性设为true会使构建随着任何测试失败而失败。
这里我们再次用project.classpath定义了运行JUnit测试的类路径。
下面定义了一个输出格式化器,在<formatter> 元素中,使用brief类型来输出每个运行的测试用例的名称及其统计信息,仅当测试失败时才有更详细的信息(plain类型默认比brief类型输出信息多一些;xml类型将测试结果以XML格式输出)。将usefile属性值设为false会将测试结果送往控制台,而不是写入文件。
<batchtest>任务收集从封闭<fileset>元素中返回的所有测试,并自动创建包含了所有测试的测试suite。我们将对所有的测试类用*Test.java命名约定取名,这些文件将编译成*Test.class文件,放在<fileset>的build.test.dir性质指向的目录中。
最后,<sysproperty>元素定义系统性质为一个键值对,这些性质可以在测试代码中访问。这里,测试需要知道从哪里找到文档,以及向哪里放入文档的索引结果,在运行测试时我们将传递这些结果,例如,测试在运行时查看送入的doc.dir系统性质来定位项目目录结构中doc目录的绝对路径。
要运行测试,请执行test目标:
$ ant test
最后,打扫战场
我们可以定义一个删除所有构建输出的Ant目标。
<target name="clean">
<delete dir="${build.dir}" />
</target>
clean目标使用<delete>任务来删除build.dir所指的目录。要删除目录,请运行clean目标:
$ ant clean
冲掉这些构建输出有积极的作用:从头开始生成构建,可以发现可能被累积构建掩盖的错误。
完整的build.xml:
<?xml version="1.0"?>
<!--
Excerpted from the book, "Pragmatic Project Automation"
ISBN 0-9745140-3-9
Copyright 2004 The Pragmatic Programmers, LLC. All Rights Reserved.
Visit www.PragmaticProgrammer.com
-->
<project name="dms" default="compile" basedir=".">
<property name="build.dir" location="build"/>
<property name="build.prod.dir" location="${build.dir}/prod"/>
<property name="build.test.dir" location="${build.dir}/test"/>
<property name="doc.dir" location="doc"/>
<property name="index.dir" location="index"/>
<property name="src.dir" location="src"/>
<property name="test.dir" location="test"/>
<property name="vendor.lib.dir" location="vendor/lib"/>
<path id="project.classpath">
<pathelement location="${build.prod.dir}" />
<pathelement location="${build.test.dir}" />
<fileset dir="${vendor.lib.dir}">
<include name="*.jar"/>
</fileset>
</path>
<target name="prepare">
<mkdir dir="${build.prod.dir}"/>
<mkdir dir="${build.test.dir}"/>
</target>
<target name="compile" depends="prepare">
<javac srcdir="${src.dir}" destdir="${build.prod.dir}">
<classpath refid="project.classpath" />
</javac>
</target>
<target name="compile-tests" depends="compile">
<javac srcdir="${test.dir}" destdir="${build.test.dir}">
<classpath refid="project.classpath" />
</javac>
</target>
<target name="test" depends="compile-tests">
<junit haltonfailure="true">
<classpath refid="project.classpath" />
<formatter type="brief" usefile="false" />
<batchtest>
<fileset dir="${build.test.dir}"
includes="**/*Test.class" />
</batchtest>
<sysproperty key="doc.dir" value="${doc.dir}" />
<sysproperty key="index.dir" value="${index.dir}" />
</junit>
</target>
<target name="clean">
<delete dir="${build.dir}" />
</target>
</project>
下面再举一个实际项目中用到的构建文件的例子:
<?xml version="1.0" ?>
<project name="JSBook" default="compile" basedir=".">
<property environment="env" />
<property name="src" value="WEB-INF/src" />
<property name="classes" value="WEB-INF/classes" />
<property name="lib" value="WEB-INF/lib" />
<property name="dist" value="dist" />
<path id="task.classpath">
<pathelement location="${classes}" />
<pathelement location="${lib}" />
<!-- Tomcat 5.0.16 Servlet 2.4 API -->
<pathelement location="${lib}/servlet-api.jar" />
<!-- Tomcat 5.0.16 JSP 2.0 API -->
<pathelement location="${lib}/jsp-api.jar" />
</path>
<target name="init" >
<echo>Init Complete !</echo>
<echo>ant home = ${ant.home} </echo>
<echo>java home = ${java.home} </echo>
<echo>user home = ${user.home} </echo>
</target>
<target name="compile" depends="init" >
<javac classpathref="task.classpath" srcdir="${src}" destdir="${classes}" />
<echo level="verbose">Seen with -verbose</echo>
<echo level="debug">Seen with -debug</echo>
<echo>Compilation Complete !</echo>
</target>
<target name="war" >
<jar jarfile="${dist}/JSPBook.war" basedir="."/>
</target>
</project>