ANT安装配置笔记和最佳实践




ANT的安装/配置笔记
作者:车东发表于:2003-03-0617:03最后更新于:2007-04-1211:04
版权声明:可以任意转载,转载时请务必以超链接形式标明文章原始出处和作者信息及本声明。
http://www.chedong.com/tech/ant.html
--------------------------------------------------------------------------------
内容摘要:
ant是一个基于JAVA的自动化脚本引擎,脚本格式为XML。除了做JAVA编译相关任务外,ANT还可以通过插件实现很多应用的调用。


ANT的基本概念:
ANT的安装:解包,设置路径
ANT的使用:最好的学习只不过是一个简单实用的例子起步……
ANT的基本概念:Java的Makefile
当一个代码项目大了以后,每次重新编译,打包,测试等都会变得非常复杂而且重复,因此c语言中有make脚本来帮助这些工作的批量完成。在Java中应用是平台无关性的,当然不会用平台相关的make脚本来完成这些批处理任务了,ANT本身就是这样一个流程脚本引擎,用于自动化调用程序完成项目的编译,打包,测试等。除了基于JAVA是平台无关的外,脚本的格式是基于XML的,比make脚本来说还要好维护一些。


每个ant脚本(缺省叫build.xml)中设置了一系列任务(target):比如对于一个一般的项目可能需要有以下任务。

任务1:usage打印本脚本的帮助信息(缺省)
任务2:clean
<-- init清空初始化环境
任务3:javadoc<--build<--init生成JAVADOC
任务4:jar<--build<--init生成JAR
任务5:all<--jar+javadoc<--build<--init完成以上所有任务:jarjavadoc
而多个任务之间往往又包含了一定了依赖关系:比如把整个应用打包任务(jar)的这个依赖于编译任务(build),而编译任务又依赖于整个环境初始化任务(init)等。

注:我看到很多项目的ant脚本中的命名基本上都是一致的,比如:编译一般叫build或者compile;打包一般叫jar或war;生成文档一般命名为javadoc或javadocs;执行全部任务all。在每个任务的中,ANT会根据配置调用一些外部应用并配以相应参数执行。虽然ANT可调用的外部应用种类非常丰富,但其实最常用的就2,3个:比如javacjavadocjar等。
ANT的安装
解包后在系统可执行路径中加入指向ant的bin的路径就可以了,比如可以在GNU/Linux上把以下配置加入/etc/profile中:
exportANT_HOME
=/home/ant
exportJAVA_HOME =/usr/java/j2sdk1.4.1
exportPATH =$PATH:$JAVA_HOME/bin:$ANT_HOME/bin

这样执行ant后,如果不指定配置文件ant会缺省找build.xml这个配置文件,并根据配置文件执行任务,缺省的任务设置可以指向最常用的任务,比如:build,或指向打印帮助信息:usage,告诉用户有那些脚本选项可以使用。


ANT的使用

最好的学习过程就是看懂那些opensource项目中的build.xml脚本,然后根据自己的需要简化成一个更简单的,ANT和APACHE上很多非常工程派的项目:简单易用,而且适应性非常强,因为这些项目的建立往往来源于开发人员日常最直接的需求。
以下是的一个WebLucene应用的例子:修改自JDOM的build.xml:

<projectdefault
="usage" basedir ="." >

<!-- =================================================================== -->
<!-- Initializationtarget -->
<!-- =================================================================== -->
< target name ="init" >
< tstamp />
< property file ="${basedir}/build.properties" />
< property name ="Name" value ="ProjectFullName" />
< property name ="name" value ="project_name" />
< property name ="version" value ="0.2" />
< property name ="year" value ="2003" />

< echo message ="-----------${Name}${version}[${year}]------------" />

< property name ="debug" value ="off" />
< property name ="optimize" value ="on" />
< property name ="deprecation" value ="on" />

< property name ="src.dir" value ="./src/WEB-INF/src" />
< property name ="lib.dir" value ="./src/WEB-INF/lib" />
< property name ="packages" value ="com.chedong.*,org.apache.lucene.*" />

< property name ="build.src" value ="./src/WEB-INF/build" />
< property name ="build.dest" value ="./src/WEB-INF/classes" />
< property name ="build.javadocs" value ="./src/doc" />

< path id ="classpath" >
< pathelement path ="${jsdk_jar}" />
< fileset dir ="${lib.dir}" >
< include name ="**/*.jar" />
</ fileset >
</ path >

< filter token ="year" value ="${year}" />
< filter token ="version" value ="${version}" />
< filter token ="date" value ="${TODAY}" />
< filter token ="log" value ="true" />
< filter token ="verbose" value ="true" />
</ target >

<!-- =================================================================== -->
<!-- Helponusage -->
<!-- =================================================================== -->
< target name ="usage" depends ="init" >
< echo message ="${Name}Buildfile" />
< echo message ="-------------------------------------------------------------" />
< echo message ="" />
< echo message ="availabletargetsare:" />
< echo message ="" />
< echo message ="jar-->generatesthe${name}.jarfile" />
< echo message ="build-->compilesthesourcecode" />
< echo message ="javadoc-->generatestheAPIdocumentation" />
< echo message ="clean-->cleansupthedirectory" />
< echo message ="" />
< echo message ="Pleaserenamebuild.properties.defaulttobuild.properties" />
< echo message ="andeditbuild.propertiestospecifyJSDK2.3classpath." />
< echo message ="" />
< echo message ="Seethecommentsinsidethebuild.xmlfileformoredetails." />
< echo message ="-------------------------------------------------------------" />
< echo message ="" />
< echo message ="" />
</ target >

<!-- =================================================================== -->
<!-- Preparesthesourcecode -->
<!-- =================================================================== -->
< target name ="prepare-src" depends ="init" >
<!-- createdirectories -->
< mkdir dir ="${build.src}" />
< mkdir dir ="${build.dest}" />

<!-- copysrcfiles -->
< copy todir ="${build.src}" >
< fileset dir ="${src.dir}" />
</ copy >
</ target >

<!-- =================================================================== -->
<!-- Compilesthesourcedirectory -->
<!-- =================================================================== -->
< target name ="build" depends ="prepare-src" >
< javac srcdir ="${build.src}"
destdir
="${build.dest}"
debug
="${debug}"
optimize
="${optimize}" >
< classpath refid ="classpath" />
</ javac >
</ target >

<!-- =================================================================== -->
<!-- Createstheclasspackage -->
<!-- =================================================================== -->
< target name ="jar" depends ="build" >
< jar jarfile ="${lib.dir}/${name}.jar"
basedir
="${build.dest}"
includes
="**" />
</ target >

<!-- =================================================================== -->
<!-- CreatestheAPIdocumentation -->
<!-- =================================================================== -->
< target name ="javadoc" depends ="build" >
< mkdir dir ="${build.javadocs}" />
< javadoc packagenames ="${packages}"
sourcepath
="${build.src}"
destdir
="${build.javadocs}"
author
="true"
version
="true"
use
="true"
splitindex
="true"
windowtitle
="${Name}API"
doctitle
="${Name}" >
< classpath refid ="classpath" />
</ javadoc >
</ target >

<!-- =================================================================== -->
<!-- Cleantargets -->
<!-- =================================================================== -->
< target name ="clean" depends ="init" >
< delete dir ="${build.src}" />
< delete dir ="${build.dest}/org" />
< delete dir ="${build.dest}/com" />
< delete >
< fileset dir ="${build.dest}" includes ="**/*.class" />
</ delete >
</ target >
</ project >
<!-- Endoffile -->

缺省任务:usage打印帮助文档,告诉有那些任务选项:可用的有build,jar,javadoc和clean.

初始化环境变量:init
所有任务都基于一些基本环境变量的设置初始化完成,是后续其他任务的基础,在环境初始化过程中,有2点比较可以方便设置:

1除了使用却缺省的property设置了JAVA源路径和输出路径外,引用了一个外部的build.properties文件中的设置,
< property file ="${basedir}/build.properties" />
这样大部分简单配置用户只要会看懂build.properties就可以了,毕竟XML比起keyvalue的属性文件还是要可读性差一些。用build.properties也可以方便其他用户从编译的细节中解放出来。

2CLASSPATH设置:使用了其中的:
< path id ="classpath" >
< pathelement path ="${jsdk_jar}" />
< fileset dir ="${lib.dir}" >
< include name ="**/*.jar" />
</ fileset >
</ path >
则相当于设置了:CLASSPATH=/path/to/resin/lib/jsdk23.jar;/path/to/project/lib/*.jar;

文件复制:prepare-src
创建临时SRC存放目录和输出目录。
<!-- =================================================================== -->
<!-- Preparesthesourcecode -->
<!-- =================================================================== -->
< target name ="prepare-src" depends ="init" >
<!-- createdirectories -->
< mkdir dir ="${build.src}" />
< mkdir dir ="${build.dest}" />

<!-- copysrcfiles -->
< copy todir ="${build.src}" >
< fileset dir ="${src.dir}" />
</ copy >
</ target >

编译任务:build
编译时的CLASSPATH环境通过一下方式找到引用一个path对象
< classpath refid ="classpath" />

打包任务:jar
对应用打包生成项目所写名的.jar文件
<!-- =================================================================== -->
<!-- Createstheclasspackage -->
<!-- =================================================================== -->
< target name ="jar" depends ="build" >
< jar jarfile ="${lib.dir}/${name}.jar"
basedir
="${build.dest}"
includes
="**" />
</ target >

生成JAVADOC文档任务:javadoc
<!-- =================================================================== -->
<!-- CreatestheAPIdocumentation -->
<!-- =================================================================== -->
< target name ="javadoc" depends ="build" >
< mkdir dir ="${build.javadocs}" />
< javadoc packagenames ="${packages}"
sourcepath
="${build.src}"
destdir
="${build.javadocs}"
author
="true"
version
="true"
use
="true"
splitindex
="true"
windowtitle
="${Name}API"
doctitle
="${Name}" >
< classpath refid ="classpath" />
</ javadoc >
</ target >

清空临时编译文件:clean
<!-- =================================================================== -->
<!-- Cleantargets -->
<!-- =================================================================== -->
< target name ="clean" depends ="init" >
< delete dir ="${build.src}" />
< delete dir ="${build.dest}/org" />
< delete dir ="${build.dest}/com" />
< delete >
< fileset dir ="${build.dest}" includes ="**/*.class" />
</ delete >
</ target >

TODO:
更多任务/扩展:(样例)

测试任务:JUnit测试
代码风格检查任务:CheckStyle,Jalopy等
邮件警报任务:可以把以上这些任务的输出警告发送到制定的用户列表中,这个任务可以设置每天自动运行。

参考资料:

JakartaANT:
http://ant.apache.org


ANT十五大最佳实践
作者:EricM.Burke,coauthorofJavaExtremeProgrammingCookbook

原文:http://www.onjava.com/pub/a/onjava/2003/12/17/ant_bestpractices.html

译者:徐彤MSN:xt121@hotmail.com



在Ant出现之前,构建和部署Java应用需要使用包括特定平台的脚本、Make文件、各种版本的IDE甚至手工操作的“大杂烩”。现在,几乎所有的开源Java项目都在使用Ant,大多数公司的内部项目也在使用Ant。Ant在这些项目中的广泛使用自然导致了读者对一整套Ant最佳实践的迫切需求。

本文总结了我喜爱的Ant技巧或最佳实践,多数是从我亲身经历的项目错误或我听说的其他人经历的“恐怖”故事中得到灵感的。比如,有人告诉我有个项目把XDoclet生成的代码放入带有锁定文件功能的版本控制工具中。当开发者修改源代码时,他必须记住手工检出(Checkout)并锁定所有将要重新生成的文件。然后,手工运行代码生成器,只到这时他才能够让Ant编译代码,这一方法还存在如下一些问题:

生成的代码无法存储在版本控制系统中。
Ant(本案例中是Xdoclet)应该自动确定下一次构建涉及的源文件,而不应由程序员手工确定。
Ant的构建文件应该定义好正确的任务依赖关系,这样程序员就不必为了完成构建而不得不按照特定顺序调用任务。
当我开始一个新项目时,我首先编写Ant构建文件。Ant文件明确地定义构建的过程,并被团队中的每个程序员使用。本文所列的技巧基于这样的假定:Ant构建文件是一个必须仔细编写的重要文件,它应在版本控制系统中得到维护,并被定期进行重构。下面是我的十五大Ant最佳实践。

1.采用一致的编码规范
Ant用户有的喜欢有的痛恨其构建文件的XML语法。与其跳进这一令人迷惑的争论中,不如让我们先看一些能保持XML构建文件简洁的方法。

首先也是最重要的,花费时间格式化你的XML让它看上去很清晰。不论XML是否美观,Ant都可以工作。但是丑陋的XML很难令人读懂。倘若你在任务之间留出空行,有规则的缩进,每行文字不超过90列左右,那么XML令人惊讶地易读。再加上使用能够高亮XML语法的优秀编辑器或IDE工具,你就不会有阅读的麻烦。

同样,精选含意明确、容易读懂的词汇来命名任务和属性。比如,dir.reports就比rpts好。特定的编码规范并不重要,只要拿出一套规范并坚持使用就行。

2.将build.xml放在项目根目录中
Ant构建文件build.xml可以放在任何位置,但是放在项目顶级目录中可以保持项目简洁。这是最常用的规范,开发者能够在顶级目录中找到预期的build.xml。把构建文件放在根目录中,也能够使人容易了解项目目录树中不同目录之间的逻辑关系。以下是一个典型的项目目录层次:

[rootdir]
|build.xml
+--src
+--lib(包含第三方JAR包)
+--build(由build任务生成)
+--dist(由build任务生成)

当build.xml在顶级目录时,假设你处于项目某个子目录中,只要输入:ant-findcompile命令,不需要改变工作目录就能够以命令行方式编译代码。参数-find告诉Ant寻找存在于上级目录中的build.xml并执行。

3.使用单一的构建文件
有人喜欢将一个大项目分解成几个小的构建文件,每个构建文件分担整个构建过程的一小部分工作。这确实是看法不同的问题,但是应该认识到,将构建文件分割会增加对整体构建过程的理解难度。要注意在单一构建文件能够清楚表现构建层次的情况下不要过工程化(over-engineer)。

即使你把项目划分为多个构建文件,也应使程序员能够在项目根目录下找到核心build.xml。尽管该文件只是将实际构建工作委派给下级构建文件,也应保证该文件可用。

4.提供良好的帮助说明
应尽量使构建文件自文档化。增加任务描述是最简单的方法。当你输入ant-projecthelp时,你就可以看到带有描述的任务清单。比如,你可以这样定义任务:

< target name ="compile"
description
="Compilescode,outputgoestothebuilddir." >
最简单的规则是把所有你想让程序员通过命令行就可以调用的任务都加上描述。对于一般用来执行中间处理过程的内部任务,比如生成代码或建立输出目录等,就无法使用描述属性。

这时,可以通过在构建文件中加入XML注释来处理。或者专门定义一个help任务,当程序员输入anthelp时来显示详细的使用说明。

< target name ="help" description ="Displaydetailedusageinformation" >
< echo > Detailedhelp... </ echo ></ target >
5.提供清除任务
每个构建文件都应包含一个清除任务,用来删除所有生成的文件和目录,使系统回到构建文件执行前的初始状态。执行清空任务后还存在的文件都应处在版本控制系统的管理之下。比如:

< target name ="clean"
description
="Destroysallgeneratedfilesanddirs." >
< delete dir ="${dir.build}" />
< delete dir ="${dir.dist}" />
</ target >
除非是在产生整个系统版本的特殊任务中,否则不要自动调用clean任务。当程序员仅仅执行编译任务或其他任务时,他们不需要构建文件事先执行既令人讨厌又没有必要的清空任务。要相信程序员能够确定何时需要清空所有文件。

6.使用ANT管理任务从属关系
假设你的应用由SwingGUI组件、Web界面、EJB层和公共应用代码组成。在大型系统中,你需要清晰地定义每个Java包属于系统的哪一层。否则任何一点修改都要被迫重新编译成百上千个文件。糟糕的任务从属关系管理会导致过度复杂而脆弱的系统。改变GUI面板的设计不应造成Servlet和EJB的重编译。

当系统变得庞大后,稍不注意就可能将依赖于客户端的代码引入到服务端。这是因为典型的IDE项目文件编译任何文件都使用单一的classpath。而Ant能让你更有效地控制构建活动。

设计你的Ant构建文件编译大型项目的步骤:首先,编译公共应用代码,将编译结果打成JAR包文件。然后,编译上一层的项目代码,编译时依靠第一步产生的JAR文件。不断重复这一过程,直到最高层的代码编译完成。

分步构建强化了任务从属关系管理。如果你工作在底层Java框架上,偶然引用到高层的GUI模板组件,这时代码不需要编译。这是由于构建文件在编译底层框架时在源路径中没有包含高层GUI面板组件的代码。

7.定义并重用文件路径
如果文件路径在一个地方一次性集中定义,并在整个构建文件中得到重用,那么构建文件更易于理解。以下是这样做的一个例子:

< project name ="sample" default ="compile" basedir ="." >
< path id ="classpath.common" >
< pathelement location ="${jdom.jar.withpath}" />
...etc
</ path >
< path id ="classpath.client" >
< pathelement location ="${guistuff.jar.withpath}" />
< pathelement location ="${another.jar.withpath}" />
<!-- reusethecommonclasspath -->
< path refid ="classpath.common" />
</ path >
< target name ="compile.common" depends ="prepare" >
< javac destdir ="${dir.build}" srcdir ="${dir.src}" >
< classpath refid ="classpath.common" />
< include name ="com/oreilly/common/**" />
</ javac >
</ target >
</ project >
当项目不断增长构建日益复杂时,这一技术越发体现出其价值。你可能需要为编译不同层次的应用定义各自的文件路径,比如运行单元测试的、运行应用程序的、运行Xdoclet的、生成JavaDocs的等等不同路径。这种组件化路径定义的方法比为每个任务单独定义路径要优越得多。否则,很容易丢失任务从属关系的轨迹。

8.定义恰当的任务从属关系
假设dist任务从属于jar任务,那么哪个任务从属于compile任务哪个任务从属于prepare任务呢?Ant构建文件最终定义了任务的从属关系图,它必须被仔细地定义和维护。

应该定期检查任务的从属关系以保证构建工作得到正确执行。大的构建文件随着时间推移趋向于增加更多的任务,所以到最后可能由于不必要的从属关系导致构建工作非常困难。比如,你可能发现在程序员只需编译一些没有使用EJB的GUI代码时又重新生成了EJB代码。

以“优化”的名义忽略任务的从属关系是另一种常见的错误。这种错误迫使程序员为了得到恰当的结果必须记住并按照特定的顺序调用一串任务。更好的做法是:提供描述清晰的公共任务,这些任务包含正确的任务从属关系;另外提供一套“专家”任务让你能够手工执行个别的构建步骤,这些任务不提供完整的构建过程,但是让那些专家用户在快速而恼人的编码期间能够跳过某些步骤。

9.使用属性
任何需要配置或可能发生变化的信息都应作为Ant属性定义下来。对于在构建文件中多次出现的值也同样处理。属性既可以在构建文件头部定义,也可以为了更好的灵活性而在单独的属性文件中定义。以下是在构建文件中定义属性的样式:

< project name ="sample" default ="compile" basedir ="." >
< property name ="dir.build" value ="build" />
< property name ="dir.src" value ="src" />
< property name ="jdom.home" value ="../java-tools/jdom-b8" />
< property name ="jdom.jar" value ="jdom.jar" />
< property name ="jdom.jar.withpath"
value
="${jdom.home}/build/${jdom.jar}" />
etc...
</ project >
或者你可以使用属性文件:

< project name ="sample" default ="compile" basedir ="." >
< property file ="sample.properties" />
etc...
</ project >
在属性文件sample.properties中:

dir.build=build
dir.src=src
jdom.home=../java-tools/jdom-b8
jdom.jar=jdom.jarjdom.jar.withpath=${jdom.home}/build/${jdom.jar}
用一个独立的文件定义属性是有好处的,它可以清晰地定义构建中的可配置部分。另外,在开发者工作在不同操作系统的情况下,你可以在不同的平台上提供该文件的不同版本。

10.保持构建过程独立
为了最大限度的扩展性,不要应用外部路径和库文件。最重要的是不要依赖于程序员的CLASSPATH设置。取而代之的是,在构建文件中使用相对路径并定义自己的路径。如果你引用了绝对路径如C:java ools,其他开发者未必使用与你相同的目录结构,所以就无法使用你的构建文件。

如果你部署开放源码项目,应该提供包含编译代码所需的所有JAR文件的发行版本。当然,这是在遵守许可协议的基础上。对于内部项目,相关的JAR文件都应在版本控制系统的管理中,并捡出(checkout)到大家都知道的位置。

当你必须引用外部路径时,应将路径定义为属性。使程序员能够用适合他们自己的机器环境的参数重载这些属性。你也可以使用以下语法引用环境变量:

< property
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值