Ant最佳实践Top 15

来源:互联网 发布:狗爹域名 编辑:程序博客网 时间:2024/05/01 22:23
                                                        Ant最佳实践Top 15
                                                       作者: Eric M. Burke, Java Extreme Programming Cookbook的合作者之一
                                                        12/17/2003
 
在Ant之前,创建和发布Java应用对于平台相关的脚本,makefiles,IDEs的属性,或者过程手册等的要求缺一不可.现在,几乎每一个开源的Java项目都使用Ant.同时,大量的公司在他们的内部项目中也使用Ant.Ant在这些项目中的广泛使用很自然的导致了对一系列创建好的最佳实践的不断增长的需要.
本文总结了几个我最喜欢的Ant 贴示(tips)或者说最佳实践.很多是从以前的项目中的错误,或者是我得到的其他开发人员的惨痛的故事中得到的灵感.比如有人告诉我,在一个项目中,将XDoclet生成的代码放置到一个版本控制工具中会锁住文件.当一个开发人员修改了源文件,他必须记得手工check out并且锁定所有的重新产生的文件.然后他必须手工运行代码生成器,并且只有这样他才能告诉Ant去编译代码.在这里,这个方法有一些问题:
·生成的代码不必保存到版本控制中去
·Ant(在这种情况下是XDoclet)应该自动决定那些文件会被下一次创建所影响.开发人员不应该手工指出这点.
·Ant buildfile应该定义正确的目标依赖,以便开发人员不要涉及到目标文件的细节顺序,目的是获得一个良好的文件创建.
当我开始每一个新的项目,我首先是创建Ant buildfile.Ant定义创建过程被开发团队的每一个开发人员从始到终使用.本文所有的贴示(tips)假定Ant buildfile文件是一个手工艺品,并且必须作为用例来写,在版本控制器中维护,周期性的重构.以下就是我的Ant最佳实践top15.
 
1.    采用一致的惯用的Style
Ant的使用者要么喜欢XML buildfile的句法,要么讨厌它.跳开这中间的令人着迷的争论,让我们看看使得XML buildfile文件整洁的一些简单的方法.
第一并且首要的是花时间格式化你的XML,使得它看起来更加吸引人.Ant可以使用丑陋的或者漂亮的XML工作,但是丑陋的XML更加难以阅读.如果你在target之间留一个空白行,坚持缩进格式,并且避免超过大约90列的文字,XML就会表现出令人惊奇的可读性.在一个能够使得XML句法增亮显示的好的编辑器或者IDE工作,你将较少的遇到困难.
同样,为targets或属性选择有意义的,可读的名称.例如,dir.report就比rpts来说是一个好的名称.给定惯用命名规范并不重要—只要遵从某一事物并且跟住它.
 
2.    build.xml放在项目的根目录
Ant buildfile文件可以放置在任何地方,但是将build.xml文件放在项目的最高一级目录使得事情简单和简洁.这是最常用的惯例,并且开发人员期望在那里找到build.xml文件.将buildfile文件放在顶级目录也使得我们能够容易的看出不同的目录在我们的项目树中的路径关系.下面是一个典型的项目的目录:
[root dir]
 | build.xml
 +--src
 +--lib (contains 3rd party JARs)
 +--build (generated by the build)
 +--dist (generated by the build)
build.xml在顶级目录的时候,你不需要更改你的工作目录就能够使用命令行编译代码,如果你在项目目录的内部的某个地方的话.仅仅需要输入: ant -find compile. -find参数告诉Ant去寻找上级目录直到找到buildfile为止.
 
3.    使用单一的Buildfile文件
一些人喜欢将一个大项目打碎到几个小的buildfiles文件中,每一个buildfile文件负责其中的一部分.这严格来说是一个观念的问题,但是必须意识到,打碎build常常使得最后难以包装成一个完整的过程.当一个单一的文件能够做的时候,小心不要过度包装buildfiles的机灵的层次关系.
即使你的项目被划分到许多不同的buildfiles,开发人员期望在项目的根目录下找到一个主build.xml文件.确保这个buildfile是可得的,即使它仅仅是将实际的工作委派到下一级的builds.
 
4.    提供良好的帮助
尽力提供buildfile的自叙文档.增加target描述是完成这类任务的一个简单方式.当你输入ant -projecthelp,你会看到每一个包括描述的target清单.例如,你可以定义一个target如下:
 description="Compiles code, output goes to the build dir.">
这个简单的规则可以包括你希望开发人员在命令行调用的所有targets的描述.内部targets可以不包括描述属性,内部targets可能包括一些执行中间过程的targets,例如,生成代码或创建输出路径.
提供帮助的另一个方法是在buildfile文件里包括XML注释.或者,定义一个命名为helptarget,当开发人员输入ant help的时候,它用来打出详细的用法信息.
        description="Display detailed usage information">
 Detailed help...
 
5.    提供一个Clean target
每一个buildfile文件应该包括一个删除所有的生成的文件和目录的target,使得每一件事情可以回到它原来的样子,初始的状态.在做了clean动作以后,所有保留的文件都能够在版本控制器中找到.例如:
    description="Destroys all generated files and dirs.">
 
 
不要自动调用clean,除非你可能有一些特别的target来产生一个全部的释放.当开发人员仅仅是编译或执行其他的任务,他们不希望buildfile在整个过程之前就执行一个完全的cleanup.这是既令人讨厌又达不到预期目的的.相信开发人员能够决定他们准备什么时候clean所有的文件.
 
6.    使用Ant管理依赖
假设你的应用由一个Swing GUI,一个web接口,一个EJB层,和一个共享的工具代码组成.在大型系统中,你需要清晰的定义哪一个Java包属于系统的哪一个层.否则,每一次你修改一点东西,你将被迫编译成百上千的文件.脆弱的依赖管理导致过度的复杂,脆弱的系统.修改一个GUI panel的布局不应该导致你重新编译你的servlets和EJBs.
随着系统逐渐变大,经常不注意的引入了依赖于客户断代码的服务器端代码,反之亦然.这是因为典型的IDE项目使用一个单一的类路径来编译每一个文件.Ant让你更有效的控制build.
设计你的Ant buildfile文件,使得Ant分阶段的编译大型项目.首先,编译共享的工具代码.把结果放到JAR文件.然后编译项目的高层次部分.当你编译高层次代码时,编译不会影响到你第一步创建的JAR文件.重复这个过程直到你编译了系统的最高层次.
分阶段building加强了依赖管理.如果你工作在一个底级别的Java框架类,却意外的引用了一个高级别的GUI panel,代码将不会编译通过.这是因为当buildfile编译底级别的框架类的时候,在源代码目录里还没有引入高级别的GUI panel代码.
 
7.    定义和重用路径
如果路径一旦被定义在中间位置,buildfile常常更加易于理解,然后就能在整个buildfile中重用.如下是一个使用这个方法的例子.
 
   
    ...etc
 
 
   
   
   
   
 
 
   
         
         
   
 
随着项目的增长和build变得日益复杂,这种技术往往会取得更多的价值.你可能会不得不定义不同的路径来编译应用的每一个层次,又如运行单元测试的路径,运行应用的路径,运行XDoclet的路径,生成JavaDocs的路径,等等.这种模块式路径方法比起为所有的事情做一个单一的路径好得多.而没有模块化则更加易于犯依赖混淆的错误.
 
8.    定义正确的target依赖
假设disttarget依赖于jartarget,而jartarget依赖于compile,且compile依赖于prepare.Ant buildfiles最终定义了一个依赖关系图表,而这个图表需要小心的定义和维护.
周期性的评审依赖关系以确保你的builds做正确适量的工作.大型的buildfiles可能会导致随着更多targets的增加而降低效率,所以你必须关闭引起你的builds工作难度的不必要的依赖关系.例如,你可能会发现你自己更新了EJB代码,而实际上开发人员只希望编译一些GUI代码,这完全没有用到EJB.
以省略依赖关系来”优化”build的努力是另外一个普遍的错误.这是一个错误是因为当开发人员为了获得一个恰当的build的时候,它强迫开发人员记住所调用的一系列targets的详细的顺序.这里有一个更好的解决方法:提供包含正确依赖的公共targets(这些targets都有描述),也提供一些”专门”的targets,这些targets允许你手工的运行个别的build步骤.这些步骤不能保证一个完整的build,但是允许个别用户在快速的和dirty代码期间选择旁路步骤build.
 
9.    为配置使用属性
每一个可能需要配置,或者可能改变的信息都应该定义成一个Ant属性.在buildfile文件中可能使用不止一次的值也是这样.属性应该定义在buildfile的顶部,或者为了最大限度的方便而定义在一个独立的属性文件.如下是当定义在buildfile文件中的样子:
 
 
 
 
 
                    value="${jdom.home}/build/${jdom.jar}"/>
    etc...
或者,你可以使用一个属性文件:
 
   etc...
那么sample.properties属性文件看起来如下:
dir.build=build
dir.src=src
jdom.home=../java-tools/jdom-b8
jdom.jar=jdom.jar
jdom.jar.withpath=${jdom.home}/build/${jdom.jar}
使用一个独立的属性文件是有好处的,因为它明确的定义了build的配置部分.你可以为不同的平台提供不同版本的属性文件,或者开发人员工作在不同的操作系统.
 
10.保持Build过程独立
最大限度的可能,不要引用一个外部的目录或者库.最终要的是,不要依赖开发人员的CLASSPATH设置.而是在你的buildfile里使用相对路径和定义你自己的路径.如果你引用了一个外部路径如C:/java/tool,其他的开发人员可能不能使用你的buildfile文件,因为他们根本不可能使用和你同样的目录结构.
如果你发布一个开源项目,提供一个包含所有的用来编译你的代码所必须的JAR文件的发布包,当然,这个包也包含版权所有的信息.对于内部项目,所依赖的JAR文件应该在版本管理器的管理下,checked out到一个众所周知周知的位置.
如果你的确引用了一个外部的路径,定义这个路径为属性.这能够使得开发人员覆盖这些设置来确保他们自己的机子可以使用.你也可以引用一个环境变量,使用如下的句法:
 
11.使用版本控制器
Buildfile是一个重要的人工制造物,和源代码一样,应该被编制版本.当你标签或者标记你的代码,给你的buildfile文件使用相同的标签或标记.这使得你回到以前的发布的时候,可以使用以前的相同版本的buildfile build软件.
除了buildfile之外,你应该在版本控制器里维护第三方的JAR文件.同样,这使得你可能重新找回你的软件的前一个发布版本.这也使得你很容易确保所有的开发人员都使用相同的JAR文件,因为他们可以从版本控制器里将它们check out到一个相对于buildfile的路径.
一般地,要避免保存build的输出文件在版本控制器里.假如你的源代码是完全版本化的,你就能够通过build过程找回前一个发布.
 
12.在开发团队最起码要使用Ant作为通用的工具
假设你的团队使用IDE,当开发人员可以轻松点击突出的按钮图标就能重新build整个应用的时候,为什么还要费力的使用Ant呢?
IDEs的问题是横跨在团队的开发人员面前的一致性和重复能力.IDEs大多数是设计给单个的开发人员的产品,而不能做到团队开发人员前一致的build.典型的IDEs要求每一个开发人员都要定义他们自己的项目文件.开发人员可能使用不同的目录结构,可能使用不同版本的变量库,或者工作在不同的平台.这使得出现这样的情况:代码在Bob那里可以编译过去,而在Sally那里却不能.
不管你的团队使用什么样的IDE,建立一个所有的开发人员都使用的Ant buildfile.定义一个规则:所有的开发人员在将他们的代码check into到版本控制器的时候都要执行Ant build.这确保代码永远通过相同的Ant buildfile build.当出现问题时,使用标准的文件Ant buildfile来执行clean build就可以了,而不是某个人特别的IDE.
开发人员可以自由的使用任何的他们习惯的IDE或编辑器.而都要使用Ant作为最起码的通用工具来确保代码永远是可build的.
 
13.使用zipfileset
人们常常使用Ant来产生WAR,JAR,ZIP和EAR文件.这些文件一般要求一个详细的内部目录结构,它常常和你的源代码,build环境的目录结构不一致.
一个非常通用的方法是写一个Ant target来拷贝完整目录的文件到一个临时区域,在这里使用想要的目录结构,然后在这里创建一个存档.但这不是最有效的方法.使用zipfileset是一个更好的解决方法.这个方法使得你可以从任何地方选择文件,然后使用一个不同的目录结构把它们放到归档文件里.如下是一个小小的例子:
    appxml="${dir.resources}/application.xml">
 
 
   
 
 
   
   
 
 
   
   
   
 
 
   
 
在这个例子里,所有的JAR文件被放置在EAR的lib目录下. hr.jarbilling.jar文件从我们的build目录拷贝,因而我们使用zipfileset来移动它们到EAR内部的lib目录.prefix属性给定了在EAR文件里的目标目录.
 
14.执行Clean Build测试
假设你的buildfile有一个cleancompile任务,执行如下的测试.首先,输入ant clean命令.其次,输入ant compile命令.然后,又输入ant compile命令.第三步可能什么也没做.如果文件编译了第二次,你的buildfile一定有错误.
仅仅当输入文件相对于对应的输出文件有了修改,buildfile才能工作.一个build过程如编译,拷贝或者执行其他的工作执行工作,而这些工作并不是必需的,那么这样的buildfile是低效率的.当一个项目的size慢慢变大时,即使一点点的低效也会变成一个大问题.
 
15.避免平台相关的Ant包装
因为一些原因,一些人喜欢使用一些称之为compile的简单批处理文件或脚本来包装他们的产品.看看脚本内部,你会找到如下的命令:
ant compile
开发人员熟悉Ant,并且有能力使用ant compile.不要包括一些除了调用Ant就没有其他任务的平台相关的脚本.当人们第一次看到你的工具的时候,你的脚本对于他们研究和理解你的工具来说就是相当令人烦恼的.同时,你也可能不能为每一个操作系统提供脚本,这对于一些用户来说是一个真正的烦恼.
 
总结
太多的公司依赖手工过程或特别的程序来编译代码以获得一个软件发布.团队如果没有使用Ant或其他工具定义一个良好的build过程的话,当代码对于一些开发人员可以编译通过而一些则不行的时候,就会浪费惊人的时间来跟踪问题.
创建和维护build脚本并不是一个迷人的工作,但是十分必要.一个优良的Ant buildfile会使得你关注你最喜欢的工作—编码!
 
参考文档
  • Ant
  • AntGraph: A tool for visualizing Ant dependencies
  • Ant: The Definitive Guide, O'Reilly
  • Java Extreme Programming Cookbook, O'Reilly
Eric M. BurkeO'Reilly的签约作者,Object Computing, Inc. in St. Louis, MO的首席软件工程师.
 


原创粉丝点击