随着项目越来越大,你的ANT脚本变得越来越臃肿,越来越依赖antcontrib来实现构建逻辑?不想放弃对构建过程和产出做精细的控制?恨不得自己写ANT Task?
等一等,在你考虑转向Maven或者真正卷起袖子开始研究ANT Task的API之前,先听我向你推荐Ruby/JRuby。相信我,也许这才是你真正需要的。
在我看来构建脚本最主要的要求是表达能力和控制能力,表达能力是我们应该能够很方便的告诉它我们要它做什么,而控制能力是我们告诉它如何去做,除了必要的构建命令的参数、依赖关系之外,它应该能够理解我们其他一些特殊要求。在项目相对简单时,ANT的XML格式的"脚本语言"能够比较好的表达构建者的要求,至少比纯Java的方式更加简单清晰,于是几乎从它诞生之日就成为Java领域当仁不让的头号构建工具。但是ANT也有它不够用的时候,尤其在控制能力上,为了实现实际使用中越来越复杂越来越精细的对构建过程和产出的要求,ANT的使用者们开始对ANT进行扩展,最具影响力的"非官方"扩展可能就是antcontrib了,很多实际Java项目的构建文件中我们都能够找到它的影子。但是一堆taskdef和<for><if><variable><substring><stringutil><length><math>之类的夹杂在ANT脚本里,怎么看怎么觉得别扭,也严重影响构建脚本的可读性。
我发现我需要的是一个具有完整功能脚本语言来写我的构建逻辑:一方面它要足够简单,我需要清晰的表达我的意图,另一方面当我需要的时候它要足够强大来帮我实现精细的控制。我自然而然想到Ruby。
由于Ruby是一个功能完整的工具,完全自己写Ruby脚本来调用Java自己的构建工具如javac、jar等等并非不可能,但是既然已经有人做了轮子,我们拿来用就是了,大不了自己再改装一下,也少走些弯路。时下比较流行的解决方案有Buildr、Raven、Rant和Antwrap,它们各有特点,大家可以根据需要进行选择。
我最终选了其中最"轻量"的Antwrap,原因嘛,我不想学新的API,我已经熟悉ANT常用的Task,而且我希望尽可能多的自己控制构建的过程和产出。Antwrap最能够满足我的需要。而前不久JRuby刚刚发布了1.0版,这样一来Ruby和Java的跨界引用变得更加容易,Ruby的实现自然就选择了JRuby。
安装JRuby和Antwrap相当容易,只要把下载的jruby-bin-1.0.tar.gz/.zip解压到本地,确保JAVA_HOME和CLASSPATH的配置,然后gem install Antwrap (选择Java版) 即可。为了顺利加载Antwrap,需要在CLASSPATH中包含ANT的ant.jar和ant-launcher.jar。(当然,如果想更加方便的share你的成果,可以把ant的文件拷贝到jruby目录下,在jruby的启动脚本加入必要的export/set命令,然后打包,这样别人只要从你提供的zip包解压出来即可使用。)
为了给大家一个直观的感觉,举个简单的例子:
ruby 代码
- require 'rubygems'
- gem 'Antwrap'
- require 'antwrap'
- @ant=AntProject.new(:name=>"SampleAntwrapBuild", :basedir=>".")
- @cvsroot=":pserver:cvsuser:password@10.10.10.1/cvsrepo/SampleProduct"
- def cvscheckout
- @ant.cvs(
- :cvsroot=>"#{@cvsroot}",
- :command=>"checkout -A",
- :package=>".",
- :dest=>"cvsoriginal",
- :compressionlevel=>"9")
- end
- def cvsupdate
- @ant.cvs(
- :cvsroot=>"#{@cvsroot}",
- :command=>"update -A -d",
- :package=>".",
- :dest=>"cvsoriginal",
- :compressionlevel=>"9")
- end
- def compile(project_name)
- @ant.javac(
- :srcdir=>"cvsoriginal/#{project_name}/src",
- :destdir=>"cvsoriginal/#{project_name}",
- :target=>"1.5",
- :encoding=>"GBK")
- @ant.copy( :todir=>"cvsoriginal/#{project_name}") do
- fileset(:dir=>"cvsoriginal/#{project_name}/src") do
- exclude(:name=>"**/*.java")
- end
- end
- @ant.jar(
- :destfile=>"build/#{project_name}.jar",
- :basedir=>"cvsoriginal/#{project_name}",
- :manifest=>"MANIFEST.MF") do
- exclude(:name=>"src/**")
- end
- end
- # to actually call your target
- if ARGV.empty?
- puts "Usage: jruby #{$0} [target]"
- else
- eval ARGV[0]
- end
可以看到几乎都是我们熟悉的ANT Task,只是更加紧凑更加灵活,一旦掌握了最最基本的Ruby语法,用它写出功能强大的构建脚本可以说是分分钟搞定。