Wednesday, July 09, 2008

什么是好的构建工具?

从我开始接触到linux的时候,我就知道一个叫做makefile的东西,不过从来不敢改,不敢动,因为对linux不熟悉,对c语言不熟悉。后来据说因为makefile对平台依赖性太强,又除了一个autoconfig,那些都是linux+c的东西。
再到后来就是ant了,很多项目中也都采用了ant,非常灵活,可以说只有想不到没有做不到。但是也的确存在一个问题,在大型项目中,维护ant的构建脚本本身也会变成一个沉重的负担,需要有专门的人来负责。我曾经在一个erp公司做过,其中包含很多子项目,ant脚本到处都是,而且彼此引用,光是看那些脚本,就看到的我目瞪口呆:ant这么强大,ant这么复杂。
没有在具体的项目中应用过maven,只是想要看看为什么有了ant,人们还需要maven。 ant和maven在某种意义上是两个完全相反的东西,你可以随心所以的使用ant,编写各种插件,脚本,让它来适应你的习惯,你的哲学,但是maven,如果你想要舒服的使用maven,最好就是适应它的哲学,适应它的习惯,不然,可能不是那么好驾驭。 maven一个很重要的哲学就是‘惯例比配置更重要’,我很赞同,使用惯例我们可以减少大量的配置文件,也就减少大量的脚本,减少维护的工作。 此外,maven标准化了项目的构建工作,的确,几乎所有的项目都包含这样一个构建流程:编译,测试,打包,部署。 在ant中,每个项目都需要从头开始(其实不一定,我在使用ant的时候就借鉴了interface的思想,定义了一些标准的target,保证所有的项目这些都有同样的ant target,完成同样的任务,虽然实现可以不一样。。。。毕竟,这不是工具本身支持的,需要人们手工来做,需要相关人员的理解,共识)。
对maven还不是太熟悉,但是觉得不如ant那么容易上手,pom的结构太复杂,也有可能导致一大堆xml配置文件。 maven有一个完美的思想和比较蹩脚的实现,maven1出来的时候大受好评,但是等到maven2,人们开始越来越多的抱怨它。 mavne的依赖管理是先对于ant最出色的特性(现在ant可以和ivy接合来实现包的依赖管理),但是现在看来人们正是对这块不满意,不是说包依赖管理不好,而是这个功能实现的不好,经常会导致构建的失败。人们可能莫名其妙的发现,上一次好好好的成功的构建,在没有人们变动的情况下,怎么现在就构建失败了,其实很可能是包仓库中某个依赖变化了,而修正这个问题据说是一场噩梦。
maven是比ant更高级的构建工具,虽然有那么多问题,但是目前来看,maven还是首选,至少最流行的spring项目就是采用maven构建的。
此外,有一个ruby开发的java项目构建工具,名字叫buildr,也成为了apache的incubutor项目,但是对于很多java开发人员来讲,部署一个ruby应用可能不是那么简单,其他远不如copy一份ant或者maven那么简单。

使用diff和patch来制作补丁

首先说明使用diff和patch一般使用来处理文本文件的,对于开源项目,这两个工具是很好的帮手,比如linux的内核发布就包含很多patch,这样你不用每次都下载完整的源代码包,只需要下载目标版本和你已有版本之间的patch。
我会通过一个实际的操作示例来说明这两个工具的使用。有一个项目,名字叫testproject,已经发布了两个版本,2.2.0和2.2.1,其中上海生产环境部署了2.2.0,深圳开发中心已经发布了新版的2.2.1,现在生产环境需要升级到2.2.1,因为新版本修复了重要的bug。 方法有很多种,这里我们说明使用diff和patch的方式。
* 首先在当前工作目录下(假设为/home/jack/),从subversion上export两个版本。
> svn export http://192.168.0.253/svn/TcCenter/TcCenter/tag/2.2.0/sourcecode 2.2.0
>svn export http://192.168.0.253/svn/TcCenter/TcCenter/tag/2.2.1/sourcecode 2.2.1
* 执行diff
>diff -Nur 2.2.0 2.2.1 >220-221.patch
上面的命令会生成一个patch文件,其中-N表示如果目标目录不包含源目录中的文件,那么认为目标目录存在一个内容为空的同名文件。 -u表示生成统一的有上下文的patch文件(后面再说patch格式)。 -r表示递归处理,在处理目录的时候需要这个参数。
* 接下来,我们开始使用patch
>cp 220_221.patch 2.2.0
>cd 2.2.0
>patch -p1<220_221.patch>rm 220_221.patch
>cd ..
>diff -Nur 2.2.0 2.2.1 >220-221_new.patch
可以看到这次得到的220-221_new.patch是空文件,这就说明现在2.2.0的源代码已经被升级到2.2.1了。
* 好了,现在我们可以将这个patch文件从深圳发到上海,然后上海更新源代码,再次编译,测试,打包,部署....

===========================
subverion的diff
===========================
接下来我要试试patch能不能处理subversion的diff得到的patch文件。
* 生成subversion的diff文件,在当前工作目录下(假设为/home/jack/).
> svn diff http://192.168.0.253/svn/TcCenter/TcCenter/tag/2.2.0/sourcecode http://192.168.0.253/svn/TcCenter/TcCenter/tag/2.2.1/sourcecode > diff.patch
>cp diff.patch 2.2.0
>patch -p0rm diff.patch
>cd ..
>>diff -Nur 2.2.0 2.2.1 >220-221_new.patch
可以看到,220-221_new.patch文件为空,说明2.2.0已经升级到2.2.1了。
这里使用diff命令和使用‘svn diff’得到的patch文件格式有点不一样,使用diff得到的为:
diff -Nur 2.2.0/build.properties 2.2.1/build.properties
--- 2.2.0/build.properties 2008-04-30 15:09:14.000000000 +0800
+++ 2.2.1/build.properties 2008-06-27 15:39:36.000000000 +0800
@@ -8,7 +8,7 @@
#--------------------------------------------#
project.name=CentralPool
#DO NOT modify below setting.
-app.version=2.2.0
+app.version=2.2.1
app.vendor=Shenzhen Helper Science & Technology Co., Ltd.
builder.name=${project.name} Team

diff -Nur 2.2.0/CHANGELOG.txt 2.2.1/CHANGELOG.txt
--- 2.2.0/CHANGELOG.txt 2008-04-08 17:02:58.000000000 +0800
+++ 2.2.1/CHANGELOG.txt 2008-06-27 15:39:36.000000000 +0800
@@ -1,16 +1,16 @@
-CENTRALPOOL PROJECT CHANGELOG
+CENTRALPOOL PROJECT CHANGELOG
--------------------------------------
ChangeLog定义了当前版本相对于上一个版本之间系统发生的变更. 这些变更包括修正的bug, 系统新增的
功能,或者系统添加的补丁等等.

而使用‘svn diff’得到的patch文件为:
Index: sub-core/src/com/hengpeng/ante/game/GameFactory.java
===================================================================
--- sub-core/src/com/hengpeng/ante/game/GameFactory.java (.../2.2.0/sourcecode) (revision 241)
+++ sub-core/src/com/hengpeng/ante/game/GameFactory.java (.../2.2.1/sourcecode) (revision 241)
@@ -51,6 +51,9 @@

case Game.GAME_TYPE_SSC:
return new SSCGame(gameData);
+
+ case Game.GAME_TYPE_NUMLOTTO:
+ return new NumLottoGame(gameData);

default:
throw new HPException(AnteException.ErroCode_NoGame,
Index: sub-core/src/com/hengpeng/ante/game/Game.java
===================================================================
--- sub-core/src/com/hengpeng/ante/game/Game.java (.../2.2.0/sourcecode) (revision 241)
+++ sub-core/src/com/hengpeng/ante/game/Game.java (.../2.2.1/sourcecode) (revision 241)
@@ -75,7 +75,13 @@
*/
public static final int GAME_TYPE_SSC = 9;

+ /**
+ * 东方6+1玩法 {@value}
+ * @value 9
+ */
+ public static final int GAME_TYPE_NUMLOTTO = 10;

+
// GAME DEFINITION
public static final int GAME336 = 11;
public static final int GAME155 = 23;
幸运的是patch命令都可以处理,实际上两种都是正确的格式。

diff和patch在linux操作系统中是自带的,但是进入windows环境就没有那么容易了。不过好在有一个gunwin32的开源项目(gunwin32.sourceforge.net),gnu下的diff和patch都可以运行在window上了。