<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    weidagang2046的專欄

    物格而后知致
    隨筆 - 8, 文章 - 409, 評論 - 101, 引用 - 0
    數據加載中……

    用Ant構建

     盡管編程--編寫和編譯代碼--是軟件開發中最明顯的部分,但并不意味著就是唯一的部分。就像你已經看到過的,測試也很重要。但交付一個完成的產品還需要更多的步驟:你還需要文檔,打包,以及部署你所開發的軟件。手工進行這些工作是煩悶的、重復性的,還容易出錯。這是個迫切需要自動化進行的任務。

    5.1 需要一個正式的構建過程


      你已經經歷了測試--編碼,測試--編碼的循環,并最終得到了一些人們可以實際使用的東西。那么該如何交付該功能呢?
      下一步就是要以確切的詞匯來標明這個產品到底是什么。它是一個API嗎?還是一個具有圖形界面的可執行程序?它是被打包在一個JAR文件中嗎?它是否包含文檔?示例代碼呢?是否所有這些東西都要刻錄在一張光盤上?還是要壓縮在一塊兒以便通過因特網上交付使用?
      一旦你作出了決定,Eclipse會簡化這些步驟的執行。在你保存了源代碼并編譯了所有東西之后,就能立刻運行單元測試,用Javadoc向導創建文檔(假設這對于你的讀者來說足夠了),把你的類導出到一個JAR文件,并把所有的東西放到一個p文件中。如果你只是要非正式地把你正在做的游戲的最新版本發給一個朋友,這個過程是不錯的,但如果你要把一個產品交付給一個客戶或公眾,你是不會這么做的。有太多的事情會出錯:忘了刷新源碼樹,忘了運行單元測試,沒注意到一個失敗的測試,生成一個Javadoc時選擇了錯誤的選項,漏拷了源碼文件,打錯了p文件名--可能的問題是無窮無盡的。當從事該項目的人不止一個時,這些問題會變得更加復雜。
      進行一個獨立的構建過程的主要原因之一是可重復性。最理想的是,你希望能夠只需在一臺工作站打入一條命令,機器就會攪拌一會兒它的硬盤,并且在最后(如果一切順利并且成功運行了單元測試),吐出一個p文件或者一張光盤。減少了人工干預也就減少了構建過程中出錯的幾率,并且任何發生的錯誤都可以被持續改正。
      因為使用圖形界面來自動化過程比較困難,構建過程通常設計為在命令行下運行。那樣的話,通過使用一條簡單的命令,或者使用Unix/Linux的chron命令或Windows的at命令設置每天的指定時間自動進行,就可以擺脫這個問題。
      使用外部的、獨立的構建過程的額外好處是,它讓開發者能夠自由使用自己選擇的開發環境。這看起來不像是一個緊迫的需求,但是強迫開發者--尤其是那些很有經驗的--放棄他們已經精通的工具,對生產力是有害的。

    5.1.1 創建構建目錄結構

      在我們查看特定的構建工具之前,讓我們來考慮一下這個任務:利用你在前面的章節中作為示例開始開發的基于文件的持久組件(Persistence),計劃和組織構建過程。想象一下,由于某些原因,你(或你的主管)決定將該產品實用化。規范化構建過程的第一步就是組織你的文件,以便能夠清晰的區分源代碼和其他資源,臨時文件,以及可交付文件。

    5.1.2 分離源碼和構建目錄

      在你創建了persistence對象之后,你沒有停下來考慮目錄結構。你簡單的接受了Eclipse提供的默認配置:項目目錄等同于源碼目錄。這種結構的結果是,源碼文件、測試類、編譯好的類文件都混在一起了:

    \persistence
      \org
        \eclipseguide
          \astronomy
            *.class
            *.java

              \persistence
                *.class
                *.java

      因為在源碼文件和生成文件之間沒有明確的界限,在開始一個新構建之前,刪除生成文件就需要你在某些目錄中刪除某些文件。這不難,但使用一個獨立的目錄更加簡單可靠,這樣你就能在開始構建之前把整個目錄弄走--這就避免了過期的文件意外地弄糟了構建過程,從而導致莫名其妙的錯誤的可能性。
      與混合源碼文件和類文件相比,一個更好的設計是在項目目錄中,為兩者創建單獨的目錄,像這樣:

    \persistence
      \src
        \org
          \eclipseguide
            \astronomy
              *.java
            \persistence
              *.java
      \bin
        \org
          \eclipseguide
            \astronomy
              *.class
            \persistence
              *.class

      Eclipse在項目結構上十分靈活。如果你一開始就已經知道你需要一個正式構建過程,當你創建項目時就已經能指定src作為源碼目錄。那你就不會在輸入名稱后立即點[完成]來接受默認配置,而可以點[下一步]前進到另一個對話框,那里提供了選項以增加一個新的源碼目錄;在[源]頁面上點擊[添加文件夾]就可以創建新的文件夾名(參看圖5.1)。

    圖5.1 新的源碼文件夾。你可以在創建一個新項目時定義一個單獨的源碼文件夾,然后Eclipse會幫助建立一個單獨的輸出文件夾。


      但別擔心--你不必全部倒回去重新照做一遍。所謂敏捷開發指的就是增量開發--按部就班,并且如果沒有好的理由,絕不以吃力的方式做任何事情。就像你至今可能預想的那樣,Eclipse輕松地完成這些變換。你所需做的一切只是新建一個源碼文件夾(src)并把你的源碼移動到里面。由Eclipse負責建立新的獨立的輸出文件夾(bin)及其子目錄。(此外,當你用Eclipse構建時,它會將所有的class文件放置到正確的位置。)
      在Java透視圖中,在項目名稱(Persistence)上右擊鼠標,選擇[新建]->[源碼文件夾],然后輸入 src 作為文件夾名(參看圖5.2)。注意對話框頂部的注釋:為避免重復,現有的項目源碼文件夾入口將被替換,并且輸出文件夾將被設置為'Persistence/bin'。這正是你所想要的,那么就點擊[完成]。(如果你不想把輸出文件夾命名為bin--比如說想把它命名為build--你可以在稍后通過從項目的快捷菜單中選擇[屬性]->[Java 構建路徑]->[源]來改變缺省的輸出文件夾。)

    圖5.2 將一個源碼文件夾添加到一個現存的項目中。Eclipse自動創建一個新的輸出目錄。


      現在,要把源碼文件移動到新的src目錄中,右鍵點擊頂層源碼目錄(org),選擇[重構]->[移動],并選擇Persistence下面的src目錄(參看圖5.3)。(你可以安全地忽略那個警告:到缺省包的引用將不會被更新。)或者--甚至更簡單--在包瀏覽器中點擊org文件夾并拖動到src文件夾上。

    圖5.3 更多的重構:將源碼樹移動到新的src目錄。


    5.1.3 為可發布文件創建一個目錄

      創建一個獨立的輸出目錄是一個好的開始,但不是發布軟件的全部。一堆目錄里面的一堆類不是一個產品。你需要決定要將哪一片發布給你的客戶,并把它們干凈地包裝在一個JAR文件中。任何好的軟件產品都要求有文檔,因為你的產品是為開發者設計的一個軟件組件,它很可能只需包含Javadoc就足夠了。(在前面章節的代碼中,你對于包含Javadoc注釋是不嚴格的,這種情形下,你應當考慮更正--但現在,讓我們把它作為讀者--你的一個借口。)
      假定Persistence組件是普遍關心的唯一部分。你需要將persistence類和astronomy類、test類分離開來。記住你仍需構建所有的類以便測試persistence類,要這么做,一個好的方法是再創建另一個目錄,里面僅放置那些組成可發布產品的文件。你將把它稱為dist(distribution的簡稱),而它將包含Persistence組件的JAR文件和Javadoc:

    \persistence
      \src
        \...
      \bin
        \...
      \dist
        \lib
          persistence.jar
        \doc
          *.html

      在你更改src目錄中的Java源文件時,Eclipse自動保持bin目錄中的類文件最新,但除此之外的事情都得你來做。如前所述,你可以用Eclipse手動去做;但為了可靠性和一致性,使用一個構建工具來自動化這個過程會好很多。

    5.2 Make:一個回顧


      在我們考慮事實上的Java標準構建工具Ant之前,讓我們來快速地回顧一下傳統的構建工具--Make--以便從其他角度看看Ant所帶來的優點。有許多不同的增強版Make,包括各種Unix變種,Gnu make,以及微軟的NMAKE(New Make),不過它們大體上都是相似的。
      Make接收一個make文件,文件中包含一個目標列表,每個目標后都接著一個腳本,當調用對應目標時運行。(默認狀況下,Make查找一個特定名字的文件,通常是makefile,但你一般可以在命令行強制指定一個其他名字的文件。)一個目標和它的命令有時稱為規則(rules)
      一個目標可以有依賴(dependencies);也就是其他的必須首先被評估的目標。當Make運行時,它查找第一個目標的依賴以查看它們是否已經存在。如果目標是文件,并且依賴也是文件,Make會比較它們的時間戳;如果依賴比目標新,Make會運行規則以更新目標。
      這是一個make文件規則的一般格式:

    TARGET: dependencies ...
      commands
      ...

      一個小的,簡單的,用來從兩個C源文件構建一個程序的make文件看起來可能會是這樣:

    myapp.exe: main.obj aux.obj
      link main.obj aux.obj -o myapp.exe

    main.obj: main.c
      cc main.c

    aux.obj: aux.c
      cc aux.c

      如果你不熟悉C,別擔心具體細節,只要注意從C源代碼創建一個可執行程序一般需要的兩個步驟:編譯源代碼為目標文件(示例中的.obj文件)以及把所有的目標文件鏈接為一個單一的可執行文件(示例中的.exe文件)。
      第一個目標,myapp.exe,是一條規定了如何通過連接兩個目標文件以構建可執行文件的規則。默認情況下,make文件中的第一個目標就是由Make執行的。Make計算依賴鏈以確保鏈上的每個東西都是最新的。你第一次運行這個make文件時,它首先編譯main.obj和aux.obj,然后把它們連接起來以構建程序myapp.exe。
      如果你修改了aux.c并再次運行make文件,main.obj仍然是最新的。因此,Make僅編譯aux.obj文件,之后就連接新的aux.obj和已有的main.obj以創建myapp.exe。
      除了一個構建程序的目標外,你還可以添加其他的目標來執行特定任務,例如刪除所有生成的文件以便強制完全重建。你可以將以下兩個目標添加到文件末尾:

    CLEANALL: CLEAN
      del *.exe
      echo Deleted executable
    CLEAN:
      del *.obj
      echo Deleted object files

      因為它們并沒有標識一個要構建的真正目標,像這樣的目標通常稱為“偽目標”(pseudo-targets)。這兩個目標并沒有包含在默認目標的依賴鏈中,因此執行它們的唯一途徑就是在啟動Make時在命令行中明確指定它們。
      指定CLEAN會刪除所有中間目標文件。指定CLEANALL會首先刪除所有中間目標文件(因為它依賴于CLEAN目標),然后刪除可執行文件--以這種方式使用的依賴和方法調用的效果類似。
      除了規則之外,一個make文件還能對變量賦值以及訪問環境變量。因為本概述只是打算提供一個對Make的概覽,在這里我們不會談及這些問題。
      正如例子中所示范的那樣,Make具有一些批處理文件和shell腳本所不具有的重要優點:
    • Make減少了構建時間。Make能夠計算哪些構建目標比它們的源碼更舊,并且只會構建對于更新構建目標來說必要的那些。當你在做一個在編譯時可能需要花費很長時間的大系統時,這能夠節省大量時間。
    • Make是說明性的。你不需要去一步一步地告訴它如何構建。相反的,你只需將需要構建的東西指定為一組相關的目標集。執行順序,或者說控制流程,不會被正規地顯式規定--盡管你在必要時可以用偽目標來順序執行一組命令。
    • Make是可擴展的。命令是由Shell來執行的,所以如果你需要任何shell命令沒有提供的功能,你可以編寫你自己的工具程序--比如,用C語言。
      除了一些古怪行為外(就比如令每個Make用戶至少發瘋一次的對空格和制表符(Tab)的過度區分),Make是一個完美的可用的構建工具。但示例用C來寫的一個原因是:為了更好的演示Make的增量構建的能力。將Make運用在Java中則無法在這點上提供相當的益處,因為絕大多數的Java編譯器自動地計算了依賴關系。假如你有一個Java類,HelloWorld

    // HelloWorld.java

    public class HelloWorld
    {
      public static void main(String[] args)
      {
        Printer printer = new Printer();
        printer.out("Hello, world");
      }
    }

      它使用這個Java類,Printer

    // Printer.java

    public class Printer
    {
      void out(String s)
      {
        System.out.println(s);
      }
    }

      你可以用一條命令來編譯這兩個類:

    javac HelloWorld.java

      Java編譯器,javac,計算了HelloWorld.java并判斷它使用了Printer.java。如果還沒有編譯Printer.java,或者如果Printer.java比Printer.class要新,javac就會編譯Printer.java。換句話說,這一條命令實際上等同于你先前看到的那個C程序的make文件的第一部分--也就是開頭那三條指定了如何構建程序的規則。
      因為增量編譯是Make與批處理文件或者shell腳本相比所能提供的最大優點,Java編譯器能自動判斷依賴關系的能力看來減少了在Java開發中對Make工具的需求。然而,這并沒有完全消除對某些類型的構建工具的需求,因為對象的類型在編譯時并不總是知道。類能包含集合引發了問題,因為一個集合可以存放任意類型的對象;例如,一個Company類可能包含有一個Employee的Vector,而編譯器可能無法在編譯時判斷出來。如果你需要可靠的增量編譯,你仍會需要某些類型的構建工具。
      最后也是最重要的一點,編譯并不是構建工具所做的唯一一件事。正如示例所示,傳統的make文件一般也執行一些像刪除舊文件這樣的簡單的“管家”任務。Java項目經常需要額外的步驟,例如生成Javadoc以及把包歸檔。

    5.3 新的Java標準:Ant


      將傳統的Make工具應用到Java中有一個嚴重的缺點:這些工具運行的是shell命令,而它們在各個平臺上都不一樣。這個事實使得采用Java開發的主要因素之一:一次編寫,到處運行的能力,變得毫無用處。明顯的解決方案就是實現一個能說得上是“Java上的Make”的工具。Ant,一個來自Apache軟件基金會(ASF)的開源構建工具,走上了這條路。除了成為了比Make更好的跨平臺解決方案之外,Ant還更新了make文件的語法,采用了標準說明格式:XML。由于這些緣故,Ant迅速地成為了Java上的標準構建工具,并被緊密地集成到Eclipse中;這種集成包括了一個特殊的編輯器,用來和Ant構建腳本一起工作。

    注意
    目前為止,因為你一直在使用Eclipse來編譯你的Java源代碼,你還不需要一個單獨的Java編譯器。如前所述,Eclipse包含了它自己的特殊的增量編譯器;你所需添加的只是一個Java運行時環境(JRE)。
    要用Ant來構造,尤其是在命令行下,你需要有一個完整的Java開發工具包(JDK)。依據你的平臺而定,你可能會有許多選擇;但至少你應該使用JDK 1.3.x(JDK 1.4.x更好),Sun的或是其它公司的都行。確保在你的PATH環境變量中,該JDK的bin目錄要比其它任何包含有JRE的目錄靠前。同樣應確保,在你的CLASS環境變量中,刪除所有到舊的JDK和JRE的引用。你不需要在classpath中包含任何JDK的標準目錄或者JAR,因為它們會依據Java編譯器的可執行文件(Windows中是javac.exe)和Java虛擬機(java.exe)的位置來自動定位。

    5.3.1 一個對XML的非常簡短的介紹

      XML(可擴展標記語言,Extensible Markup Language)已經成為了表述任何類型語言,因此你可能已經用過它諸多功能中的其中一些了。如果你還沒有用過它,或者已經用過但對它的一些技術還不了解,這份介紹可以讓你能更容易地理解后面對Ant構建文件的討論。
      XML來源于SGML(標準廣義標記語言,Standard Generalized Markup Language),與其很相似的HTML(超文本標記語言,Hypertext Markup Language)也是如此。都使用標簽,也就是用尖括號括起來的標識符,就像這樣:

    <TITLE>

      不過在HTML和XML之間還是有少數一些重要的不同點。因為HTML是設計用來為一個較有限的目標--描述如何將數據顯示為一個網頁--提供服務的,它定義了一組標準標簽。TITLE是一個有效的HTML標簽,但ORDER_NUMBER不是。然而,XML是可擴充的。由你正在使用的應用程序來定義哪些標簽是有效的。在一個使用XML來表示一個在線商店的數據的程序中,ORDER_NUMBER就很有可能是一個有效的標簽。
      一個像<TITLE>這樣的標簽稱為一個開標簽(Opening Tag);它標記了一段數據的開頭。開標簽通常需要有一個閉標簽,也就是和開標簽名字相同,前面有個斜杠的標簽。下面定義了一個網頁的標題:

    <TITLE>A very brief introduction to XML</TITLE>

      HTML對語法要求相當松。開標簽并不總是要求有閉標簽。例如,標簽<P>被用來標記段落開頭,而</P>應該是用來標記末尾。然而實際上,你可以簡單地使用<P>來在一個網頁上表明文字段落間的間隔。這在XML中絕對不是如此--每個開標簽都必須要有一個閉標簽。
      有時候在HTML中你可以回避不正確的嵌套的開標簽和閉標簽;但在XML中就不行了。以下代碼由于嵌套不正確,在XML中是無效的:

    <B><I>This is not valid in XML!</B></I>

      HTML和XML的一個最大不同就是XML是大小寫敏感的。在HTML中,<TITLE>和<title>都是有效且是等價的。在XML中,取決于應用程序,它們都可能有效,但并不等價。

    元素和屬性
      一個開標簽和一個閉標簽定義了一個元素(element)。每個XML文檔必須有一個根元素(或文檔元素)來把文檔中的所有其它元素包圍起來。
      每個元素的開標簽可能包含了形式為名字-值對(name-value pairs)的與元素相關的附加信息,我們稱之為屬性(attributes)。其中的值必須總是用引號包圍。按標簽不同,特定的屬性可能是必需的,或是可選的。比如,Ant定義了一個<target>標簽來標識構建目標。這個target標簽接受好幾個屬性,例如depends和description,但只有name屬性是必需的:

    <target name="Compile" depends="Init">
      <!-- do compilation stuff here-->
    </target>

      (注意了,如同在HTML中一樣,你可以插入以<!--開頭,-->結尾的注釋。)
      有時候元素會沒有任何內容。例如,用來運行Java程序的Ant標簽,<java>,允許你在屬性中給定所需的所有信息。如果你有一個類文件Hello.class,你可以放置在一個target里面來運行它,如下所示:

    <target name="SayHello">
      <java classname="Hello.class"> </java>
    </target>

      作為一種便捷的形式,像這里那個具有/<java>標簽的空元素可以把開標簽寫成以/>結尾,然后忽略閉標簽。以下和前面的例子是等同的:

    <target name="SayHello">
      <java classname="Hello.class"/>
    </target>

    用屬性和嵌套元素來表示數據
      屬性(例如<target>標簽中的name屬性)和嵌套元素(例如被<TITLE>和</TITLE>標簽所包圍的文本)在XML中都能被用來說明數據。由應用程序來選擇。有時應用程序會同時支持兩種格式并讓用戶來選擇。Ant有時會提供屬性以選擇單個選項(例如classname),復雜些的就用嵌套標簽,例如文件集,或是路徑與個別文件的組合。
      舉個例子,標簽<java>,讓你既可以用一個屬性來指定類路徑(classpath),也可以用嵌套元素。你可以用classpath來將路徑設置為預定義好的屬性java.class.path(Ant把它設為你的環境的類路徑),如下所示:

    <target name="SayHello">
      <java classname="Hello.class" classpath="${java.class.path}"/>
    </target>

      或者你可以使用一個嵌套的classpath元素,兩者是等價的:

    <target name="SayHello">
      <java classname="Hello.class">
        <classpath path="${java.class.path}"/>
      </java>
    </target>

      嵌套元素可以依次包含嵌套元素。例如,你可以用一個或多個的嵌套<pathelement>元素來替換掉<classpath>標簽中的path屬性,別的元素也可如此:

    <target name="SayHello">
      <java classname="Hello.class">
        <classpath>
          <pathelement path="${java.class.path}"/>
          <pathelement location="c:/junit/lib/junit.jar"/>
        </classpath>
      </java>
    </target>

      由于允許像這樣的多個選項,有時Ant和其它使用XML的程序會讓人難以理解。嵌套元素提供了比屬性強得多的靈活性,后者局限于單個值。如果你僅僅需要一個值,使用簡單些的語法來設置選項會很方便。
      使用嵌套元素這種方式來擴展選項顯示了XML的主要問題:冗長。你包含進去的每一點點的數據都添加了另一對開標簽和閉標簽。好在(或者說是因為很有必要),大多數基于XML的程序都提供了工具,以使得編寫XML的工作更輕松些。

    5.3.2 一個簡單的Ant示例

      在探究Ant及其構建腳本的細節之前,讓我們通過“Hello,World.”的Ant等價物來看看Eclipse中的Ant的使用技巧。如同Make默認將一個make文件命名為makefile一樣,Ant的默認構建腳本名為build.xml。和Make一樣,你可以通過在調用Ant時顯式指定一個文件來跳過默認設置。然而,通常還是遵守這個約定較好,尤其是因為Eclipse也同樣將構建腳本默認命名為build.xml,并且會自動用Ant腳本編輯器打開叫這個名字的文件。(你可以到[窗口]->[首選項]->[工作臺]->[文件關聯]對話框去更改它的行為,但把它修改為打開所有的.xml文件并不是個好主意--將來你會見到其它類型的XML文件,它們將更能從其它專用編輯器獲益。如果你不愿把你的構建腳本叫做build.xml,在[文件關聯]對話框中逐個輸入所有的構建腳本名,以便用Ant編輯器打開它們。)
      要創建構建文件,可以在一個現存的項目(比如你的老項目Hello)上右擊鼠標,然后在快捷菜單中選擇[新建]->[文件]。輸入build.xml作為文件名并點擊[完成]。如果編輯器沒有自動為你打開build.xml,那么就雙擊build.xml文件。輸入以下內容:

    <?xml version="1.0"?>
    <project name="Hello" default="print message">
      <target name="print message">
        <echo message="Hello from Ant!"/>
      </target>
    </project>

      Ant編輯器并不像Java編輯器那么有用,但是它提供了一些基本的方便,例如你可以在任何時候通過按下[Ctrl]+[空格]鍵來調用自動代碼完成功能。在標簽外時,它會顯示有效的標簽;在標簽里時,它將顯示該標簽的有效屬性。(后面的功能尤其有用,因為各個標簽的屬性名并不相同。)同時,Ant編輯器還提供了語法高亮和一個大綱視圖。
      要運行這個腳本,首先保存它,然后在[包瀏覽器]中右擊build.xml,在快捷菜單中選擇[運行Ant]。這樣將打開一個已經選定了默認目標的對話框(參看圖5.4)。

    圖5.4 運行一個Ant文件。默認目標已經被自動選定。


      點擊對話框底部的[運行]按鈕,Eclipse的控制臺視圖會輸出如下內容:

    Buildfile: c:\eclipse\workspace\hello\build.xml

    print message:
      [echo] Hello from Ant!
    BUILD SUCCESSFUL
    Total time: 2 seconds

    在Eclipse外部運行Ant
      除了可以在Eclipse內部運行Ant腳本外,你也可以在Eclipse外部使用構建腳本。要這么做,你需要從Apache軟件基金會(ASF,Apache Software Foundation)(Ant項目可以在http://ant.apache.org找到)下載并安裝完整的Ant發行版。如果當前版本和Eclipse自帶的那個不同(或者不兼容),你就得找到舊版來下載或是升級Eclipse自帶的Ant。
      要升級Eeclipse自帶的Ant,在Eclipse主菜單中選擇[窗口]->[首選項]->[Ant]->[運行時]。然后從classpath中清除1.5.1版的ant.jar和optional.jar,并添加新版的這兩個JAR文件的路徑。
      在下載適合于你系統的p文件(或tar文件)并解壓之后,將bin目錄路徑添加到你的path,lib目錄路徑添加到classpath。如果你將Ant安裝在Windows上的c:\jakarta-ant-1.5.1目錄,你可以通過在命令行下鍵入如下命令來將這些目錄加入到你的path中:

    SET PATH=c:\jakarta-ant-1.5.1\bin;%PATH%
    SET CLASSPATH= c:\jakarta-ant-1.5.1\lib;%CLASSPATH%

      這些更改只會對當前命令行窗口有效。要想讓設置長期生效,需要用Windows NT/2000/XP中的[控制面板]的[系統],或是Windows 95/98/ME中的autoexec.bat文件來修改它們,之后你打開的任何命令行窗口都將設置適合于Ant的選項。在執行這些步驟后,現在你可以進入c:\eclipse\workspace\Hello目錄,輸入ant以運行Ant:

    C:\eclipse\workspace\Hello>ant
    Buildfile: build.xml

    print message:
      [echo] Hello from Ant!

    BUILD SUCCESSFUL
    Total time: 2 seconds

      如果你在項目的正式構建中使用Ant,但在日常工作中繼續使用Eclipse的自動編譯(這非常方便),你可能要在Eclipse中--或者更愿意在命令行下,偶爾運行ant來構建。這么做可以確保你沒有干擾正式構建過程,并且在構建中包含了你最近創建的任何文件。
      在你制作一個更大的構建文件前,讓我們首先來看看構成一個Ant的make文件的重要標簽及屬性的相關細節。

    5.3.3 項目

      一個構建文件必須具備的文檔元素是<project>標簽,它必須指定一個默認目標,還可以指定一個名字(可選的)。此外,還可以標明項目的基本目錄。表5.1中列出了它的屬性:

    表5.1 <project>標簽屬性
    屬性 描述 是否必需
    default 要運行的默認目標
    name 項目名稱
    basedir 基本目錄
    description 項目描述

      在basedir屬性中,你既可以指定相對路徑也可以指定絕對路徑;不管哪種情況,這都將被解析為其他標簽可以使用的絕對路徑。然而使用一個相對路徑會較好,因為這樣能讓構建更具可移植性。在進行一次構建時,其它開發者的機器以及正式構建用機不需要設置得和你的一樣。以下示例將basedir屬性設置為當前路徑(.)--也就是說,定位到build.xml所在的目錄:

    <project name="Hello" default="compile" basedir="." description = "Hello, world build file">

    <project>標簽可以有以下嵌套元素:
    • <desciption>--如果你需要將描述擴展到超過一行,你可以用嵌套元素來包含項目描述,而非用一個屬性。強烈推薦寫上描述。
    • <target>--如5.3.4節所述。
    • <property>--如5.3.6節所述。

    5.3.4 目標

      一個目標(target)就是一個任務或是一組相關任務的容器標簽,可以(粗略地)比喻為一個方法(method)。它可具有如表5.2所列屬性:

    表5.2 <target>標簽屬性
    屬性 描述 是否必需
    name 目標名稱
    depends 依賴關系列表
    if 僅當設置了指定屬性時執行
    unless 僅當未設置指定屬性時執行
    description 目標描述

      為你的主要目標給出一份描述是個好主意,因為Ant提供了一個-projecthelp選項來列出所有具有描述的目標,并把它們作為主要目標。這個選項令你的構建文檔在一定程度上可以進行自我文檔編制。
      這里是一個例子:

    <target name="compile" depends="init" description="Compile all sources">

    5.3.5 任務

      如果把一個目標比喻為一個方法,一個任務(task)可以比喻為方法中的一條語句。Ant提供了大量的任務--超過100條,如果你把核心任務和可選任務都算上。
      Ant的巨大優點之一是它對跨平臺問題是透明處理的。例如,在UNIX中,一個文件路徑在目錄和文件間用的是向前的斜線(/),而在Windows中,用的是一個反斜線(\)。在Ant中,你都可以使用,然后Ant會提供對你正在使用的系統來說正確的格式。對于類路徑來說也是一樣的。在UNIX中,一個類路徑中不同的路徑是用一個冒號分隔的,而在Windows中使用的則是一個分號;你兩個都可以用,剩下的事情就交給Ant了。
      以下是一些常見的人物,它們都有一組基本屬性--足以理解示例并開始編寫你自己的構建文件。需要一份所有任務及其選項的完整描述的話,請參考http://ant.apache.org/manual/index.html上的Ant文檔。

    <buildname>
      該任務從一個文件中讀取構建號(build number),將屬性build.number設置為該號碼,然后將build.number的值+1后寫回文件。它只有一個屬性,如表5.3中所列:

    表5.3 <buildname>任務屬性
    屬性 描述 是否必需
    file 要讀取的文件(默認:build.number)

      這里是一個例子:

    <buildnumber file="buildnum.txt" />

    <copy>
      該任務復制一個或一組文件。要拷貝單個的文件,用file屬性即可。要拷貝多個文件,需要用一個嵌套的<fileset>元素。
      通常,該任務僅當目標文件不存在或比源文件舊時才執行復制,但你可以通過設置override屬性為true來改變默認行為。<copy>任務的屬性如表5.4中所列:

    表5.4 <copy>任務屬性
    屬性 描述 是否必需
    file 源文件名 是,除非使用了<fileset>
    tofile 目標文件名 是,除非使用了todir
    todir 目標目錄 是,如果拷貝一個以上的文件
    overwrite 覆蓋更新的目標文件 否;默認是false
    includeEmptyDirs 復制空目錄 否;默認是true
    failonerror 如果找不到文件則停止構建 否;默認是true
    verbose 列出已復制的文件 否;默認是false

      一個<fileset>嵌套元素可以用來指定一個以上的文件。(參看5.3.7節。)
      這里是一個示例:

    <copy file="log4k.properties" todir="bin"/>

    <delete>
      該任務刪除一個或一組文件,或者一個目錄。要刪除單個的文件,用file屬性即可。要刪除多個文件,則要用一個嵌套的<fileset>元素。要刪除目錄,用directory屬性即可。<delete>任務的屬性如表5.5中所列:

    表5.5 <delete>任務屬性
    屬性 描述 是否必需
    file 要刪除的文件 是,除非使用了<fileset>或dir
    dir 要刪除的目錄 是,除非使用了<fileset>或file
    verbose 列出已刪除的文件 否;默認是false
    failonerror 如果出錯則停止構建 否;默認是true
    includeEmptyDirs 使用<fileset>時刪除文件夾 否;默認是false

      一個嵌套的<fileset>元素可以用來指定一個以上的文件。(參看5.3.7節。)
      這里是兩個例子:

    <delete file="ant.log"/>
    <delete dir="temp"/>

    <echo>
      該任務將一條信息寫到System.out(默認),一個文件,一份日志,或者一個偵聽器。它的屬性如表5.6中所列:

    表5.6 <echo>任務屬性
    屬性 描述 是否必需
    message 要寫入的文本 是,除非用了文本作為元素內容
    file 輸出文件
    append 附加到文件(而非覆蓋) 否;默認是false

    這里有一些例子:

    <echo message="Hello"/">
    <echo>
      This is a message from Ant.
    </echo>

    <jar>
      該任務將一組文件壓縮到一個JAR文件中。允許的選項如表5.7中所示:

    表5.7 <jar>任務屬性
    屬性 描述 是否必需
    destfile JAR文件名
    basedir 要打包的文件的基本目錄
    includes 要打包的文件的模式列表
    excludes 要排除的文件的模式列表

      模式列表是由逗號或空格分隔的文件匹配模式列表。<jar>接受如同<fileset>元素的嵌套元素。(參看5.3.7節。)
      這里是一些例子:

    <jar destfile="dist/persistence.jar"
      basedir="bin"
      includes=
      "org/eclipseguide/persistence/**, org/eclipseguide/astronomy/**"
      excludes="*Test*.class "/>

    <jar destfile="dist/persistence.jar">
      <include name="**/*.class"/>
      <exclude name="**/*Test*"/>
    </jar>

    <java>
      任務java使用一個JVM(Java虛擬機)來調用一個類。默認情況下,所用的JVM和Ant用的是同一個。如果你是在調用一個穩定的自定義構建工具,這樣可以節省時間;但如果你是在用它運行沒測試過的代碼,你將冒錯誤代碼乃至構建進程崩潰的風險。你可以將fork選項設置為true來調用一個新的JVM。該任務的屬性如表5.8中所列:

    表5.8 <java>任務屬性
    屬性 描述 是否必需
    classname 要運行的類的名稱 是,除非指定了jar
    jar 要運行的可執行JAR文件的名稱 是,除非指定了classname
    classpath 要用的Classpath
    fork 用一個新的JVM運行類或JAR 否;默認是false
    failonerror 當發生錯誤時停止構建 否;默認是false
    output 輸出文件
    append 附加或覆蓋默認文件

      任務<java>可用以下嵌套元素:
    • <classpath>--可用于代替classpath屬性
    • <arg>--可用于指定命令行參數
      這是一些例子:

    <java classname="HelloWorld"/>
    <java classname="Add" classpath="${basedir}/bin">
      <arg value="100"/>
      <arg value="200"/>
    </java>

    <javac>
      該任務編譯一個或一組Java文件。它有一組復雜的選項(參看表5.9),但它比你所想象的要更容易使用,因為很多選項是提供給你以控制編譯器選項的。Ant設定的選項是面向目錄工作的,而非單個的Java文件,這讓構建項目更容易。

    表5.9 <javac>任務屬性
    屬性 描述 是否必需
    srcdir 源碼樹的根 是,除非使用了嵌套的<src>
    destdir 輸出目錄
    includes 要編譯的文件的模式列表 否;默認包含所有的.java文件
    excludes 用忽略的文件的模式列表
    classpath 要用的Classpath
    debug 包含調試信息 否;默認是false
    optimize 使用優化 否;默認是false
    verbose 提供詳細輸出
    failonerror 發生錯誤時停止構建 否;默認是true

      默認情況下,<javac>編譯時包含調試信息。這樣做通常適合于將被用于產品環境的構建。你可能希望有一種方法來打開或關閉這個選項,也許會采取分開調試構建和發布構建的目標的方法。
      <javac>可用以下嵌套元素:
    • <classpath>--可用于代替classpath屬性。
    • <jar>接受如同一個<fileset>元素那樣的嵌套元素。(參看5.3.7節。)
      這是一些例子:

    <javac srcdir="src" destdir="bin"/>

    <javac srcdir="${basedir}" destdir="bin"
      includes="org/eclipseguide/persistence/**"
      excludes="**/*Test*">
      <classpath>
        <pathelement path="${java.class.path}"/>
        <pathelement location="D:/log4j/jakarta-log4j-1.2.7/dist/lib/log4j-1.2.7.jar"/>
      </classpath>
    </javac>

    <javadoc>
      任務<javadoc>從Java源代碼文件生成一份Javadoc。任務jarjava中用于選擇要包含哪些文件的選項應該比較熟悉了。指定給javadoc的首要選項設定了要包含哪些Javadoc注釋;參看表5.10。

    表5.10 <javadoc>任務屬性
    屬性 描述 是否必需
    sourcepath 源碼樹的根 是,除非指定了sourcefiles或者sourcepathref
    sourcepathref 到一個指定源碼樹根的路徑結構的引用 是,除非指定了sourcepath或者sourcefiles
    sourcefiles 源碼文件的逗號分隔列表 是,除非指定了sourcepath或者sourcepathref
    destdir 目標目錄 是,除非已經指定了doclet
    classpath 類路徑(Classpath)
    public 僅顯示公共類及成員
    protected 顯示公共和保護的類及成員 否;默認是true
    package 顯示包,保護和公共的類及成員
    private 顯示所有的類及成員
    version 包含@version信息
    use 包含@use信息
    author 包含@author信息
    failonerror 出錯時停止構建 否;默認是true

      任務<javadoc>可用以下嵌套元素:
    • <fileset>--可用于選擇一組文件。Ant自動把**/*.java添加到每個組。
    • <packageset>--可用于選擇目錄。目錄路徑默認為和包名稱一致。
    • <classpath>--可用于設置classpath。
      這是一些例子:

    <javadoc destdir="doctest"
      sourcefiles ="src/org/eclipseguide/persistence/ObjectManager.java"/>

    <javadoc destdir="doc"
      author="true"
      version="true"
      use="true"
      package="true">
      <fileset dir = "${src}/org/eclipseguide/astronomy/">
        <include name="**/*.java"/>
        <exclude name="**/*Test*"/>
      </fileset>
      <classpath>
        <pathelement path="${java.class.path}"/>
        <pathelement location ="D:/log4j/jakarta-log4j-1.2.7/dist/lib/log4j-1.2.7.jar"/>
      </classpath>
    </javadoc>

    <mkdir>
      該任務創建一個目錄。它具有如表5.11所示的一個屬性。如果指定了一個嵌套目錄,那么若必要的話會連父目錄也一起創建。

    表5.11 <mkdir>任務屬性
    屬性 描述 是否必需
    dir 要創建的目錄

      這是一個示例:

    <mkdir dir="dist/doc">

    <tstamp>
      該任務設置屬性DSTAMP,TSTAMP以及TODAY。一個嵌套元素,<format>,可用于使用Java類SimpleDateFormat定義的格式來改變它們的格式,但默認情況下,這些格式如下所示:

    DSTAMP yyyyMMdd
    TSTAMP hhmm
    TODAY MMM dd yyyy

      關于<tstamp>和<format>元素的更多信息,請參看Ant文檔。

    5.3.6 屬性

      屬性是你可以在一個構建文件中用作符號常數的名稱-值對。屬性的值是通過用${和}括起名字的方式來引用的。例如,如果一個屬性junit_home已經用值D:/junit/junit3.8.1定義,你可以使用該屬性以在編譯時添加junit的JAR文件到classpath中:

    <javac srcdir="src" destdir="bin" classpath="${junit_home}/lib/junit.jar"/>

      屬性可以用幾種方式定義:
    • 由Ant預定義
    • 在Ant命令行,用-D選項(例如,ant -Djunit_home=D:/junit/junit3.8.1)定義
    • 在一個構建文件中用<property>任務定義
      由Ant預定義的屬性包含了所有的標準Java系統屬性,包括了以下內容:
    • java.class.path
    • os.name
    • os.version
    • user.name
    • user.home
      Ant特有的屬性包括:
    • ant.version
    • ant.file
    • ant.project.name

    <property>和名字屬性
      在一個Ant構建文件中設置屬性的最常用方式是利用<property>任務及其屬性name,以及屬性value或location。屬性value用于設置一個直接的值:

    <property name="jar_name" value="myapp.jar"/>
    <property name="company" value="Acme Industrial Software Inc."/>

      屬性location用于設置一個絕對路徑或文件名。如果你設定了一個相對路徑,Ant會認為它是基于basedir屬性的,然后把它轉換為一個絕對路徑并解析它。此外,文件路徑分隔符會被轉換為適合于平臺的字符(/,\,或者是:)。例如:

    <property name="junit_home" location= "D:/junit/junit3.8.1"/>
    <property name="src" location="src"/>

      第一個例子不會被改動(除了文件路徑分隔符外),因為它表示一個絕對路徑。第二個例子會被擴展,因為它是個相對路徑;假設basedir(基本路徑)是c:\eclipse\workspace\persistence,${src}就等同于"c:\eclipse\workspace\persistence\src"。

    <property>和文件屬性
      你可以用file屬性使用標準Java屬性文件格式來從一個文件中讀取屬性。假設有個叫build.properties的文件放在基本目錄中或是在classpath中:

    # build.properties
    junit_home= D:/junit/junit3.8.1
    log4j_home=D:/log4j/jakarta-log4j-1.2.7

      你可以用以下標簽來讀取這些屬性:

    <property file="build.properties"/>

    <property>和環境屬性
      通過用environment屬性給環境設置前綴的方法,像讀取屬性那樣去讀取環境變量也是可行的。以下給環境設置了前綴myenv:

    <property environment="myenv">

      之后,你只要使用前綴myenv就可以像訪問屬性一樣訪問環境變量。例如,如果環境中定義了JUNIT_HOME,你可以用${myenv.JUNIT_HOME}獲取它的值。
      你應該小心使用該技術,因為并不是在所有操作系統上都支持它。同時,即使底層操作系統是大小寫不敏感的,在Ant中,屬性名也是大小敏感的。這很容易在各版本的Windows中因疏忽而導致問題,因為系統保留了環境變量的大小寫而執行比較時卻不區分大小寫。
      例如,如果有一個變量CLASSPATH,其值是c:\mylibs,以下既不會創建一個叫classpath的新變量,也不會改變原CLASSPATH變量的大小寫:

    set classpath=.%classpath%;c:\anotherlib

      相反的,這樣做會更新原CLASSPATH變量的值為c:\mylibs;c\anotherlib。要確保大小寫像你期望的那樣,你可以先清除該變量然后重新定義它。以下幾行可以放在命令行或是一個批處理文件中,它們可以強制把classpath變成小寫:

    set tmpvar=%classpath%
    set classpath=
    set classpath=%tmpvar%;c:\anotherlib

      如果你正要為Ant設置環境變量,像這樣使用Windows批處理文件來預防問題是值得考慮的──尤其是當該批處理文件會用在其它系統上時。

    5.3.7 文件集和路徑結構

      由于Ant的特性,許多Ant任務,如<javac>和<jar>要求你設置路徑和文件集。Ant提供了元素以使你能給它們指定足夠多的細節,你既可以明確地選擇文件和目錄,也可以使用模式(patterns)來包含或排除一組文件或目錄。因為這些元素除了引用對象以外,什么事情也不做,所以被稱為類型(types)。在這里你僅使用兩種類型:<fileset>和<classpath>。

    <fileset>
      正如其名字所表述的那樣,<fileset>元素讓你選擇文件集。一個<fileset>必需的屬性只有基本目錄。如果你別的什么也沒有設定,該目錄下的所有文件及其子目錄都將被選定──特定的臨時文件以及特定工具(如CVS)生成的文件除外。(這樣的文件通常都有特殊的名字,用一個波紋號(~)或#開頭和結尾,或者具有像CVS和SCCS這樣的特定的名字和擴展名;它們和一個典型項目的一般文件相同的情況極為罕見。要獲得一份Ant要默認排除的完整的模式列表,請參考Ant文檔。)
      你也可以選擇或排除那些和你提供的模式相匹配文件。模式可以包含以下通配符:

    ? 匹配一個任意字符
    * 匹配零或多個字符
    ** 匹配零或多個目錄

      考慮這兩個常見的例子:你可以在include屬性中用模式**/*.java來包含所有的Java源文件,也可以在exclude屬性中用模式**/*Test*來排除測試用例。共同使用時,它們指定了除測試用例外的所有Java文件。
      <fileset>元素的屬性如表5.12所列。

    表5.12 <fileset>元素屬性
    屬性 描述 是否必需
    dir 目錄樹的根
    defaultexcludes 排除通常的臨時文件和工具文件 否;默認是true
    includes 要包含的文件的模式列表
    excludes 要排除的文件的模式列表
    followsymlinks 使用符號鏈接所指定的文件

      嵌套元素<include>和<exclude>可以分別用來代替includes屬性和excludes屬性。
      這是一些例子:

    <fileset dir = "src/org/eclipseguide/astronomy"
      includes = "**/*.java"
      excludes = "**/*Test*"/>

    <fileset dir = "src/org/eclipseguide/astronomy/">
      <include name="**/*.java"/>
      <exclude name="**/*Test*"/>
    </fileset>

    <classpath>
      <classpath>元素讓你可以指定哪些目錄和JAR文件是當類要運行時(或者,對于Java編譯器來說,要編譯時),程序應該搜索的。默認情況下,Ant繼承了環境的類路徑,但你都需要為特定程序(如JUnit)添加額外的目錄或JAR文件。使用類路徑的任務提供了一個classpath屬性,但有時使用一個<classpath>嵌套元素會更方便些──尤其是當類路徑很長的時候。路徑可以包含用一個冒號或一個分號隔開的多個文件或目錄;Ant會將分隔符轉換為適合于當前操作系統的字符。
      表5.13中列出了<classpath>元素的屬性。

    表5.13 <classpath>元素屬性
    屬性 描述 是否必需
    path 冒號或分號分隔的路徑
    location 單個文件或目錄

      一個或多個<pathelement>元素可以通過嵌套來構造一個更長的類路徑。<pathelement>接受和<classpath>:path以及location相同的屬性。
      此外,一個<fileset>可以用于指定文件。
      這是一些例子:

    <classpath path = "bin"/>
    <classpath>
      <pathelement path="${java.class.path}"/>
      <pathelement location="${junit_path}"/>
      <pathelement location="${log4j_path}"/>
    </classpath>

    5.3.8 Ant的額外能力

      這里講述的基本知識已經足以讓你開始用Ant來工作而不會不知所措了。當你用Ant做更多的事情之后,你很可能會遇到一種情況──也許會發覺你一直在反復使用相同的<filelist>──并想知道有沒有一種比剪切、粘貼更優雅的解決方案。通常,你會發現在Ant中,幾乎沒有什么是不可能的。
      減少冗余代碼的一種方法是使用引用。例如,Ant中的每個元素都能指定一個ID;并且(取決于涉及的元素的類型)你可以在構建文件中的其它地方使用ID來引用那個元素。例如,你可以使用id屬性給一個<classpath>指定一個標識符:

    <classpath id="common_path">
      <pathelement path="${java.class.path}"/>
      <pathelement location="${junit_path}"/>
      <pathelement location="${log4j_path}"/>
    </classpath>

      這樣一來,這個類路徑就可以在別的地方用refid屬性來引用:

    <javac srcdir="src" destdir="bin">
      <classpath refid=common_path/>
    </javac>

      Ant提供了任務和類型以供你過濾文件,如同以文本來復制、替換符號一樣,這樣你就能夠在你的構建中包含版本信息──例如,使用帶有一個<filterset>的<copy>任務。它讓你可以通過使用選擇器類型(如<contains>,<date>和<size>)來按照復雜的條件選擇文件。在很罕見的情況下,Ant沒有可以符合你需求的任務,你會發現編寫自己的Ant任務是很簡單的事情。

    5.4 一個Ant構建的示例


      以下是你的構建過程需要做的主要步驟:
    • 編譯程序,輸出到bin目錄
    • 在bin目錄中運行單元測試
    • 生成一份Javadoc文檔,輸出到dist/doc目錄
    • 將程序的類文件打包到dist目錄下的一個JAR文件中
      因為你可能需要能逐步完成這些工作,它們在Ant構建文件中是獨立的目標。然而通常,你會需要一次性完成所有步驟,所以你也需要有一個目標依賴于這些獨立的目標。
      通常這些獨立的目標都有共通的安裝需求。你可以創建一個初始化目標來執行安裝,該目標可以以依賴的方式包含這些獨立目標。因為這是一個相當簡單的示例,在這里,你所需要做的一切只是通過調用tstamp任務,初始化屬性DSTAMP,TSTAMP和TODAY為當前的日期和時間,然后打印日期和時間。

    5.4.1 創建構建文件build.xml

      要創建構建文件,請遵循以下步驟:
        
    1. 在[包資源管理器]中的項目Persistence上右擊鼠標,選擇[新建]->[文件]。  
    2. 在[新建文件]對話框中鍵入build.xml并點擊[完成]。
      在定義任何目標前,讓我們來創建一些屬性,之后就可以像符號常量那樣使用,而無需在構建文件中雜亂地堆放實際值。這個方法會讓構建文件更容易維護。這是build.xml的開頭:

    <?xml version="1.0"?>
    <project name="Persistence" default="BuildAll" basedir=".">

      <description>
      Build file for persistence component,
      org.eclipseguide.persistence
      </description>

        <!-- Properties -->
        <property name="bin" location="bin"/>
        <property name="src" location="src"/>
        <property name="dist" location="dist"/>
        <property name="doc" location="${dist}/doc"/>
        <property name="jardir" location="${dist}/lib"/>
        <property name="jarfile" location="${jardir}/persistence.jar"/>
        <property name="logpropfile" value="log4j.properties"/>
        <property name="relpersistencepath" value="org/eclipseguide/persistence"/>
        <property name="alltests" value="org.eclipseguide.persistence.AllTests"/>
        <property name="junit_path" location="D:/junit/junit3.8.1/junit.jar"/>
        <property name="log4j_path" location="D:/log4j/jakarta-log4j-1.2.7/dist/lib/log4j-1.2.7.jar"/>

      如你所料,目錄一般是用location屬性來指定的,Ant會基于項目的基本目錄將它擴展為絕對路徑。有一個例外:relpersistencepath,這是一個你會在多個不同地方會用到的相對路徑,它是基于不同目錄的;要避免Ant將它轉變為絕對路徑,你需要用value屬性來設置它。
      也請注意你顯式指定了幾個類路徑。這不是最好的方法──因為這意味著該構建只能在一臺以特殊方式配置的機器上工作──但這是最簡單的。你可能想知道你能否用你在Eclipse中設置的classpath變量來代替。答案是肯定的,使用一個定制的第三方Ant任務可以實現;但這將意味著你只能在Eclipse中使用該構建過程。(如果你不介意這個限制,你可以通過到eclipse.tools新聞組搜索一個定制的Ant任務的代碼。)
      除了該方法之外,特別是當你在Eclipse外部用命令行構建時,你可以用其它幾種方法來設置這些類路徑。第一個方法,也可能是最簡單的,就是通過以下命令將它們添加到環境的CLASSPATH變量中:

      <Set CLASSPATH=%CLASSPATH%;D:/junit/junit3.8.1/junit.jar;D:/log4j/jakarta-log4j-1.2.7/dist/lib/log4j-1.2.7.jar>

      第二個方法是在命令行顯式用-D選項把它們傳遞給Ant:

      <ant -Djunit_path=D:/junit/junit3.8.1/junit.jar -Dlog4j_path=D:/log4j/jakarta-log4j-1.2.7/dist/lib/log4j-1.2.7.jar>

      更好一點的方法是將這些路徑存在它們自己的環境變量中。你可以在構建文件中用以下屬性標簽讀取它們:

      <property environment="env"/>
      <property name="junit_path" value="${env.JUNIT_HOME}/lib"/>
      <property name="log4j_path" value="${env.LOG4J_HOME}/lib"/>

      或者是在命令行中像這樣來傳遞它們:

      <ant -Djunit_path=%JUNIT_HOME%\lib -Dlog4j_path=%LOG4J_HOME%\lib>

      最后,其它的選項是使用一個屬性文件。你可能有一個名為build.properties的文件,其中有如下幾行:

      <cjunit_path=D:/junit/junit3.8.1/junit.jar>
      <log4j_path=D:/log4j/jakarta-log4j-1.2.7/dist/lib/log4j-1.2.7.jar>

      要在文件中使用這些值,需要在build.xml包含如下標簽:

      <property file="build.properties"/>

      在設置了屬性之后,就需要包含主要目標了。先放置默認目標不是必要的(就像用Make時一樣),但由于這是個特殊的目標──它僅僅是以一組依賴性的方式來把其它目標鏈接起來,你就需要這樣寫:

      <!-- Main targets -->
      <target name="BuildAll"
        depends="-Init, -Prep, Compile, Test, Javadoc, Jar"
        description="Complete rebuild. Calls Init, Compile, Test, Javadoc, Package"/>
        <echo message="Build complete."/>
      </target>

      按照被BuildAll調用的順序包含剩下的主目標:下一個是編譯目標。注意,通過將源目錄標識為org,你可以編譯org.eclipseguide.persistence包和 org.eclipseguide.astronomy包中的任何東西,包括單元測試。該技術是拷貝任何所需資源──在這個例子中是log4j.properties文件──的極好方法:

      <target name="Compile"
        depends="-Init"
        description="Compile all Java classes">
        <!-- Compile org.* (${src}) -->
        <javac srcdir="${src}" destdir="${bin}">
          <classpath>
            <pathelement path="${java.class.path}"/>
            <pathelement location="${junit_path}"/>
            <pathelement location="${log4j_path}"/>
          </classpath>
        </javac>
        <!-- Copy log4j.properties files -->
        <copy file="${logpropfile}" todir="${bin}"/>
        <echo message="Compiled."/>
      </target>

      下一個目標是運行單元測試。要在Eclipse外部運行JUnit測試,你需要用到一個JUnit的TestRunner類。由于你需要能夠在命令行下運行該構建文件并且記錄到一個文件中,你需要使用基于文本的TestRunner,即 junit.textui.TestRunner,而不是漂亮的圖形界面版本。要作為一個Java應用程序運行,需要用Ant的java任務。要確保它不會連同你的構建過程一同崩潰,你需要指定它使用獨立的JVM,通過設置fork屬性為true就可以了。同時,你還必需以嵌套值的方式提供其它的一些值,包括TestRunner要運行的測試類名以及需要用到的類路徑:

      <target name="Test" depends="-Init " description="Run JUnit
        <!-- Run test suite using separate JVM -->
        <java fork="yes" classname="junit.textui.TestRunner" taskname="junit" failonerror="true">
          <arg value="${alltests}"/>
          <classpath>
            <pathelement path="${java.class.path}"/>
            <pathelement location="${bin}"/>
            <pathelement location="${log4j_path}"/>
            <pathelement location="${junit_path}"/>
          </classpath>
        </java>
        <echo message="Tested!"/>
      </target>

      Javadoc目標包括了你在這里用到的絕大多數復雜任務。首先,你需要在javadoc標簽中,以屬性來指定包名(packagename)和你要包含的Javadoc備注。然后,由于要排除掉單元測試,你要使用一個嵌套的<fileset>,里面依次包含嵌套的標簽<include>和<exclude>:

      <target name="Javadoc" depends="-Init" description="Create Javadoc">
        <!-- Javadoc, only for persistence classes -->
        <javadoc destdir="${doc}" author="true" version="true" use="true" package="true">
          <fileset dir="${src}/${relpersistencepath}">
            <include name="**/*.java"/>
            <exclude name="**/*Test*"/>
          </fileset>
          <classpath>
            <pathelement path="${java.class.path}"/>
            <pathelement location="${junit_path}"/>
            <pathelement location="${log4j_path}"/>
          </classpath>
        </javadoc>
        <echo message="Javadoc complete."/>
      </target>

      目標Jar相當明了。正如你在javadoc任務中所做的一樣,你在這里使用一個<fileset>來指定要排除的測試文件。由于用戶需要log4j.properties文件,你還得拷貝它:

      <target name="Jar" depends="-Init ">
        <!-- Jar for persistence classes -->
        <jar destfile="${jarfile}"
          basedir="${bin}"
          includes="${relpersistencepath}/*.class"
          excludes="**/*Test*"
        />
        <echo message="${bin}${relpersistencepath}/**"/>
        <!-- Copy log4j.properties to provide a sample -->
        <copy file="log4j.properties" todir="${dist}"/>
        <echo message="Packaging complete"/>
      </target>

      最后,該是內部目標了:-Init和-Prep。(它們的名字用一個連字符開頭,這可以避免它們被直接使用。雖然這不是必需的,但實踐證明這是個好辦法,因為這能令你的意圖更明確清晰。)-Init打印時間。所有的主目標都依賴于它:

      <!-- Internal targets -->
      <target name="-Init"> <!-- private target, omit description-->
        <!-- Set timestamp and print time -->
        <tstamp/>
        <echo message="Build time: ${TODAY} ${TSTAMP}"/>
      </target>

      -Prep僅當你指定了BuildAll目標時調用。它從以前的構建中刪除所有東西──特別是bin和dist目錄:

      <target name="-Prep">
        <!-- Delete output directories -->
        <delete dir="${bin}"/>
        <delete dir="${dist}"/>
        <delete dir="${jardir}"/>
        <!-- Create output directories -->
        <mkdir dir="${bin}"/>
        <mkdir dir="${dist}"/>
        <mkdir dir="${jardir}"/>
      </target>
    </project>

    5.4.2 執行一個構建

      運行Ant構建文件和以前一樣──在build.xml上右擊鼠標并選擇[運行Ant]。但現在你有更多的選項,這是因為你有更多目標。注意,默認目標BuildAll已經被自動選中了,不過你可以用復選框選擇其它目標。例如,你可以選擇Compile和Javadoc(見圖5.5)。

    圖5.5 你可以在Ant構建對話框中顯式選擇目標


      你也可以設置目標執行的順序,只需點擊[排序]按鈕來打開如圖5.6所示的對話框。點擊一個目標并點擊[上移]或[下移]來改變它的構建順序。選擇好之后,點擊[確定]并點擊[運行]來開始構建。

    圖5.6 目標排序對話框。在這里,你可以改變目標執行的順序。


      和以前一樣,輸出會顯示在控制臺視圖。不同類型的消息會用不同顏色顯示:Ant狀態消息是綠色,<echo>消息是橙色,而錯誤──有的話──是紅色。
      如果你很小心,而且沒有作出僅對當前Eclipse環境有效的假定(例如所依賴的類路徑設置只有你的Eclipse配置才有),你應該能夠在命令行構建。要這么做的話,只需在命令行下鍵入ant,就像你在示例Hello中所做的一樣。
      你已經注意包含了項目描述和主目標,這樣一來,別人能比較容易使用你的構建文件,因為他們可以在命令行下鍵入ant -projecthelp。這樣會輸出以下內容:

    C:\eclipse\workspace\persistence>ant -projecthelp
    Buildfile: build.xml

    Build file for persistence component,
    org.eclipseguide.persistence

    Main targets:

    BuildAll Complete rebuild. Calls Init, Compile, Test, Javadoc, Pa
    Compile Compile all Java classes
    Javadoc Create Javadoc
    Test Run JUnit tests

    Default target: BuildAll

      不過由于Ant和Eclipse集成在一起,在Eclipse里運行構建文件有不少好處──尤其是當你剛開始開發時。

    5.4.3 調試構建

      雖然Eclipse和Ant沒有為Ant提供一個調試器,但它們可以幫助識別和糾正可能發生的多種類型的錯誤。抵御錯誤的最前線,自然是由編輯器提供的語法高亮了。在Ant編輯器中,備注一般是紅色的,文本內容是黑色的,標簽和屬性是藍色的,屬性值是綠色的。如果什么東西的顏色和應有的不同(例如有幾行代碼顯示為紅色),這會很明顯,而你就能知道有什么錯了。
      然而,Ant編輯器的語法高亮并不能標識所有錯誤;除了備注缺少括號和閉標簽之外,你你還需要保存你的構建文件以便能夠正確解析它。在你保存文件之后,錯誤會被標識在Ant編輯器旁邊的大綱視圖中,也會標識在編輯器的右邊空白處。點擊邊緣的紅框,就會直接跳到錯誤處(如圖5.7)。

    圖5.7 Ant編輯器在右邊空白處標識了一個語法錯誤。


      有些錯誤只有在你運行構建文件時才能識別出來。比如在你使用了一個無效屬性時。假設你記得<javac>使用了<fileset>所使用的屬性和嵌套元素的一個超集,并且寫了如下內容:

    <javac dir="${src}" destdir="${bin}">

      確實,<javac>幾乎具有<fileset>所使用的所有屬性;但唯一的不同之處是,<javac>使用srcdir而<fileset>用的是<dir>。如果運行帶有該錯誤的構建文件,當Ant嘗試執行該任務時,會導致在調試器窗口顯示如下問題:

    [javac] BUILD FAILED: file:C:/eclipse/workspace/persistence/
    build.xml:38: The <javac> task doesn't support the "dir" attribute.

      點擊該錯誤,會導致Ant嘗試跳轉到非法代碼。當你在文本[javac]上點擊時──這時就成功地找到問題了。(點擊任何方括號中的任務名,不只是那些有錯誤的,都會跳轉到構建文件中的對應代碼。)
      當然,一旦你解決了最初的一些在構建過程和構建文件中的問題,構建問題經常都涉及到源代碼了。這正是Ant和Eclipse集成的閃光點,因為在一個編譯錯誤上點擊,就會跳轉到源代碼中產生問題的地方。
      例如,假設在FileObjectManager類的createObjectManager()方法的參數列表中,你把變量type的名字錯拼為typo。當編譯時,你會得到一個“未解析的符號”的錯誤(見圖5.8)。

    圖5.8 Ant調試輸出。在一個錯誤上點擊就會跳轉到源代碼中的對應行。


      在調試視圖中的錯誤信息上點擊,就會在編輯器中打開相應的源文件,同時光標會跳到錯誤所在行。

    5.5 總結


      團隊開發對開發過程提出了新的需求。其中之一就是對協同團隊中不同開發者的工作并給出一個正式構建的需求。因為這應該是一個可重復的過程,簡單地給出一個特別的構建是不夠的。執行一個正式構建的傳統方法是使用一個叫做Make的命令行工具;這個工具具有廣泛的兼容性,在多種不同平臺上都有其對應版本,但它們都有一個共同的格式,而且語法有些晦澀。
      你當然可以使用Make來構建Java產品,但Java和傳統編程語言(如C/C++)在需求上有些許不同;首要的一點就是,Java很努力地要成為一門跨平臺語言,這就使得Make不夠理想。開發Ant很大程度上就是為了滿足這個需求。同時,除了保留Make的精神外,它還引入了一些新特性,包括XML語法和使用Java類的可擴展性。
      因為它和Eclipse的是集成的,一個Ant構建過程既可以在Eclipse內部運行,也可以在外部用命令行運行。這樣,在命令行下運行的正式過程,可以完全獨立于Eclipse。這個能力提供了額外的好處──開發者們將可以使用任何自己喜歡的開發環境。
      盡管團隊開發需要一個構建工具,但Ant的使用并不只局限于團隊。即便是在一個像Eclipse這樣能夠自動編譯代碼并且為創建Javadoc文檔、JAR文件以及p文件提供了很易用的向導的開發環境中,個人也可以從Ant的使用中獲益。一個構建過程由多個步驟組成,雖然簡單,但可能冗長乏味并很容易出錯,所以花一點時間用Ant把構建過程自動化將是一項值得考慮的投資。
    轉自:http://addone.blogchina.com/1389346.html

    posted on 2005-05-26 18:24 weidagang2046 閱讀(3225) 評論(0)  編輯  收藏 所屬分類: Java

    主站蜘蛛池模板: 在线综合亚洲欧洲综合网站| 亚洲精品不卡视频| 美女视频黄a视频全免费网站一区| **一级一级毛片免费观看| 亚洲综合自拍成人| 久久精品免费观看国产| 亚洲国产成人久久综合一| 久久精品国产这里是免费| 久久久久亚洲AV无码麻豆| 久久99精品免费视频| 亚洲经典在线中文字幕| 国产精品1024永久免费视频| 亚洲免费观看在线视频| 在线观看日本免费a∨视频| 亚洲中文字幕无码mv| 国产精品酒店视频免费看| 又硬又粗又长又爽免费看 | 99热在线免费观看| 久久亚洲熟女cc98cm| 91精品免费在线观看| 亚洲日韩国产二区无码| 国产一级淫片视频免费看| 国产国产人免费人成成免视频| 亚洲中文字幕无码不卡电影| 久久青草免费91观看| 精品久久亚洲中文无码| 免费a级毛片永久免费| 韩国免费a级作爱片无码| 亚洲最大福利视频网站| 色视频色露露永久免费观看| 国产无遮挡色视频免费观看性色| 亚洲Aⅴ无码专区在线观看q| 久久精品女人天堂AV免费观看| 真人无码作爱免费视频| 亚洲国产精品VA在线观看麻豆| 日韩免费一区二区三区在线播放| 精品国产亚洲AV麻豆| 亚洲AV日韩AV永久无码绿巨人| 久久久久久久免费视频| 一区二区三区免费电影| 亚洲精品国产成人|