分享
 
 
 

关于buildtool的构想--从ant说起

王朝other·作者佚名  2008-05-19
窄屏简体版  字體: |||超大  

ant――你要是不会,出门都不好意思跟人打招呼的那个ant,每个人都用过。

它是一个build tool,用xml来描述target,用xml来设置每个task的属性。

ant的好处我们都体会到了

1。什么都是xml。而xml地球人都知道。

2。功能强大。从编译java文件到checkin cvs,反正几乎你想得到的功能它都能作。

3。扩展容易,如果你发现某个功能ant没有,自己实现一个Task类就是。

4。一些功能设计得很合理。比如javac和java自动检查时间戳和依赖关系检查等等。

但是,用多了,发现缺点也不少

1。什么都是xml。而xml的语法有些时候显得很繁琐。

2。xml用来描述逻辑异常笨拙。

3。所有的逻辑都只能在java里用Task实现。要做一些跨越不同Task之间的通讯很困难。比如:先读取第一个文件的时间戳,再读取另一个文件中储存的时间戳,再根据两个时间戳之间的距离判断下一步调用哪个task或者target。

4。xml的代码重用困难。很难定义一些常用的xml element作为库,然后再不同文件甚至项目中重用。

5。对module的支持有限。

仔细想想,其实,需求发展到逻辑重用,模块管理,不同task通讯等,已经离描述数据这个xml最擅长的领域越来越远了。

如果把task作为基本的组成元件,那么,上面提出的几点需求,都是关注于对这些基本元件的管理和组合。或者说,glue。

到此,口号呼之欲出,那就是:script。

很多script,作为一个完整的语言,是做glue的最理想选手。

下面谈谈我对一个基于script的built tool的构想。

首先,这个build tool仍然需要允许通过java来自定义task。

我们定义这样一个接口:

java代码:

interface Command{

Object execute(CommandContext ctxt)

throws Throwable;

}

我们计划让所有的task(我们这里叫它们command)都实现这个接口。

CommandContext负责传递一些象log之类的信息。

这个execute返回一个Object,这个值作为这个Command的返回值,可以用来和其它的Command通信。

我们允许这个函数抛出任何异常,这个framework将会处理这些异常。

然后,定义一些基本的Command,比如,ReturnCommand负责直接返回某一个值

java代码:

class ReturnCommand implements Command{

private final Object v;

public Object execute(CommandContext ctxt){

return v;

}

ReturnCommand(Object v){this.v=v;}

}

PrintCommand负责打印一句话

java代码:

class PrintCommand implements Command{

private final String msg;

public Object execute(CommandContext ctxt){

ctxt.getLogger().log(msg);

return null;

}

PrintCommand(String msg){this.msg=msg;}

}

FailCommand负责报告错误

java代码:

class FailCommand implements Command{

private final String msg;

public Object execute(CommandContext ctxt){

throw new CommandException(msg);

}

FailCommand (String msg){this.msg=msg;}

}

如此等等。这样的基本元件还有很多,比如file copy, javac, zip, jar等。

但是,如果仅仅如此,那么这个工具的能力最多也就和ant一样。

我们最需要的,是把不同的command组合起来的能力。

而组合,最常见的,就是顺序执行。

java代码:

class SeqCommand implements Command{

private final Command c1;

private final Command c2;

public Object execute(CommandContext ctxt){

c1.execute(ctxt);

return c2.execute(ctxt);

}

SeqCommand (Command c1, Command c2){

this.c1 = c1;

this.c2 = c2;

}

}

上面这个简单的Command,就负责按照顺序执行连续的两个command。

除了顺序执行,还有错误处理。我们也许会希望,当某个步骤执行失败时,去执行另外一个动作,为此,我们需要先定义一个接口来描述错误恢复:

java代码:

interface CommandRecovery{

Command recover(Throwable th)

throws Throwable;

}

当某个command失败的时候,这个接口会被调用。实现这个接口,可以有选择地对某一种或者几种错误进行恢复。

然后定义具体的错误恢复逻辑

java代码:

class RecoveredCommand implements Command{

private final Command c1;

private final CommandRecovery c2;

public Object execute(CommandContext ctxt){

try{

return c1.execute(ctxt);

}

catch(Throwable th){

return c2.recover(th).execute(ctxt);

}

}

RecoveredCommand (Command c1, CommandRecovery c2){

this.c1 = c1;

this.c2 = c2;

}

}

有try-catch,就有try-finally,我们也可以定义一个command,让它保证某个关键动作必然运行

java代码:

class FinallyCommand implements Command{

private final Command c1;

private final Command c2;

public Object execute(CommandContext ctxt){

try{

return c1.execute(ctxt);

}

finally{

c2.execute(ctxt);

}

}

FinallyCommand (Command c1, Command 2){

this.c1 = c1;

this.c2 = c2;

}

}

前面的顺序执行,我们是直接扔掉了前一个command的返回值。但是有些时候,我们也许希望根据第一个command的返回值来决定下一步的走向。为此,仿照CommandRecovery接口,定义CommandBinder接口:

java代码:

interface CommandBinder{

Command bind(Object v);

}

然后定义BoundCommand类:

java代码:

class BoundCommand implements Command{

private final Command c1;

private final CommandBinder c2;

public Object execute(CommandContext ctxt){

final Object v =

return c1.execute(ctxt);

return c2.bind(v).execute(ctxt);

}

BoundCommand (Command c1, CommandBinder c2){

this.c1 = c1;

this.c2 = c2;

}

}

先透露一下,这个BoundCommand非常重要,就是它负责在不同的command间传递信息。

基本上的框架搭好了,下面,假设我们用一个类似groovy的脚本来写某个target,我们的目标是先取得当前时间,然后打印出这个时间,然后调用javac,最后在程序结束后,打印程序结束的信息:

java代码:

new BoundCommand(

new GetTimeCommand(),

new CommandBinder(){

public Command bind(Object v){

final Command c2 = new PrintCommand("build time is "+v);

final Command javacc = new JavaCCommand();

final Command done = new PrintCommand("build successful");

return new SeqCommand(c2, new SeqCommand(javacc, done));

}

}

);

上面的代码,先调用GetTimeCommand,取得当前时间,然后把这个实现传递到这个匿名类中去,这个匿名类根据这个时间2,创建了下一步的command c2。

接下来,它调用两次SeqCommand来表达两次顺序执行。

最终,当这个command被执行的时候,它就会完成我们上面要求的几个步骤。

不错,挺好。达到了在步骤间任意传递信息的要求。甚至,我们也可以重用某些command或者函数。

唯一一个问题:这个代码他妈的比xml还恶心!

这还是很简单的情况,如果我们综合顺序,错误处理,分支等等,代码会丑陋得不忍卒睹。

看来,不是随便什么script都可以胜任的。

那么,让我们先静下心来反过来想想,我们到底希望有什么样的语法呢?

写伪码,应该是这样:

java代码:

time <- getCurrentTime

print time

javac

print "success"

我们的目标是:用脚本语言把前面繁杂的java代码屏蔽起来,让语法简洁的脚本自动调用上面那些臃肿的代码。

幸好,我手头有一个脚本语言可以达到类似的语法,

java代码:

do {time=now} $

info.print time

javac {classpath=...; fork=...; compatibility="1.4";...}

info.print "build successful"

这些do, 等函数其实是用SeqCommand, BoundCommand等实现的,只不过表面上看不到了。

更加复杂的逻辑,比如包含顺序执行,也包含错误处理的:

java代码:

auto (info.println "build done") $

do {time=now} $

info.println ("build starting at " + time)

do {t1 = readFile "file1"} $

do {t2 = readFile "file2"} $

let

diff = t2 - t1;

writeFile "file3" diff

end

这段脚本要先读取当前时间,然后打印build start;然后先后从file1和file2读取两个数;然后把这两个数的差额写入file3, 最后,无论成功与否,打印build done。

auto函数的意思是:当后面那些东西执行完毕后,无论是否出现exception,都要打印"build done"。

你如果感兴趣可以试着用java或者groovy写写,看看结果多么可怕。

如此,一个完整的build框架就建立起来了,我们只要填空式地给系统加入各种command实现,一个灵活优美的build tool就出炉了。

最后,预告一下,基于这个思想的open source 项目Neptune即将启动,欢迎有志之士参加。

你可以参与这个框架核心的搭建(跟我合作),也可以编写独立的各种Command来丰富框架的功能。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有