蚂蚁怒了(Ant in Anger)(节选)(二)

类别:Java 点击:0 评论:0 推荐:

团队开发过程

       即使团队的每个成员都被允许自行选择IDE或者编辑器,甚至操作系统,你还是需要设置一个为每种平台设置一个基线。特别使JDK和Jar文件需要完全同步。理想情况是为所有的开发者和目标系统选择最新的稳定的可用java/JDK版本并且一段时间内一只使用它。

考虑派一个人负责控制所有使用的工具的版本——特别是开源软件工具有每夜构建的新版本可用之时。除非需要,这些工具只需每个月更新一次,或者当正式版本发行时。

       另一种好的策略是使用统一的目录树,增加新的额外工具在那颗树中。所有的参考都可以和此树相关。如果团队成员希望增加一个项目的目录到他们的路径下,那么就可以在路径下包括命令行工具——包括那些Ant exec任务调用的工具。把每件事都放在代码控制工具之下,然后你就可以完全从CVS或同类型工具中获得构建/运行环境的一站式服务。

使用Ant进行分发(Deploy)

       Ant和以前的工具比如说Make有个很大的不同,就是分发java到远程站点的过程已经合理地被融入到Ant之中了。过去我们不得不亲自来做这些事情,而且很多人还做了很大的努力以使这种任务更简单。

       Ant可以通过jar/tar/zip打包文件来进行分发,用根据jar扩展的war任务来更好的进行servlet分发。Jlink是一个jar生成工具,让你得以合并多个子jar文件。在由各个子项目产生了独立的jar文件,而最终需要一个合并的jar文件时,Jlink是个理想的构建过程。Cab可以在Win32平台下用来构建Cab文件,如果你不得不为IE分发的话,这是很有用的。

       ftp任务让你将素材上传到服务器上。小心的将你的ftp密码放在构建文件中——有严格的访问权限控制更好。如果你需要在上传文件前确定文件是否有Unix扩展名,FixCRLF任务在这时候是个很有用的过渡步骤。能够提供更安全的上传文件到web服务器的WebDav任务长久以来一直被讨论,但是它现在还在待实现列表中等待实现。有意思的是在jakarta活动库中确实有这个任务。MacOS X, Linux 和 Windows XP全部都支持WebDav文件系统,你甚至可以穿过防火墙使用<copy>来分发。

       EJB任务能够半自动的分发EJB,而<serverdeploy>套件能够分发到多个服务器,Ant鼓励用户编写能够在他们的服务器上重新发布的他们自己的分发任务。比如说,Tomcat4.1安装程序就包括分发,解除分发和重新载入web应用程序的任务。

       最后,当然也有在使用Copy和Copydir拷贝文件到目标地点时的回滚(fallback)任务,或者只是使用Mail或附加的MimeMail发送给用户或进程。在一个工程里,我们的团队甚至通过一长串Copy任务的构建文件使用Ant去构建CD镜像,而且居然工作的非常好,当然也比我们将它们邮寄给myrealbox.box的免费email服务,然后从遥远终端的web浏览器将其下载,其中我们还要通过WinNT远程桌面连接运行,然后通过SSH通道传输要容易的多。

目录结构

       你如何组织你的极度依赖工程的目录?作为开始,这里有一些目录布局的模式。所有的Jakarta项目都遵循一种大概相似的风格,这样很容易的可以从一个项目转入另一个,而且需要的时候也很容易清理。

简单的工程

这个工程包含子目录:

bin

基本库

build

构造树;由Ant创建并且可以在clean任务中清除

dist

在这里分发输出;Ant创建Clean清除

doc

手工文档

lib

将java库导出至此

src

原文件按照匹配包名的框架结构放在层次结构的树中;Javac的依赖规则需此目录

Bin,lib和src目录应处于源码控制下。其它的文件——inf文件,图像等等放在分发jar里的另一个内容目录下,这些文件也可放在源文件夹下, web.xml和类似的清单文件(manifest) 放在元数据(metadata)目录, web内容——JSP, html,图像等等放在web目录。将这些内容一起放在一个目录里(或者子目录里),这样会使在分发前测试链接更容易。实际产生分发包时——比如说war文件——可以由适当的Ant任务解决:不需要完全的按照分发层次建立你的源文件目录树。

Javadoc输出可以被导向到build/下的doc/文件夹,或者到doc/javadoc。

接口和实现分离

如果接口和实现的代码分离, 这样就可以进行微小的变化,建立单独的路径,存放接口文件——或者更好就构建为jar:一个jar作为接口,一个作为实现。

松耦合的子项目

松耦合的多个项目有它们自己目录树,源代码的存取权限。唯一的例外是bin和lib目录跨越所有的项目。有时候这是很好的——这有助于保持xerces.jar的各个拷贝同步,但有时候却很糟糕——它会在单元测试完成前就更新了功能jar文件。

为了保持横跨所有子项目的单一的构建,使用能够向下调用子项目的父目录的build.xml文件。

如果不同的团队有不同的存取/提交权限,这种风格工作的很好。风险是由于给了子工程更多的回旋余地,可能最后导致不兼容的源文件,库和构建过程,而且增加了你的工作量,和集成的痛苦。

保持控制一个清晰的松耦合项目集的唯一方法是保持一个完全的自动构建和建议所有代码兼容性的测试进程。Sam Ruby就运行这样的进程检测所有的Apache Java库并通知每个人,当一些事情发生的时候;你自己的项目苦于一考虑使用Cruise Control进行自动、连续和后台运行的构建进程。

紧耦合的子项目

紧耦合的项目将所有的源都放在同一个树里;不同的工程有不同的子目录。构建文件可以被移动到这些子目录中(比方说src/com/iseran/core和src/com/iseran/extras),或者把它放在顶极目录——使用独立的构建文件命名为core.xml和extras.xml。

如果每个人彼此信任,而且子项目不是庞大复杂的话,这种项目风格工作的很好。风险是分割项目以更松的耦合设计将成为项目前进的必须——但是目前为止由于进度的压力和纠缠不清的构建文件使得项目分割几近于不可能。如果是这样的话,那就进行保持直到有时间对项目目录结构进行重构。

Ant更新策略

一旦你开始使用Ant,你就得为你的团队制定一个何时和如何进行代码更新的策略。一个简单的策略是“任何承受沉重压力的官方版本都将所有不重要的任务(像睡觉的时候看见日光一样)推进了火炉”。这使你与变化和Ant新开发版本的偶尔的不稳定隔绝,但主要缺点是它是你与Ant新加入的任务和特性隔绝。

一次更新行为经常要修改build.xml。大部分的修改都试图向后兼容,但是有时不兼容的改变是必须的。这就是为什么每次大的里程碑版本发布后都需要做一次平静的更新。这也是为什么在CVS树中包含ant.jar和相关的文件能帮助确定你的软件的旧版本是否仍然能够被构建。

最富进攻性的策略是每周甚至每日都获得Ant源文件的快照,构建它,使用它。这强迫你要更有规律的调整build.xml,以使新任务和属性能稳定工作。否则你真的不得不想要新的特性,享受免费的额外工作,或者乐于给你的同僚增添工作的麻烦。

一旦你使用新任务扩展Ant,正常的构建可能突然就变得易于失败。最新的Ant构建仍然总是书写扩展的最好选择,因为你能利用基础类进行有秩序的提高。这也能防止你浪费时间在那些已经完成的事情上。一件作一些复杂事情比如说连接EJB引擎,SOAP服务器或者仅仅是将文本文件改为大写的这样新提交的任务也许才是你需要做的——所以,抓住它,提高它,并且贡献这种提升给世界上的其它人。这当然要比你开始孤立的在Ant 0.8完成你的“文本大小写转换”任务,六个月后宣布它的存在并且毫不客气的发现你所获得的对已有的实现是非常有帮助的要好。这种过程最终的好处是它使你的任务增加给Ant CVS树变得简单,而且当Ant接纳了所有你需要使你项目工作的变化时,提前了日期。如果这发生了的话,你就可以转向一个Ant的官方版本,并且度过所有的难关。

你也应该进展开发邮件清单(dev mailing list),因为这是其它开发者可以提交他们的工作,困难和经验的地方。容量可以很高:每天40个以上的消息,所以考虑发送他们到一个你不经常使用的email地址。并且不让团队里的任何人注册,否则可能会很分心。

把所有放在一起

Ant构建过程到底是个什么样子的呢?为了简化我们假定一个单一的目录结构,构建文件应该包含许多顶极的目标:

l         build - (增量)构建

l         test – 运行Junit测试

l         clean – 清除输出目录

l         deploy – 装载jar,war,还有任何的可执行的系统

l         publish – 输出源或二进制流到任何分发目录

l         fetch – 从CVS树得到最新的源文件

l         docs/javadocs – 文档产生

l         all - clean, fetch, build, test, docs, deploy

l         main – 默认构建过程 (经常是build 或者build & test)

子项目'web', 'bean-1', 'bean-2'可以被赋予它们自己的构建文件——web.xml, bean-1.xml, bean-2.xml——使用同一个入口点。其它与数据库,网站图像等相关的顶极目标也可以考虑,如果它们也是过程的一部分的话。

Debug/release开关可以用在编译任务前就被调用的,定义了相关属性的,单独的初始化目标控制。Ant调用在这里是个骗局,因为它允许你可以有两个属性初始化路径。

内部目标应该在构造过程时使用:

l         init – 初始化属性,附加任务,读入每个用户的属性文件

l         init-release – 初始化Release属性

l         compile – 实际的编译

l         link/jar – 制作jar文件或同类事物

l         staging – 在被发布到生产站点前进行预发布进程并且测试

Debug/release的转换可以通过设定init-release属性的状态解决,比如release.build 被设置:-

<target name="init-release" if="release.build">

    <property name="build.debuglevel" value="lines,source"/>   

  </target>

然后你的独立的目标,比如"compile",依靠这个状态的目标;有默认的属性被设置,然后这个属性被使用。因为Ant属性时不变的,如果release目标被运行,它的设置就会改写默认的值:

<target name="compile" depends="init,init-release">

    <property name="build.debuglevel" value="lines,vars,source"/>

    <echo>debug level=${build.debuglevel}</echo>

    <javac destdir="${build.classes.dir}"

       debug="true"

       debuglevel="${build.debuglevel}"

       includeAntRuntime="false"

       srcdir="src">

      <classpath refid="compile.classpath"/>

    </javac>

  </target>

结果,我们现在有了构建文件,release模式只能包含文件名和行调试信息(使用在bug报告),而开发系统还包含变量信息。

在初始化任务中能够重复定义的项目名字属性很有用。这让你能够从多个构建文件中辨别出那个Ant文件是损坏的。

什么参加内部Ant任务要看你自己的项目。一个非常重要的策略是“通过引用保留路径重新定义”——你可以通过给路径一个ID来复用它,然后通过‘refid’属性去引用它们。你只需要在文件中定义一个共享路径,文件集也可以用相似的方法复用。

一旦你设置好了路径结构,并且定义好了Ant任务,那就应该开始译码了。具有早优先级的目标必须在自动测试进程中被设置,因为它不仅帮助确保代码工作,也能验证构建过程工作。

好了。构建文件不像新增加的源文件需要改变,只有当你想改名发布或者部分构建过程。在一些点上你可能武断的想重新构造整个的构建进程,重构项目等,但是接下来你的构建文件应该作为分割构建过程的基础——仅仅将共用的属性拖到一个供构建文件可以读入的属性文件中,保持目标名统一和项目协调工作。重新构造源代码控制系统经常是更困难的工作。

结束语

软件开发是一种乐趣。能够置身于处于集成压力的紧迫项目的旋涡之中,并且试图在该死的截至日期前将所有的事情变成代码,这很有趣——而且的确令人振奋。在开发过程中增加一点自动化或许将减少一些混乱,减少一些乐趣,但是这将成为你能够控制你的开发过程的开始。你能够同样拥有你的乐趣,你有更少的事情需要去担心,更短的构建/测试/发布周期,却有更多的时间去享受有你自己特色的踱步或者滑雪这种重要的事情。所以就那样作吧,并拥有你自己的乐趣!

 

本文地址:http://com.8s8s.com/it/it16490.htm