一起学习
雅加达蚂蚁是一种类似于Nmake,专门为爪哇语言设计的产品生成器。
为什么需要雅加达蚂蚁
假如你是一个爪哇应用软件项目的开发部负责人,那么你的任务就是设计软件,编写原代码,并把已初步写好的爪哇用户端应用程序编译部署到用户端,或是把已写好的爪哇伺服器端应用程序编译部署到伺服器端。而这要涉及到以下几个步骤:
从原代码库中取出一套最新版本的原代码文件,
将原代码文件编译为类文件,
将类文件,HTML文件及要用到的图像文件捆绑成一些jar文件,
将这些jar文件送到一个QA(也即质量保障)目录或伺服器中,供检测人员检测,
将检测人员检测出的产品缺陷一一改过,产生更新版的原代码。
从上的过程周而复始,直到你认为产品质量已达到这一次的产品发布的要求为止.
在这个周而复始的过程中,你可能负责管理一个三个人的小组,一个中小型的项目。这三个人如果有一个人在送出成品的关键时刻不在,就会影响到你按时送出成品。这时,你就需要一个事先设置好的自动化的程式,可以使小组中的任何人都可以启动这个自动化的程式,生成产品。
你也有可能负责管理一个大型项目,包括十几个分项目,几十个程序设计人员,几百个文件。每个月都有新的程序员参加进来, 又有旧的程序员离开公司另谋高就.在这种动态过程中,要做到有条不紊地按时送出成品,你就必须有一个非常理想的产品生成过程,和一个非常理想的产品生成器。
这种产品生成器,就包括大家所熟悉的Make, GNU Make, Nmake, Jam等。Nmake尤其为使用C 作Windows 编程的程序师所熟悉和使用。微软的Visual C 更提供了makefile的编辑器。
NMake在原则上讲,可以用作任何语言,包括爪哇语言的产品生成器。然而,NMake有很多缺点,而且新一代的爪哇语言程序师不一定有Visual C 的经验,甚至不一定有兴趣。在这种情况下,我们很自然地需要有一种和爪哇语言匹配的,弥补了 Nmake的缺陷的,超越操作系统的,专为爪哇语言设计的产品生成器。
Apache.org正在组织开发的,原代码公开(Open Source)的雅加达蚂蚁项目,就是为满足这一需要而作的一个产品生成器。雅加达蚂蚁本身是用纯爪哇语言开发的,配置文件采用XML格式。在下一个版本里还可能会提供一个图形介面来让用户更直观地修改系统的配置。
笔者在本文中,将简单举例介绍这一工具软件的使用.由于大多数用户可能是在Win32系统上使用这一工具,因此笔者决定以NT机上情况为例加以说明。实际上本文所举的例子并不难移植到Unix机上。
怎样安装雅加达蚂蚁
首先,你需要下面列出的软件:
JDK1.1或更新版.我使用的是JDK1.3版。你可以从升阳公司的网站下载这一软件(在你这样做时,请详细阅读升阳公司的版权说明)。在后面,我假设计你的JDK安装在D:\JDK1.3。
Jakarta Ant。你可以从apache.org下载这一软件的编译版(Binary),而且如果你对原代码感兴趣的话,还可从下载它的原代码版。(在你这样做时,请详细阅读apache.org的版权说明。)
安装的过程实际上就是解压缩的过程。由于我想把它安装到我的D:驱动器上,在缺省的情况下,1.3版的编译版和原代码版会被释放到D:\jakarta-ant-1.3。为了简单起见,我把目录改名为D:\AntTest。
在D:\AntTest目录下,应当有四个目录:bin,docs,lib和src。如果你没有安装原代码版,就不会有src。
怎样修改雅加达蚂蚁
你可以修改src目录中的文件。如果你更改了原代码,那么编译它的方法如下。
修改D:\AntTest\build.bat文件,在开头加入以下的语句
SET PATH=D:\jdk1.3;D:\jdk1.3\bin;
SET JAVA_HOME=D:\jdk1.3
SET ANT_HOME=D:\AntTest\lib
SET CLASSPATH=.;%ANT_HOME%\ant.jar;%ANT_HOME%\jaxp.jar;%ANT_HOME%\parser.jar;
换到D:\AntTest目录下,执行build.bat。
这将产生一个新的目录D:\AntTest\build,其中lib子目录里面应该有两个jar文件,它们就是运行的结果。
在下面,我就举例说明怎样使用这个功能强大的工具。
一个实例
在D:\AntTest目录下建立一个新的目录,叫Test,并在它下面建立一些子目录。关于这些目录的结构,请见图1。
图1.实例的目录的结构
建立一个爪哇语言类,叫做AntTest.java,其内容如下。
package com.myCompany.myPackage;
public class AntTest
{
public static void main(String[] args)
{
System.out.println("This is a test.");
}
}
显然,这是一个最简单的类文件,它属于一个叫做com\myCompany\myPackage的包,大家在路径结构可以看出,原代码文件应当在最里层的\myPackage目录下。我们利用这个最简单的类来演示并不简单的三件任务:
编译,这将把包结构复制到你指定的目标目录下,并把.java文件编译成.class文件。
在指定的目录下生成.jar文件。
把.jar文件复制到产品目录去,以供质量保证人员进行检测。
怎样写雅加达蚂蚁的XML生成文件
雅加达蚂蚁需要你给它指定运行的规则,这些规则存放在一个叫做build.xml的XML生成文件里。我在 D:\AntTest\Test下写这个build.xml文件。
===============================================================================================
项目
首先,我们需要在XML生成文件的开始给出项目的基本属性,包括项目名,项目的基目录名,以及项目的缺省目标名。由于每个XML生成文件只能有一个项目,因此项目名并不重要。项目的基目录名确定以后,下面再使用相对目录名就都是基于此基目录名的。项目的缺省目标名是一个重要的属性,它指定当用户不提供要运行的目标时,项目应运行此缺省目标。
<project name="Ant" default="build" basedir=".">
每个雅加达蚂蚁项目都包括若干目标,每个目标相当于一个要干的事情,每个目标又都可以依赖于其它的零个到多个目标。
由于我们有三个事情要做,可以初步确定,我们需要三个目标。但是,通常人们在第一个目标开始之前,先确定一些参数,这样我们就需要一个在所有三个目标之前的目标。因此,我们实际上需要四个目标。
初始化目标,名为init,给出必要的参量。
编译目标,名为build,它应当在初始化目标init完成之后执行。
在指定的目录下生成.jar文件目标,名为jar。它应当在编译目标build完成之后执行。
把.jar文件复制到产品目录去的目标,名为copy。,它应当在生成.jar文件目标jar完成之后执行。
目标
那么,怎样建立一个目标呢?首先让我们看看第一个目标。
<target name="init">
<property name="jardir" value="jar"/>
<property name="Src" value="src"/>
<property name="Dest" value="classes"/>
</target>
每个目标必有目标名,这个目标的名字叫init。由于它不依赖于其它的目标,因此它没有depends属性。这个目标的任务是建立三个变量, Src, Dest 和 jardir。这三个变量实际上就是源代码所在的位置,编译后文件所在的位置,产品所在的位置。我在这里均使用相对路径,是相对于项目的基路径而言的。你也可以使用绝对路径。
现在,我们来看看第二个目标。这个目标的depends属性是init,意为它必须在第一个目标(名为init)完成之后才能执行。如果本目标还取决于另一个目标(X)的执行,那么depends的值应为"init,X",即中间用逗号隔开。
<target name="build" depends="init">
<javac destdir="${Dest}" debug="on" >
<src path="${Src}"/>
<include name="**"/>
</javac>
</target>
这个目标内部包括有<javac/>标识符,这是一个雅加达蚂蚁的系统内定目标,它会启动爪哇语言的编译器,编译处于<src/>所指明的目录下的所有符合<include/>条件的文件。在<include/>里,*意为所有的文件,而**意为所有的文件和子目录。至于雅加达蚂蚁系统怎样找到爪哇语言的编译器,则依赖于一个必须由用户自已指明的环境变量,JAVA_HOME,请见后面所给出的build.bat文件内容。
在第三个目标内,也使用了一个系统内定目标<jar>。它会启动爪哇语言的jar生成器,在basedir处运行, 并生成名为jarfile的jar文件。
<target name="jar" depends="build">
<jar jarfile="test.jar" basedir="${Dest}"/>
<jar jarfile="testSrc.jar" basedir="${Src}"/>
</target>
在第四个目标内,也使用了一个系统内定目标<copy>。它会把所有在内指明的文件, 也就是在项目的基目录处的所有jar文件,复制到todir属性所指明的位置。includes属性规定在寻找文件时应寻找满足所指定条件的文件。
<target name="copy" depends="jar">
<copy todir="${jardir}">
<fileset dir="${basedir}" includes="*.jar" />
</copy>
</target>
从上就是所有的四个目标。它们被执行的顺序与它们在XML文件里出现的顺序并没有关系,但在这里作者有意做得使它们相同,以使读者易于读懂。
完整的生成文件如下:
<?xml version="1.0"?>
<project name="Ant" default="build" basedir=".">
<target name="init">
<property name="jardir" value="jar"/>
<property name="Src" value="src"/>
<property name="Dest" value="classes"/>
</target>
<target name="build" depends="init">
<javac destdir="${Dest}" debug="on">
<src path="${Src}"/>
<include name="**"/>
</javac>
</target>
<target name="jar" depends="build">
<jar jarfile="test.jar" basedir="${Dest}"/>
<jar jarfile="testSrc.jar" basedir="${Src}"/>
</target>
<target name="copy" depends="jar">
<copy todir="${jardir}">
<fileset dir="${basedir}" includes="*.jar" />
</copy>
</target>
</project>
批处理命令
在执行这一XML生成文件时,我们需要指明一些环境变量,因此作者建议写一个批处理文件来做这些事。下面就是build.bat的内容。
SET ANT_HOME=D:\AntTest
SET JAVA_HOME=D:\JDK1.3
%ANT_HOME%\bin\ant.bat %1
这个批处理文件接受一个可有可无的参量,亦即要执行的目标名。如果不提供目标名,系统就会选择缺省目标。因此发出build的命令只会执行第一个和第二个目标,而build jar会执行第一、二、三个目标。要执行所有的目标需要键入build copy指令。
========================================================================================
雅加达蚂蚁的图形用户界面
截至本文成文之日,雅加达蚂蚁的图形用户界面仍然未正式发行。但是,在它的原代码版中已经包括了它的图形用户界面部分,叫做Antidote。
下面的插图就是雅加达蚂蚁的图形用户界面的示范。可以看到,Antidote 可以用来打开一个XML生成文件,浏览文件内容,修改文件内容,并且在图形用户界面内运行雅加达蚂蚁,生成项目成品。图形用户界面的下部就可显示雅加达蚂蚁的运行结果。
图2.雅加达蚂蚁的图形用户界面的示范
怎样编译生成雅加达蚂蚁的图形用户界面
编译生成Antidote是一个非常好的练习。如果读者已经安装了原代码版,那么只需要编辑一个XML生成文件即可使用 雅加达蚂蚁来生成Antidote。首先,我们需要建立一些环境变量。
在D:\AntTest\src\antidote\下,建立一个叫build.bat的批处理文件,内容如下。
rem @echo off
SET PATH=D:\jdk1.3;D:\jdk1.3\bin;C:\Winnt4Ws\System32
SET JAVA_HOME=D:\jdk1.3
SET ANT_HOME=D:\AntTest
SET CLASSPATH=.;%ANT_HOME%\lib\ant.jar;%ANT_HOME%\lib\jaxp.jar;%ANT_HOME%
\lib\parser.jar;
call %ANT_HOME%\bin\ant.bat -Dant.install="%ANT_HOME%" %1 %2 %3 %4 %5 %6 %7 %8 %9
其中,C:\Winnt4Ws是我的Windows NT安装的位置。不指明这个路径,系统可能找不到xcopy这个外部命令。
在同样的地方,建立一个叫build.xml的生成文件,内容如下。
<project name="Antidote" default="jar" basedir=".">
<!-- Give user a chance to override without editing this file
(and without typing -D each time it compiles it) -->
<property file="${user.home}/.ant.properties" />
<property name="Name" value="Antidote" />
<property name="name" value="antidote" />
<property name="version" value="0.1" />
<property name="ant.home" value="../../" />
<property name="src.etc.dir" value="etc" />
<property name="src.dir" value="." />
<property name="build.dir" value="build/antidote" />
<property name="ant.build.dir" value="build/antidote" />
<property name="lib.dir" value="${ant.build.dir}/lib" />
<property name="build.classes" value="${build.dir}/classes" />
<property name="ant.dist.dir" value="dist/antidote" />
<path id="classpath">
<pathelement location="${lib.dir}/ant.jar" />
</path>
<property name="packages" value="org.apache.tools.ant.gui.*" />
<property name="manifest" value="etc/manifest" />
<!-- ================================ -->
<!-- Set some the defaults the user can override in .ant.properties -->
<!-- ================================= -->
<property name="build.compiler" value="modern" />
<property name="build.compiler.emacs" value="on" />
<!-- ===================================== -->
<!-- Define a global set of patterns that can be referenced by -->
<!-- its id attribute -->
<!-- ============================== -->
<patternset id="chmod.patterns">
<include name="**/antidote" />
</patternset>
<!-- ============================== -->
<!-- Prepares the build directory -->
<!-- ============================= -->
<target name="prepare">
<mkdir dir="${build.dir}" />
<mkdir dir="${ant.build.dir}" />
<tstamp />
</target>
<!-- ============================= -->
<!-- Compiles the source code -->
<!-- ============================== -->
<target name="compile" depends="prepare">
<mkdir dir="${build.classes}" />
<javac srcdir="${src.dir}" destdir="${build.classes}" debug="on" deprecation="on" optimize="off">
<classpath refid="classpath" />
</javac>
<copy todir="${build.classes}">
<fileset dir="${src.dir}">
<include name="**/*.properties" />
<include name="**/*.gif" />
</fileset>
</copy>
<filter token="VERSION" value="${version}" />
<filter token="DATE" value="${TODAY}" />
<filter token="TIME" value="${TSTAMP}" />
<copy todir="${build.classes}" overwrite="true" filtering="on">
<fileset dir="${src.dir}">
<include name="**/version.txt" />
</fileset>
</copy>
</target>
<!-- ========================= -->
<!-- Creates the jar archive -->
<!-- =============================== -->
<target name="jar" depends="compile" description="Build a jar file">
<mkdir dir="${lib.dir}" />
<jar jarfile="${lib.dir}/${name}.jar" basedir="${build.classes}" includes="org/**" manifest="${manifest}" />
</target>
<!-- ============================== -->
<!-- Creates the distribution -->
<!-- ========================== -->
<target name="dist" depends="jar" description="Creates the distribution">
<mkdir dir="${ant.dist.dir}" />
<mkdir dir="${ant.dist.dir}/lib" />
<mkdir dir="${ant.dist.dir}/src" />
<copy todir="${ant.dist.dir}/src">
<fileset dir="${src.dir}" />
</copy>
<copy todir="${ant.dist.dir}/lib">
<fileset dir="${lib.dir}" />
</copy>
<copy file="README" tofile="${ant.dist.dir}/README" />
<copy file="WHATSNEW" tofile="${ant.dist.dir}/WHATSNEW" />
<copy file="TODO" tofile="${ant.dist.dir}/TODO" />
<copy file="LICENSE" tofile="${ant.dist.dir}/LICENSE" />
</target>
<!-- ============================= -->
<!-- Packages the distribution with ZIP -->
<!-- ============================ -->
<target name="dist-zip" depends="dist">
<zip zipfile="${Name}-${version}.zip" basedir="${ant.dist.dir}" includes="**" />
</target>
<!-- ============================= -->
<!-- Packages the distribution with TAR-GZIP -->
<!-- ===================================== -->
<target name="dist-tgz" depends="dist">
<tar tarfile="${Name}-${version}.tar" basedir="${ant.dist.dir}" includes="**" />
<gzip zipfile="${Name}-${version}.tar.gz" src="${Name}-${version}.tar" />
</target>
<!-- ======================================== -->
<!-- Cleans up generated stuff -->
<!-- ======================================== -->
<target name="clean">
<delete dir="${build.dir}" />
<delete dir="${ant.dist.dir}" />
</target>
<!-- ============================================== -->
<!-- Total cleanup -->
<!-- ===================================== -->
<target name="total-clean" depends="clean">
<delete file="${lib.dir}/${name}.jar" />
<delete file="${Name}-${version}.zip" />
<delete file="${Name}-${version}.tar" />
<delete file="${Name}-${version}.tar.gz" />
</target>
</project>
运行build.bat,然后在build/antidote/子目录下,找到antidote.jar文件,把它复制到D:\AntTest\lib\处。回到 D:\AntTest\src\Antidote,建立一个文件,名为run.bat,内容如下:
SET ANT_HOME=D:\AntTest
SET JAVA_HOME=D:\jdk1.3
SET CLASSPATH=.;%ANT_HOME%\lib\ant.jar;%ANT_HOME%\lib
\jaxp.jar;%ANT_HOME%\lib\parser.jar;%ANT_HOME%
\lib\antidote.jar;
SET PATH=D:\jdk1.3;D:\jdk1.3\bin;C:\Winnt4Ws\System32
%JAVA_HOME%\bin\java org.apache.tools.ant.gui.Main
运行run.bat,你就应该看到如图2所示的antidote图形界面。
下载本文示例代码
雅加达蚂蚁:新一代Java产品生成器雅加达蚂蚁:新一代Java产品生成器雅加达蚂蚁:新一代Java产品生成器雅加达蚂蚁:新一代Java产品生成器雅加达蚂蚁:新一代Java产品生成器雅加达蚂蚁:新一代Java产品生成器雅加达蚂蚁:新一代Java产品生成器雅加达蚂蚁:新一代Java产品生成器雅加达蚂蚁:新一代Java产品生成器雅加达蚂蚁:新一代Java产品生成器雅加达蚂蚁:新一代Java产品生成器雅加达蚂蚁:新一代Java产品生成器