利用
Ant
实现项目自动构建测试备份并发布到项目
web
www.jiedichina.com,南京捷帝科技
Ant
是一个非常好的
java
编译工具。作者说这个东西是不带
make
缺陷的
make
工具(
linux/unix
用户对
make/makefile
应该是很熟悉了吧),这里我们来具体实践一下使用
Ant
进行项目构建管理。
文章开始,我也没有免了俗套,先简要的介绍一下
Ant
。
Ant
是一个
java
的基本构建工具(我这里使用构建这个词,是因为我觉得对于
Ant
来讲,编译只是其中很小的一部分)。
Ant
是使用
java
开发的,但不仅仅是一个只适用于
java
工具,也可以为其他语言提供相映的功能,就像其他的
make
工具一样。而且由于
java
是跨平台的,所以
ant
也是可以跨平台使用的,真正的实现了
java
的一次编写到处运行的。当前
Ant
已经推出了
1.54
版了,这个版本需要
jdk1.1
以上版本的支持。不过我没有具体试验过,一来
jdk1.1
找起来很不容易,二来也没有人会用这个低版本的
jdk
进行开发了,尤其在中国。
Ant
需要用户自己编写一个
build
脚本来对项目进行管理。这个脚本是
xml
格式的文档(近来非常流行使用
xml
文档,至少我看到的
apache.org
上的
java
项目的配置文件都是
xml
格式了,看来我们也的与时俱进呀)。使用任何一种你喜欢的编辑器都可以编写这样的
build
脚本,如果你愿意当然可以使用
windows
自带的记事本来写,没有人会反对。我在这里使用的是
eclipse
。选择他的缘故是因为
eclipse
自身已经集成了
Ant 1.5.3
版本(作为
eclipse
的一个插件),可以很方便的使用,而且集成了
Ant
的编辑器,可以做到代码提示,最重要的原因是我的项目是用
eclipse
来开发的,所以
eclipse
是不二的选择。
在
eclpise
中是不需要配置
Ant
的如果你是在其他的环境中使用
Ant
那么需要配置相应的
path
和
classpath
,将你的
ant_home
下的
lib
文件夹加入
classpath
中,这里包含了
ant
的基本类包。将
ant_home
下的
bin
文件夹加入你的
path
中,这样就可以在
windows
终端窗口使用
ant
工具了。
Ant
的执行是
Ant [
脚本文件
]
这种格式,如果没有指明脚本文件,那么
ant
会在当前文件夹下寻找
build.xml
文件作为当前的脚本文件,如果连这个也找不到就只好报错了。
当然在
eclipse
中是不用这么麻烦的。只要在
eclipse
中运行扩展工具里的
run as
中的
ant build
就可以了。
Ant
会自动去寻找当前项目文件夹中的
build.xml
的。并且以后就可以直接使用了。
基本知识介绍完毕,开始进入正题:
Ant
的
build.xml
的编写。下面以我的
InterfacePackage
项目的开发来进行
build.xml
的解说。
首先大家要记住
build.xml
是
xml
文档(好像是废话,因为文件的扩展名已经告诉我们答案了),所以所编写的
build.xml
必须遵守
xml
文档的规范
,
否则
ant
会毫不留情的报错(计算机就是这样,一点也不给人留面子,每次报错都是直截了当冷酷无情的)。
<!--
这是申明,说明这确实是一个
xml
文档
-- >
<?xml version="1.0" encoding="UTF-8" ?>
<!
—
这里说明了我的项目的文件名
,default
指定的是默认的
task-- >
<project
name="ConsoleViewer" default="dest">
这里我们基础到了几个
ant
的基本概念,就是每一个脚本都是由
project
段内的一些内容为主体的,通过
project
标签我们可以指定项目名,和默认的
task
。
Task
是
ant
中的一个最重要的概念,
ant
通过执行脚本中的一系列
task
来完成工作,所以
task
是可以说是
ant
的灵魂。
<property
name="project.version.info" value="1.1.0" />
<property name="project.name.info" value="InterfacePackage" />
<property name="project.package.type.info" value="jar" />
<property name="project.encoding.info" value="UTF-8" />
<property name="base.dir" value="." />
<property name="src.dir" value="${base.dir}/src" />
<property name="bin.dir" value="${base.dir}/bin" />
<property name="dest.dir" value="${base.dir}/dest" />
<property name="doc.dir" value="${base.dir}/doc" />
<property name="code.dir" value="${base.dir}/code" />
<property name="lib.dir" value="${base.dir}/lib" />
<property name="bak.dir" value="${base.dir}/bak"/>
<property name="juit.report.dir" value="${base.dir}/JUnitReport" />
<property name="web.dir" value="E:/env/deploy/html/ibeyond/${project.name.info}" />
<property name="web.doc.dir" value="${web.dir}/doc" />
<property name="web.code.dir" value="${web.dir}/code" />
<property name="web.src.dir" value="${web.dir}/src" />
<property name="web.src.dir" value="${web.dir}/bin" />
<property name="web.old.dir" value="${web.dir}/old" />
<property name="web.bin.dir" value="${web.dir}/bin" />
<property name="web.report.dir" value="${web.dir}/test/report"/>
以上这些是我定义的一些
property
。何谓
property
呢?就是
ant
脚本的一些属性,而
task
就是
ant
脚本的方法,通过属性和方法的结合完成对象所能完成的工作,看来
ant
也是面向对象的。以上
property
我不需解释大家也肯定应该能看的懂吧
.
其中带
info
后缀的是指信息,带
dir
后缀的当然是指文件夹了。
<path
id="appClassPath">
<pathelement path="${java.class.path}"/>
<fileset dir="${lib.dir}">
<include name="*.jar"/>
</fileset>
<fileset dir="${bin.dir}">
<include name="**/*.class"/>
</fileset>
</path>
先配置一下路径问题,因为是一个
java
项目,当然优先考虑的是
classpath
了。
<target
name="init">
<tstamp />
<delete dir="${dest.dir}" />
<delete dir="${doc.dir}" />
<delete dir="${code.dir}" />
<delete dir="${bin.dir}" />
<mkdir dir="${base.dir}"/>
<mkdir dir="${bin.dir}"/>
<mkdir dir="${dest.dir}"/>
<mkdir dir="${doc.dir}"/>
<mkdir dir="${code.dir}"/>
<mkdir dir="${bak.dir}"/>
<mkdir dir="${juit.report.dir}"/>
<mkdir dir="${juit.report.dir}/xml"/>
<mkdir dir="${web.dir}"/>
<mkdir dir="${web.code.dir}"/>
<mkdir dir="${web.doc.dir}"/>
<mkdir dir="${web.old.dir}"/>
<mkdir dir="${web.src.dir}"/>
<mkdir dir="${web.bin.dir}"/>
<mkdir dir="${web.report.dir}"/>
</target>
首先进行初始化任务,看起来也很简单。就是清空一些文件夹然后建立一些文件夹,这个
target
就是
task
的集合,也就是说一个
target
可以执行多个
task
,也很容易理解为了完成一个目标当然有可以使用多个方法了,这里的
delete
和
mkdir
都是各自独立的
task
。我在第一行写了
<stamp />
这个声明,说他是声明是因为如果你想在你的
build
脚本中使用当前的时间日期的话就必须指定这个,然后在后文中就可以使用
${DSTAMP}
这样
的
标签
了。
<target
name="javadoc" depends="init">
<javadoc packagenames="*"
destdir="${doc.dir}"
sourcepath="${src.dir}"
charset="${project.encoding.info}"
encoding="${project.encoding.info}"
author="true"
version="true"
use="true"
splitindex="true"
windowtitle="${project.name.info} ${project.version.info} API Documentation">
<classpath refid="appClassPath" />
<doctitle>
<![CDATA[ <h1>${project.name} APIs(Version ${project.version})</h1> ]]>
</doctitle>
<bottom>
<![CDATA[ <div algin="center">Copyright © 2002-2003 www.ibeyond.org, All Rights Reserved.</div>]]>
</bottom>
<tag name="todo" scope="all" description="To do:" />
</javadoc>
</target>
第二个
target
,其中只有一个
task
就是
javadoc
,用来生成项目的
api doc
,基本上
javadoc
的参数都被
ant
支持了。所以你平时怎么用
javadoc
现在依然可以怎么用
javadoc
。合格
target
出现了一个新的关键字“
depends
”
也就是依赖,就是说如果要执行这个
task
那么就必须先运行他的“
depends
”在这里就是“
init
”,所以我写的这个
build
就像是文学中的倒叙似的。
<taskdef
name="java2html" classname="com.java2html.Java2HTMLTask" />
<target name="javacode" depends="javadoc">
<java2html title="${project.name.info}version(${project.version.info}) Source Code Online View"
simple="no"
tabsize="4"
marginsize="2"
header="true"
footer="true"
encoding="UTF-8"
destination="${code.dir}">
<fileset dir="${src.dir}">
<include name="**/*.java"/>
</fileset>
<javadoc localRef="${doc.dir}" />
</java2html>
</target>
又来了一个新的关键字
taskdef
,这个是用来做什么的呢?听我慢慢道来。
古人云:“人无完人”,
ant
的开发者也是这样,他不可能预见到所有用户的需求,也就不能知道当前用户需要哪些
task
,肯定就会出现用户希望得到的
task
而
ant
默认没有支持的情况,所以
ant
的开发者提供了一个借口,可以让用户自定义自己的
task
,然后通过
taskdef
来声明
(
对
要
为开发
者的
这
个想法
击节
叫好
,
用
汤
司令了一句名言“高,
实
在是高”
)
,
这样
就
实现
了
ant
的无限
扩
展了。
Taskdef
至少要指明name和classname。Name用来指定
这
个
task
的任
务
的
标签
名,像
javadoc
这样
的就是
标签
名
(javadoc
是ant内置的task),classpath就是
这
个
标签
名的要
执
行的
类
的方法名,
关
于
这
个
扩
展
task
的
开发
我会在其他文章中
阐
述的,
这
里就到此
为
止,大家心里有
这
个印象就可以了(
java2html
可以看出是一个把java代
码转换
成
html
文档的工具,我使用的是我的修改版,可以支持字符
编码
,
详
情
见
拙著的〈
java2html
改造手
记
〉系列文章,当前我已
经开发
了
java2html
的
图
形界面,同
样
我会另外撰文
阐
述)。
<target name="compile" depends="init">
<javac srcdir="${src.dir}"
destdir="${bin.dir}"
encoding="${project.encoding.info}"
debug="off"
optimize="on">
<classpath refid="appClassPath" />
</javac>
</target>
这个也很简单,就是一个编译。
<target
name="junit" depends="compile">
<junit printsummary="yes" fork="yes" haltonfailure="no">
<classpath>
<pathelement location="${bin.dir}"/>
<pathelement path="${java.class.path}"/>
</classpath>
<formatter type="xml"/>
<batchtest todir="${juit.report.dir}/xml/${DSTAMP}">
<fileset dir="${bin.dir}">
<include name="**/*Test.class" />
</fileset>
</batchtest>
</junit>
<junitreport todir="${juit.report.dir}/xml/${DSTAMP}">
<fileset dir="${juit.report.dir}/xml/${DSTAMP}">
<include name="TEST-*.xml"/>
</fileset>
<report format="noframes" todir="${juit.report.dir}/html/${DSTAMP}"/>
</junitreport>
</target>
这是一个测试
target
。执行全部的测试代码,并把测试结果生成
xml
文档并将这个
xml
文档转换成
html
文档,
junit
也是
ant
默认支持的(
java
的开源社区的力量实在是太强大了,向广大的开源社区的同志们致敬)。
<target
name="package" depends="junit">
<jar jarfile="${dest.dir}/${project.name.info}-${project.version.info}-N${DSTAMP}.${project.package.type.info}"
basedir="${bin.dir}" />
</target>
<target name="backup" depends="package">
<zip destfile="${bak.dir}/${project.name.info}-${project.version.info}-N${DSTAMP}.zip">
<zipfileset file="${base.dir}/build.xml" />
<zipfileset file="${dest.dir}/${project.name.info}-${project.version.info}-N${DSTAMP}.${project.package.type.info}" />
<zipfileset dir="${src.dir}" prefix="src/"/>
<zipfileset dir="${bin.dir}" prefix="bin/" />
<zipfileset dir="${doc.dir}" prefix="doc/" />
<zipfileset dir="${code.dir}" prefix="code/" />
<zipfileset dir="${lib.dir}" prefix="lib/" />
<zipfileset dir="${juit.report.dir}" prefix="JunitReport/"/>
</zip>
</target>
<target
name="email" depends="backup">
</target>
以上内容是把应用打包备份等(
jar/zip
都是
ant
内置支持的)。
Email
这一段我使空着的,因为当前这个项目只有我一个人开发,所以我也不会给自己发
email
,如果你是一个团队开发异地开发,倒是可以利用这个将测试结果或者其他你想发的东西发送给你指定的人,而我的这个脚本要直接把开发结果发送到
web
上去,所以
email
功能对我来说也许是多余的。
<target
name="dest" depends="email">
<copy todir="${web.code.dir}">
<fileset dir="${code.dir}" />
</copy>
<copy todir="${web.doc.dir}">
<fileset dir="${doc.dir}" />
</copy>
<copy todir="${web.old.dir}">
<fileset dir="${bak.dir}">
<include name="${project.name.info}-${project.version.info}-N${DSTAMP}.zip"/>
</fileset>
</copy>
<copy todir="${web.src.dir}">
<fileset dir="${src.dir}" />
</copy>
<copy todir="${web.report.dir}">
<fileset dir="${juit.report.dir}/html" />
</copy>
<copy todir="${web.bin.dir}">
<fileset dir="${dest.dir}">
<include name="${project.name.info}-${project.version.info}-N${DSTAMP}.${project.package.type.info}"/>
</fileset>
</copy>
</target>
将刚才生成的乱七八糟的东西
copy
到指定的地方去,完成部署,这样我的脚本就完成了。最后别忘了再脚本的最后面添加
</project>
,这样才符合
xml
文档的格式。
在这个脚本里我实现了项目
Api
文档的生成,在线
java
代码的生成,编译,测试,打包,备份,部署到
web
等,我个人觉得这个已经满足一般的项目需求了,当然你也可以根据以上内容进行扩充,例如利用
ant
对
cvs
的内置支持实现版本控制,或者每日构建等(每日构建可是微软的看家法宝,而且
xp
编程每日构建只是最低要求,以上纯是道听途说)。发挥你的想象力去重复利用
ant
给你带来的乐趣和成就感吧。
还是那句老话,没有你做不到,只有你想不到。
以上内容
iBeyond
版权所有
(
注
ibeyond
和
beyondii
是同一个人,从名字上就可以看出
)
,你也许可以从
www.ibeyond.org
得到相关信息,
也可以通过admin@ibeyond.org
和我联系。