国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 學(xué)院 > 開(kāi)發(fā)設(shè)計(jì) > 正文

關(guān)于build tool的構(gòu)想--從ant說(shuō)起

2019-11-18 12:53:57
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

  ant——你要是不會(huì),出門都不好意思跟人打招呼的那個(gè)ant,每個(gè)人都用過(guò)。
  
  它是一個(gè)build tool,用xml來(lái)描述target,用xml來(lái)設(shè)置每個(gè)task的屬性。
  
  ant的好處我們都體會(huì)到了
  
  1。什么都是xml。而xml地球人都知道。
  
  2。功能強(qiáng)大。從編譯java文件到checkin cvs,反正幾乎你想得到的功能它都能作。
  
  3。擴(kuò)展輕易,假如你發(fā)現(xiàn)某個(gè)功能ant沒(méi)有,自己實(shí)現(xiàn)一個(gè)Task類就是。
  
  4。一些功能設(shè)計(jì)得很合理。比如javac和java自動(dòng)檢查時(shí)間戳和依靠關(guān)系檢查等等。
  
  但是,用多了,發(fā)現(xiàn)缺點(diǎn)也不少
  
  1。什么都是xml。而xml的語(yǔ)法有些時(shí)候顯得很繁瑣。
  
  2。xml用來(lái)描述邏輯異常笨拙。
  
  3。所有的邏輯都只能在java里用Task實(shí)現(xiàn)。要做一些跨越不同Task之間的通訊很困難。比如:先讀取第一個(gè)文件的時(shí)間戳,再讀取另一個(gè)文件中儲(chǔ)存的時(shí)間戳,再根據(jù)兩個(gè)時(shí)間戳之間的距離判定下一步調(diào)用哪個(gè)task或者target。
  
  4。xml的代碼重用困難。很難定義一些常用的xml element作為庫(kù),然后再不同文件甚至項(xiàng)目中重用。
  
  5。對(duì)module的支持有限。
  
  仔細(xì)想想,其實(shí),需求發(fā)展到邏輯重用,模塊治理,不同task通訊等,已經(jīng)離描述數(shù)據(jù)這個(gè)xml最擅長(zhǎng)的領(lǐng)域越來(lái)越遠(yuǎn)了。
  
  假如把task作為基本的組成元件,那么,上面提出的幾點(diǎn)需求,都是關(guān)注于對(duì)這些基本元件的治理和組合?;蛘哒f(shuō),glue。
  
  到此,口號(hào)呼之欲出,那就是:script。
  
  很多script,作為一個(gè)完整的語(yǔ)言,是做glue的最理想選手。
  
  下面談?wù)勎覍?duì)一個(gè)基于script的built tool的構(gòu)想。
  
  首先,這個(gè)build tool仍然需要答應(yīng)通過(guò)java來(lái)自定義task。
  
  我們定義這樣一個(gè)接口:
  
  java代碼:
  
  interface Command{
  Object execute(CommandContext ctxt)
  throws Throwable;
  }
  
  我們計(jì)劃讓所有的task(我們這里叫它們command)都實(shí)現(xiàn)這個(gè)接口。
  
  CommandContext負(fù)責(zé)傳遞一些象log之類的信息。
  
  這個(gè)execute返回一個(gè)Object,這個(gè)值作為這個(gè)Command的返回值,可以用來(lái)和其它的Command通信。
  
  我們答應(yīng)這個(gè)函數(shù)拋出任何異常,這個(gè)framework將會(huì)處理這些異常。
  
  然后,定義一些基本的Command,比如,ReturnCommand負(fù)責(zé)直接返回某一個(gè)值
  
  java代碼:
  
  class ReturnCommand implements Command{
  PRivate final Object v;
  public Object execute(CommandContext ctxt){
  return v;
  }
  ReturnCommand(Object v){this.v=v;}
  }
  
  PrintCommand負(fù)責(zé)打印一句話
  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負(fù)責(zé)報(bào)告錯(cuò)誤
  
  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等。
  
  但是,假如僅僅如此,那么這個(gè)工具的能力最多也就和ant一樣。
  
  我們最需要的,是把不同的command組合起來(lái)的能力。
  
  而組合,最常見(jiàn)的,就是順序執(zhí)行。
  
  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;
  }
  }
  
  上面這個(gè)簡(jiǎn)單的Command,就負(fù)責(zé)按照順序執(zhí)行連續(xù)的兩個(gè)command。
  
  除了順序執(zhí)行,還有錯(cuò)誤處理。我們也許會(huì)希望,當(dāng)某個(gè)步驟執(zhí)行失敗時(shí),去執(zhí)行另外一個(gè)動(dòng)作,為此,我們需要先定義一個(gè)接口來(lái)描述錯(cuò)誤恢復(fù):
  
  java代碼:
  
  interface CommandRecovery{
  Command recover(Throwable th)
  throws Throwable;
  }
  
  當(dāng)某個(gè)command失敗的時(shí)候,這個(gè)接口會(huì)被調(diào)用。實(shí)現(xiàn)這個(gè)接口,可以有選擇地對(duì)某一種或者幾種錯(cuò)誤進(jìn)行恢復(fù)。
  
  然后定義具體的錯(cuò)誤恢復(fù)邏輯
  
  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,我們也可以定義一個(gè)command,讓它保證某個(gè)要害動(dòng)作必然運(yùn)行
  
  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;
  }
  }
  
  前面的順序執(zhí)行,我們是直接扔掉了前一個(gè)command的返回值。但是有些時(shí)候,我們也許希望根據(jù)第一個(gè)command的返回值來(lái)決定下一步的走向。為此,仿照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;
  }
  }
  
  先透露一下,這個(gè)BoundCommand非常重要,就是它負(fù)責(zé)在不同的command間傳遞信息。
  
  基本上的框架搭好了,下面,假設(shè)我們用一個(gè)類似groovy的腳本來(lái)寫某個(gè)target,我們的目標(biāo)是先取得當(dāng)前時(shí)間,然后打印出這個(gè)時(shí)間,然后調(diào)用javac,最后在程序結(jié)束后,打印程序結(jié)束的信息:
  
  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));
  }
  }
  );
  
  上面的代碼,先調(diào)用GetTimeCommand,取得當(dāng)前時(shí)間,然后把這個(gè)實(shí)現(xiàn)傳遞到這個(gè)匿名類中去,這個(gè)匿名類根據(jù)這個(gè)時(shí)間2,創(chuàng)建了下一步的command c2。
  
  接下來(lái),它調(diào)用兩次SeqCommand來(lái)表達(dá)兩次順序執(zhí)行。
  
  最終,當(dāng)這個(gè)command被執(zhí)行的時(shí)候,它就會(huì)完成我們上面要求的幾個(gè)步驟。
  
  不錯(cuò),挺好。達(dá)到了在步驟間任意傳遞信息的要求。甚至,我們也可以重用某些command或者函數(shù)。
  
  唯一一個(gè)問(wèn)題:這個(gè)代碼他媽的比xml還惡心!
  
  這還是很簡(jiǎn)單的情況,假如我們綜合順序,錯(cuò)誤處理,分支等等,代碼會(huì)丑陋得不忍卒睹。
  
  看來(lái),不是隨便什么script都可以勝任的。
  
  那么,讓我們先靜下心來(lái)反過(guò)來(lái)想想,我們到底希望有什么樣的語(yǔ)法呢?
  
  寫偽碼,應(yīng)該是這樣:
  
  java代碼:
  
  time <- getCurrentTime
  print time
  javac
  print "success"
  
  我們的目標(biāo)是:用腳本語(yǔ)言把前面繁雜的java代碼屏蔽起來(lái),讓語(yǔ)法簡(jiǎn)潔的腳本自動(dòng)調(diào)用上面那些臃腫的代碼。
  
  幸好,我手頭有一個(gè)腳本語(yǔ)言可以達(dá)到類似的語(yǔ)法,
  
  java代碼:
  
  do {time=now} $
  info.print time >>
  javac {classpath=...; fork=...; compatibility="1.4";...} >>
  info.print "build successful"
  
  這些do, >>等函數(shù)其實(shí)是用SeqCommand, BoundCommand等實(shí)現(xiàn)的,只不過(guò)表面上看不到了。
  
  更加復(fù)雜的邏輯,比如包含順序執(zhí)行,也包含錯(cuò)誤處理的:
  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
  
  這段腳本要先讀取當(dāng)前時(shí)間,然后打印build start;然后先后從file1和file2讀取兩個(gè)數(shù);然后把這兩個(gè)數(shù)的差額寫入file3, 最后,無(wú)論成功與否,打印build done。
  
  auto函數(shù)的意思是:當(dāng)后面那些東西執(zhí)行完畢后,無(wú)論是否出現(xiàn)exception,都要打印"build done"。
  
  你假如感愛(ài)好可以試著用java或者groovy寫寫,看看結(jié)果多么可怕。
  
  如此,一個(gè)完整的build框架就建立起來(lái)了,我們只要填空式地給系統(tǒng)加入各種command實(shí)現(xiàn),一個(gè)靈活美麗的build tool就出爐了。
  
  最后,預(yù)告一下,基于這個(gè)思想的open source 項(xiàng)目Neptune即將啟動(dòng),歡迎有志之士參加。
  
  你可以參與這個(gè)框架核心的搭建(跟我合作),也可以編寫?yīng)毩⒌母鞣NCommand來(lái)豐富框架的功能。

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 阿克陶县| 土默特左旗| 巩义市| 凉山| 石阡县| 南澳县| 石景山区| 孟津县| 冷水江市| 柳江县| 石狮市| 攀枝花市| 大埔区| 泉州市| 饶河县| 时尚| 遵义县| 岳阳县| 武义县| 克拉玛依市| 娄烦县| 临桂县| 怀柔区| 莒南县| 英吉沙县| 福建省| 大悟县| 泌阳县| 平昌县| 元谋县| 吴忠市| 舟曲县| 兴国县| 古丈县| 淮南市| 太康县| 河西区| 永德县| 丰镇市| 昌乐县| 浦东新区|