專題:Ant-1.7.x

專題:Ant-1.7.x

——本文是基於Ant-1.7.1版。

 

Ant是什麼?

Apache Ant是一個基於Java的構建工具。據最初的創始人James Duncan Davidson介紹,這個工具的名稱是another neat tool(另一個整潔的工具)的首字母縮寫。隨着應用程序的生成過程變得更加複雜,確保在每次生成期間都使用精確相同的生成步驟,同時實現儘可能多的自動化,以便及時產生一致的生成版本,這就變得更加重要了。C或C++中的傳統項目經常使用make工具來做這件事情,其中生成任務是通過調用shell命令來執行的,而依賴關係定義在每個生成文件之間,以便它們總是以必需的順序執行。Ant與make類似,它也定義生成文件之間的依賴關係;然而,與使用特定於平臺的shell命令來實現生成過程所不同的是,它使用跨平臺的Java類。使用Ant,您能夠編寫單個生成文件,這個生成文件在任何Java平臺上都一致地操作(因爲Ant本身也是使用Java語言來實現的);這就是Ant最大的優勢。Ant的其他關鍵優勢包括其突出的簡單性和無縫地使用自定義功能來擴展它的能力。

 

Ant生成文件剖析

     Ant沒有定義它自己的自定義語法;相反,它的生成文件是用XML編寫的,每個生成文件(通常爲build.xml,也可爲任意xml文件)由單個project元素組成,該元素包含一個或多個target元素。一個目標(target)是生成過程中已定義的一個步驟,它執行任意數量的操作,比如編譯一組源文件。這些操作本身是由其他專用任務標籤執行的。然後這些任務將根據需要被分組到各個target元素中。一次生成過程所必需的所有操作可以放入單個target元素中,但是那樣會降低靈活性。將那些操作劃分爲邏輯生成步驟,每個步驟包含在它自己的target元素中,這樣通常更爲可取。這樣可以執行整體生成過程的單獨部分,卻不一定要執行其他部分。例如,通過僅調用某些目標,您可以編譯項目的源代碼,卻不必創建可安裝的項目映像。頂級project元素需要包含一個default屬性,如果在Ant被調用時沒有指定目標,這個屬性將指定要執行的目標。然後需要使用target元素來定義該目標本身。下面是一個最基本的生成文件:

<?xml version="1.0"?>

<projectdefault="init">

    <targetname="init"/>

</project>

 

添加描述:

      通常用以下兩種方式來使待構建的項目更具描述性,同時無需改變其功能。

1.   XML註釋可以使用在整個生成文件中以提高清晰性。

2.   Ant自己定義的description元素和description屬性,它們用於提供更結構化的註釋。

例如:

<?xml version="1.0"?>

<projectdefault="init"name="ProjectArgon">

    <description>

A simple project introducing the use of descriptive tags in Ant buildfiles.

</description>

    <!-- XML comments can also be used -->

    <targetname="init"description="Initialize Argondatabase">

       <!-- perform initialization stepshere -->

    </target>

</project>

 

屬性

     Ant中的屬性類似編程語言中的變量,它們都具有名稱和值。然而與通常的變量不同,一經設置,Ant中的屬性就不可更改;如果嘗試給某個現有屬性賦予一個新的值,這不會被看作是一個錯誤,但是該屬性仍然會保留其現有值。在Ant中設置屬性,如下:

<propertyname="metal"value="beryllium"/> 

爲了在生成文件的其他部分引用這個屬性,使用語法:${metal} 

例如,爲了使用這樣一個值,它是另一個屬性的值的組成部分,將標籤寫爲下面這樣:

<propertyname="metal-database" value="${metal}.db"/>

 

Ant中的預定義的屬性

1.   Java環境設置用於運行Ant的所有系統屬性,均可作爲Ant屬性使用,比如${user.home}。

2.   Ant定義的自己的一小組屬性:

1)    ${ant.version},這個屬性包含Ant的版本;

2)    ${basedir},這個屬性是項目目錄的絕對路徑(由包含生成文件的目錄所定義,或者由project元素的可選basedir屬性所定義)。

3)    Ant的location屬性。屬性經常用於引用文件系統上的文件或目錄,但是對於使用不同路徑分隔符(例如,/與\)的平臺來說,這樣可能在跨越不同平臺時導致問題。Ant的location屬性專門設計用於以平臺無關的方式包含文件系統路徑。像下面這樣使用location來代替value:

<property name="database-file"location="archive/databases/${metal}.db"/>

     用於location屬性的路徑分隔字符將被轉換爲當前平臺的正確格式;而且由於文件名是相對的,它被認爲是相對於項目的基目錄。我們同樣可以容易地寫爲下面這樣:

<property name="database-file"location="archive\databases\${metal}.db"/>

這個標籤的兩個版本都會在不同的平臺具有相同的行爲。儘量使用相對路徑名稱而不是絕對路徑名稱,這樣還會更加靈活。

 

定義依賴關係

     使用target元素的depends屬性來實現的依賴關係。

     生成一個項目一般需要許多步驟,例如首先要編譯源代碼,然後將它打包爲Java歸檔文件(JavaArchive File,JAR)。這其中的步驟都具有清楚定義的順序,例如,在編譯器從源代碼生成類文件之前,您不能打包類文件。與順序指定target所不同的是,Ant採用一種更靈活的方法來定義依賴關係,就像make和類似的生成工具所做的那樣。每個目標的定義依據的是在它在能夠執行之前必須完成的其他所有目標。例如:

<targetname="init"/>

<targetname="preprocess"depends="init"/>

<targetname="compile"depends="init,preprocess"/>

<targetname="package"depends="compile"/>

     這種方法允許您執行項目任何階段的生成過程;Ant會首先執行已定義的先決階段。在上面的例子中,如果讓Ant完成compile步驟,它將判斷出需要首先執行init和preprocess這兩個目標。init目標不依賴其他任何目標,因此它將首先被執行。然後Ant檢查preprocesstarget,發現它依賴init目標;由於已經執行了init,Ant不會再次執行它,因而開始執行preprocess目標。最後可以執行compile任務本身。注意目標出現在生成文件中的順序並不重要:執行順序是由depends屬性唯一確定的。

 

運行Ant

1、從命令行運行Ant

     從命令提示符調用Ant可以簡單得只需鍵入單獨的ant命令。如果您這樣做,Ant將使用默認的生成文件(build.xml);該生成文件中指定的默認目標就是Ant嘗試要生成的目標。還可以指定許多命令行選項,後面跟着任意數量的生成目標,Ant將按順序生成這其中的每個目標,並在此過程中解決所有依賴關係。

下面是從命令行執行的Ant生成任務的一些典型輸出:

Buildfile:build.xml

init:

[mkdir]Createddir:E:\tutorials\ant\example\build

[mkdir]Createddir:E:\tutorials\ant\example\dist

compile:

[javac]Compiling8 source files to E:\tutorials\ant\example\build

dist:

[jar]Buildingjar:E:\tutorials\ant\example\dist\example.jar

BUILDSUCCESSFUL

Totaltime:2 seconds

 

命令行選項

     就像make工具默認情況下尋找一個名爲makefile的生成文件一樣,Ant尋找一個名爲build.xml的文件。因此,如果您的生成文件使用這個名稱,就不需要在命令行指定它。當然,有時使用具有其他名稱的生成文件更方便,在那樣的情況下,您需要對Ant使用-buildfile<file>參數(-f<file>是其簡寫形式)。

     另一個有用的選項是-D,它用於設置隨後可以在生成文件中使用的屬性。這對於配置您想要以某種方式開始的生成過程是非常有用的。例如,爲了將name屬性設置爲某個特定的值,您會使用一個類似下面這樣的選項:

-Dmetal=beryllium

這個功能可用於覆蓋生成文件中的初始屬性設置。正如前面指出過的,屬性的值一經設置就不能改變。-D標誌在讀取生成文件中的任何信息之前設置某個屬性;由於生成文件中的指派落在這個初始指派之後,因此它不會改變其值。

 

2、從Eclipse內運行Ant生成任務

     名爲build.xml的文件在Eclipse的導航程序視圖中使用一個Ant圖標來標識和裝飾。右鍵單擊這些文件會提供一個RunAnt...菜單選項。

 

生成一個簡單的JAVA項目

編譯源代碼 (<javac …/>)

     通過屬性javac編譯Java代碼的任務的編寫方式:

<javacsrcdir="src"destdir="build"debug="on"/>

     這個標籤尋找src目錄中以 .java爲擴展名的所有文件,並對它們調用javac編譯器,在build目錄中生成類文件。如果沒有指定destdir屬性則在srcdir指定的目錄中生成類文件。其他有用的屬性包括:

classpath:等價於javac的-classpath選項。

debug="true":指示編譯器應該帶調試信息編譯源文件。(debug的值可取true/false/on/off/yes/no)

 

javac任務的一個重要特點:

     javac僅編譯那些它認爲需要編譯的源文件。如果某個類文件已經存在,並且對應的源文件自從該類文件生成以來還沒有改變過,那麼該源文件就不會被重新編譯。javac任務的輸出顯示了實際被編譯的源文件的數目。

編寫一個clean目標來從目標目錄移除生成的任何類文件是個很好的習慣。如果想要確保所有源文件都已編譯,就可以使用這個任務。

 

編譯器選項

     Ant的javac任務的默認行爲是調用運行Ant本身的任何JVM的標準編譯器。然而,可能想要單獨地調用編譯器。例如當希望指定編譯器的某些內存選項,或者需要使用一種不同級別的編譯器的時候。爲實現這個目的,只需將javac的fork屬性設置爲true,比如:

<javacsrcdir="src"fork="true"/>

如果想要指定一個不同的javac可執行文件,並向它傳遞一個最大內存設置,您可以像下面這樣做:

<javacsrcdir="src"fork="true"executable="d:\javase6\bin\javac"memorymaximumsize="128m"/>

     甚至可以將Ant配置爲使用某種不同的編譯器。受支持的編譯器包括開放源代碼的Jikes編譯器和來自GNU編譯器集(GNUCompiler Collection,GCC)的GCI編譯器。可以通過兩種方式指定這些編譯器:1)可以設置build.compiler屬性,這將應用於使用javac任務的所有場合;2)或根據需要設置每個javac任務中的compiler屬性。

javac任務還支持其他許多選項。請參考Ant手冊以瞭解更多細節(請參閱參考資料)。

 

創建JAR文件 (<jar…/>)

     在編譯Java源文件之後,結果類文件通常被打包到一個JAR文件中,這個文件類似zip歸檔文件。每個JAR文件都包含一個清單文件,它可以指定該JAR文件的屬性。下面是Ant中jar任務的一個簡單使用例子:

<jardestfile="package.jar"basedir="classes"/>

這將創建一個名爲package.jar的JAR文件,並把classes目錄中的所有文件添加到其中(JAR文件能夠包含任意類型的文件,而不只是類文件)。此處沒有指定清單文件,因此Ant將提供一個基本的清單文件。manifest屬性允許指定一個用作該JAR文件的清單的文件。清單文件的內容還可以使用manifest任務在生成文件中指定。這個任務能夠像文件系統寫入一個清單文件,或者能夠實際嵌套在jar之內,以便一次性地創建清單文件和JAR文件。例如:

<jardestfile="package.jar"basedir="classes">

           <manifest>

              <attributename="Built-By"value="${user.name}"/>

              <attributename="Main-class"value="package.Main"/>

           </manifest>

    </jar>

 

創建javadoc文件 (<javadoc…/>)

     在編譯Java源文件之後,結果類文件通常被打包到一個JAR文件中,這個文件類似zip歸檔文件。每個JAR文件都包含一個清單文件,它可以指定該JAR文件的屬性。下面是Ant中jar任務的一個簡單使用例子:

<javadocdestdir="${distDir}/docs/api"encoding="GBK"docencoding="GBK">

    <packagesetdir="${srcDir}">

        <includename="**"/>

    </packageset>

</javadoc>

 

時間戳生成 (<tstamp/>)

     在生成環境中使用當前時間和日期,以某種方式標記某個生成任務的輸出,以便記錄它是何時生成的,這經常是可取的。這可能涉及編輯一個文件,以便插入一個字符串來指定日期和時間,或將這個信息合併到JAR或zip文件的文件名中。

這種需要是通過簡單但是非常有用的tstamp任務來解決的。這個任務通常在某次生成過程開始時調用,比如在一個init目標中。這個任務不需要屬性,許多情況下只需<tstamp/>就足夠了。tstamp不產生任何輸出;相反,它根據當前系統時間和日期設置Ant屬性。下面是tstamp設置的一些屬性、對每個屬性的說明,以及這些屬性可被設置到的值的例子:

屬性

說明

例子

DSTAMP

設置爲當前日期,默認格式爲yyyymmdd

20100307

TSTAMP

設置爲當前時間,默認格式爲hhmm

1603

TODAY

設置爲當前日期,帶完整的月份

2010年12月17日

例如,按如下方式創建了一個JAR文件:

<jardestfile="package.jar" basedir="classes"/>

在調用tstamp任務之後,我們能夠根據日期命名該JAR文件,如下所示:

<jardestfile="package-${DSTAMP}.jar" basedir="classes"/>

因此,如果這個任務在2010年03月07日調用,該JAR文件將被命名爲

package-20100307.jar。    

示例:

<targetname="package">

    <tstamp/>

    <jardestfile="package-${DSTAMP}.jar"basedir="D:\workspace\ejb3\ejb01\src"/>

</target>

     還可以配置tstamp任務來設置不同的屬性,應用一個當前時間之前或之後的時間偏移,或以不同的方式格式化該字符串。所有這些都是使用一個嵌套的format元素來完成的,如下所示:

<tstamp>

    <formatproperty="OFFSET_TIME"pattern="HH:mm:ss"offset="10"unit="minute"/>

</tstamp>

     上面的清單將OFFSET_TIME屬性設置爲距離當前時間10分鐘之後的小時數、分鐘數和秒數。用於定義格式字符串的字符與java.text.SimpleDateFormat類所定義的那些格式字符相同。

綜合示例

生成簡單 Java 項目,ant將編譯 src 目錄下的所有源代碼,將結果類文件放在 build 目錄下,然後把所有類文件打包到 dist 目錄中的一個 JAR 文件中。要自己試驗這個生成文件,您所需要的就是包含一個或多個 Java 源代碼文件的 src 目錄,這個目錄可以包含從簡單的“Hello World”程序到來自某個現有項目的大量源文件的任何內容。如果需要向 Java classpath 添加 JAR 文件或其他任何內容,以便成功地編譯源代碼,只需在 javac 任務中爲其添加一個 classpath 屬性。

// build.xml

<?xml version="1.0"?>

<projectdefault="dist"name="HelloWorld"basedir=".">

    <description>ASimple Java Project</description>

    <propertyenvironment="evn"/>

    <propertyname="srcDir"location="src"/>

    <propertyname="buildDir"location="build"/>

    <propertyname="distDir"location="dist"/>

    <targetname="init"depends="clean">

       <tstamp/>

       <mkdirdir="${buildDir}"/>

       <mkdirdir="${distDir}"/>

    </target>

    <targetname="compile"depends="init"description="編譯">

       <javacsrcdir="${srcDir}"destdir="${buildDir}"/>

    </target>

    <targetname="dist"depends="compile"description="打包">

       <jardestfile="${distDir}/package-${DSTAMP}.jar"basedir="${buildDir}">

           <manifest>

              <attributename="Built-By"value="${user.name}"/>

              <attributename="Main-Class"value="package.Main"/>

           </manifest>

       </jar>

       <jardestfile="${distDir}/package-src-${DSTAMP}.jar"basedir="${srcDir}"/>

    </target>

    <targetname="clean">

       <deletedir="${buildDir}"/>

       <deletedir="${distDir}"/>

    </target>

</project>

 

     下面是使用該文件執行的某次生成過程的示例輸出(得到的輸出可能不一樣,具體取決於src目錄的內容):

Buildfile: D:\workspace\ejb3\HelloWorld\buildfile.xml

clean:

   [delete] Deleting directoryD:\workspace\ejb3\HelloWorld\build

   [delete] Deleting directoryD:\workspace\ejb3\HelloWorld\dist

init:

    [mkdir] Created dir:D:\workspace\ejb3\HelloWorld\build

    [mkdir] Created dir:D:\workspace\ejb3\HelloWorld\dist

compile:

    [javac] Compiling 2 source files toD:\workspace\ejb3\HelloWorld\build

dist:

      [jar] Building jar: D:\workspace\ejb3\HelloWorld\dist\package-20100422.jar

      [jar] Building jar:D:\workspace\ejb3\HelloWorld\dist\package-src-20100422.jar

BUILD SUCCESSFUL

Total time: 1 second

 

文件系統操作

創建目錄 (<mkdir…/>)

     最基本的文件系統操作之一就是創建目錄或文件夾。做這項工作的任務名爲mkdir,毫不奇怪,它非常類似於具有相同名稱的Windows和UNIX/Linux命令。

<mkdirdir="archive/metals/zinc"/>

     mkdir任務的另一個有用特性是它的如下能力:在父目錄還不存在時創建它們。考慮一下上面的清單,設想archive目錄存在,但是metals目錄不存在。如果使用底層平臺的mkdir命令,您需要首先顯式地創建metals目錄,然後第二次調用mkdir命令來創建zinc目錄。但是Ant任務比這更加智能,它能夠一次性創建這兩個目錄。類似地,如果目標目錄已經存在,mkdir任務不會發出錯誤消息,而只是假設它的工作已經完成,從而什麼也不做。

 

刪除目錄 (<delete…/>)

<deletedir="archive/metals/zinc"/>

這將刪除指定的目錄連同它包含的所有文件以及子目錄。使用file屬性而不是dir屬性可以指定要刪除的單個文件。

 

複製和移動文件及目錄 (<copy…/>, <move…/>)

     在Ant中製作文件的一份拷貝很簡單。例如:

<copyfile="src/Test.java"tofile="src/TestCopy.java"/>

     還可以使用move來執行重命名操作而不是拷貝文件:

<movefile="src/Test.java"tofile="src/TestCopy.java"/>

     另一個常用的文件系統操作是將文件複製或移動到另一個目錄。做這項工作的Ant語法同樣也很簡單:

<copyfile="src/Test.java"todir="archive"/>

<movefile="src/Test.java"todir="archive"/>

     默認情況下,Ant僅輸出它執行的移動和複製操作的摘要,包括諸如已移動或複製的文件的數量等信息。如果想看到更詳細的信息,包括涉及的文件名稱等,您可以將verbose屬性設置爲true。

 

創建和解壓縮zip及tar文件 (<zip…/>, <gzip…/>)

在前一節中,我們看到了如何創建JAR文件。創建其他歸檔文件的過程幾乎完全相同。下面是創建zip文件的Ant任務:

<zipdestfile="output.zip"basedir="output"/>

相同的語法也可用於創建tar文件。還可以使用GZip和BZip任務來壓縮文件。例如:

<gzipsrc="output.tar"zipfile="output.tar.gz"/>

解壓縮和提取文件 (<unzip…/>)

<unzipsrc="output.zip"dest="extractDir"/>

還可以包括overwrite屬性來控制覆蓋行爲。默認設置是覆蓋與正在被提取的歸檔文件中的條目相匹配的所有現有文件。相關的任務名稱是untar、unjar、gunzip和bunzip2。

<unzipsrc="output.zip"dest="extractDir"/>

<unjarsrc="output.jar"dest="extractJar"/>

<untarsrc="output.tar"dest="extractJar"/>

 

替換文件中的標記 (<replace…/>)

     文件系統操作是replace任務,它執行文件中的查找和替換操作。token屬性指定要查找的字符串,value屬性指定一個新的字符串,查找到的標記字符串的所有實例都被替換爲這個新的字符串。例如:

<replacefile="input.txt"token="old-string"value="new-string"/>

     替換操作將在文件本身之內的適當位置進行。爲了提供更詳細的輸出,可把summary屬性設置爲true。這將導致該任務輸出找到和替換的標記字符串實例的數目。

 

其它有用的任務和技術

模式匹配

     使用通配符字符:”*”,它匹配零個或多個字符;以及”?”,它僅匹配一個字符。

     注意:模式匹配只能在文件集中使用,如在fileset,zipfileset中使用,而像copy,zip等屬性中不能使用。

u  對文件執行模式匹配。匹配給定目錄中以.java結尾的所有文件的模式是:”*.java”。

u  對目錄執行模式匹配。匹配帶src前綴的任何目錄中的所有Java文件的模式是:“src*/*.java”。

u  匹配任意數量的目錄的模式是:”**”,它匹配任意數量的目錄。例如,模式”**/*.java”將匹配當前目錄結構下的所有Java文件。

     以相當一致的方式對文件系統任務使用模式,比如嵌套的fileset元素。先前,我們使用這個任務來複制單個文件:

<copyfile="src/Test.java"todir="archive"/>

     如果我們想要使用一個模式,可以將file屬性替換爲一個fileset元素,如下所示:

<copytodir="archive">

    <filesetdir="src">

       <includename="**/*.java"/>

    </fileset>

</copy>

fileset默認情況下包含指定src目錄下的所有文件,因此爲了僅選擇Java文件,我們對模式使用一個include元素。類似地,我們可以對另一個模式添加一個exclude元素,從而潛在地排除include指定的匹配項。甚至可以指定多個include和exclude元素;這樣將得到一組文件和目錄,它們包含include模式的所有匹配項的並集,但排除了exclude模式的所有匹配項。注意還有一個通常很有用的文件集特性,但是對於沒有意識到它的人來說,這個特性偶爾會產生混淆。這個特性稱爲默認排除:即自動從文件集內容中排除的內置模式列表。該列表包括與名爲CVS的目錄相匹配的條目,以及以~字符結尾的文件,它們可能是備份文件。您通常不想在文件系統操作中包括這類文件和目錄,因此排除這些文件是默認行爲。然而,如果確實想無例外地選擇所有文件和目錄,可以將文件集的defaultexcludes屬性設置爲no。

 

使用選擇器

     文件集用於指定一組文件,並且這個組的內容可以使用include和exclude模式來指定。也可以結合稱爲選擇器的特殊元素使用include和exclude來選擇文件。下面是Ant可用的核心選擇器的列表:

size:這個選擇器用於根據文件的字節大小選擇文件(除非使用units屬性來指定了不同的單位)。when屬性用於設置比較的性質(less、more或者equal),value屬性定義每個文件將與之作比較的目標大小。

contains:只有包含給定文本字符串(由text屬性指定)的文件才匹配這個選擇器。默認情況下,查找操作是大小寫敏感的;添加casesensitive="no"可以改變默認設置。

filename:name屬性指定文件名要與之匹配的模式。它本質上與include元素相同,以及與指定了negate="yes"時的exclude元素相同。

present:從當前目錄結構中選擇如下文件:它們與指定的targetdir目錄中的文件具有相同的名稱和相對目錄結構。

depend:這個選擇器與present選擇器具有相同的效果,只不過匹配的文件被限制到相對於targetdir位置中的對應文件來說,最近已修改過的那些文件。

date:這個選擇器基於其最後修改日期選擇文件。when屬性指定作比較的性質是before、after還是equal,datetime屬性指定與之作比較的日期和時間,這個日期和時間具有給定的固定格式MM/DD/YYYYHH:MM AM_or_PM。注意Windows平臺上有一個內置的2秒偏移,以允許底層文件系統的不精確性,這可能導致匹配的文件數量超過預期。允許的迴旋時間量可以使用granularity屬性來更改(以毫秒爲單位來指定)。

depth:這個選擇器檢查每個文件的目錄結構層次數目。min和/或max屬性用於選擇具有想要的目錄層次數目的的文件。

     還可以通過在一個選擇器容器內嵌套一個或多個選擇器來組合選擇器。最常用的選擇器容器and僅選擇它包含的所有選擇器都選擇了的文件。其他選擇其容器包括or、not、none和majority。

下面是一個文件集的例子,它僅選擇那些大於512字節並且包含字符串“hello”的文件。

<copytodir="dist">

    <filesetdir="src">

       <and>

           <containstext="hello"/>

           <sizevalue="512"when="more"/>

       </and>

    </fileset>

</copy>

 

將生成文件鏈接起來

     有兩種生成大型項目的不同方法。一種是讓一個單一的生成文件做所有事情;另一種是讓高級別的生成文件調用其它生成文件以執行特定任務,從而將生成過程劃分爲許多較小的部分。在簡單的情況下,您可以使用antfile屬性,僅指定那些要使用的生成文件,Ant將生成該生成文件中的默認目標。例如:

<antantfile="sub-build.xml"/>

     在父生成文件中定義的任何屬性默認將傳遞給子生成文件,雖然這可以通過指定inheritAll="false"來避免。通過使用property元素來傳入顯式的屬性也是可以做到的,即使將inheritAll設置爲false,這些屬性也仍然適用於子生成文件。這個功能很適合用於給子生成文件傳入參數。

 

     讓我們來考慮一個例子。下面是我們想要調用的一個生成文件:

// sub.xml

<?xml version="1.0"?>

<projectdefault="showMessage">

    <targetname="showMessage">

       <echomessage="Message=${message}"/>

    </target>

</project>

注:echo任務,它簡單地輸出給定的消息。

下面是調用第一個生成文件的第二生成文件,它還給第一個生成文件傳入message屬性:

// build.xml

<?xml version="1.0"?>

<projectdefault="callSub">

    <targetname="callSub">

       <antantfile="sub.xml"target="showMessage"inheritAll="false">

           <propertyname="message"value="Hello fromparent build"/>

       </ant>

    </target>

</project>

     運行第二個生成文件所得到的輸出如下:

Buildfile: D:\workspace\ejb3\HelloWorld\build4.xml

callSub:

showMessage:

     [echo] Message=Hello from parent build

BUILD SUCCESSFUL

Total time: 328 milliseconds

 

使用CVS知識庫

     CVS是concurrent versionssystem(併發版本控制系統)的縮寫。它是一個源代碼控制系統,設計用於跟蹤許多不同開發人員做出的更改。注意,爲了利用Ant中的cvs任務,您需要在機器上安裝cvs命令,並使其從命令行可用。

下面是從CVS知識庫提取模塊的一個例子生成文件:

<?xml version="1.0"?>

<projectname="CVSExtract"default="extract"basedir=".">

    <propertyname="cvsRoot"value=":pserver:[email protected]:/home/eclipse"/>

    <targetname="extract">

       <cvscvsRoot="${cvsRoot}"package="org.eclipse.swt.examples"dest="${basedir}"/>

    </target>

</project>

cvs任務的主要屬性是cvsRoot,它是對CVS知識庫的完整引用,包括連接方法和用戶詳細信息。這個參數的格式如下:

[:method:][[user][:password]@]hostname[:[port]]/path/to/repository

在上面的例子中,我們作爲匿名用戶連接到Eclipse項目的中央知識庫。然後其他屬性指定了我們希望提取的模塊以及放置提取文件的目的地。提取是CVS任務的默認操作;其他操作可通過使用command屬性來指定。請參閱參考資料以瞭解關於CVS的更多信息。

 

使用自定義任務來擴展Ant

創建自定義的任務

     爲實現一個簡單的自定義任務,我們所需做的就是擴展org.apache.tools.ant.Task類,並重寫execute()方法。因此,作爲這個文件排序自定義任務的框架,我們將編寫如下代碼:

importorg.apache.tools.ant.BuildException;

importorg.apache.tools.ant.Task;

publicclass FileSorter extends Task {

//Themethod executing the task

   public void execute()throws BuildException{}

}

注意我們聲明execute()方法拋出一個BuildException異常。如果這個任務出了任何錯誤,我們將拋出這個異常以便向Ant指出故障。大多數任務,不管是核心任務還是自定義任務,都利用屬性來控制它們的行爲。對於這個簡單任務,我們需要一個屬性來指定要排序的文件,需要另一個屬性來指定排序內容的輸出。我們把這兩個屬性分別叫做file和tofile。Ant使得支持自定義任務中的屬性非常容易。爲此,我們只需實現一個具有特別格式化的名稱的方法,Ant能夠使用生成文件中指定的對應屬性的值來調用這個方法。這個方法的名稱需要是set加上屬性的名稱,因此在這個例子中,我們需要名爲setFile()和setTofile()的方法。當Ant遇到生成文件中的一個屬性設置時,它會尋找相關任務中具有適當名稱的方法(稱爲setter方法)。生成文件中的屬性是作爲字符串來指定的,因此我們的setter方法的參數可以是一個字符串。在這樣的情況下,Ant將在展開值所引用的任何屬性之後,使用該屬性的字符串值來調用我們的方法。但有時我們想把屬性的值看作是一種不同的類型。這裏的示例任務就是這種情況,其中的屬性值引用文件系統上的文件,而不只是引用任意的字符串。可以通過將方法參數聲明爲java.io.File類型來容易地做到這點。Ant將接受屬性的字符串值,並把它解釋爲一個文件,然後傳遞給我們的方法。如果文件是使用相對路徑名稱來指定的,則會被轉換爲相對於項目基目錄的絕對路徑。Ant能夠對其他類型執行類似的轉換,比如boolean和int類型。如果您提供具有相同名稱但是具有不同參數的兩個方法,Ant將使用更明確的那一個方法,因此文件類型將優先於字符串類型。這個自定義任務需要的兩個setter方法類似如下:

//Thesetter for the"file"attribute

publicvoid setFile(File file){}

//Thesetter for the"tofile"attribute

publicvoid setTofile(File tofile){}

 

實現自定義的任務

使用前一小節開發的框架,現在我們能夠完成這個簡單的文件排序任務的實現:

import java.io.*;

import java.util.*;

import org.apache.tools.ant.BuildException;

import org.apache.tools.ant.Task;

/**

 *Asimpleexampletasktosortafile

 */

publicclass FileSorterextends Task {

    private Filefile,tofile;

    // The method executing the task

    publicvoid execute()throws BuildException {

       System.out.println("Sortingfile=" + file);

       try {

           BufferedReader from =new BufferedReader(new FileReader(file));

           BufferedWriter to =new BufferedWriter(new FileWriter(tofile));

           List allLines =newArrayList();

           // read in the input file

           String line =from.readLine();

           while (line !=null) {

              allLines.add(line);

              line = from.readLine();

           }

           from.close();

           // sort the list

           Collections.sort(allLines);

           // write out the sorted list

       for (ListIterator i =allLines.listIterator(); i.hasNext();) {

              String s = (String)i.next();

              to.write(s);

              to.newLine();

           }

           to.close();

       } catch (FileNotFoundException e) {

           thrownew BuildException(e);

       } catch (IOException e) {

           thrownew BuildException(e);

       }

    }

    // The setter forthe"file"attribute

    publicvoid setFile(File file) {

       this.file = file;

    }

    // The setter for the"tofile"attribute

    publicvoid setTofile(Filetofile) {

       this.tofile = tofile;

    }

}

 

     兩個setter方法簡單地對屬性的值排序,以便這些值能夠在execute()方法中使用。這裏,輸入文件被逐行地讀入一個列表中,然後被排序並逐行地輸出到輸出文件。注意,爲簡單起見,我們很少執行錯誤檢查——例如,我們甚至沒有檢查生成文件是否設置了必需的屬性。不過我們的確至少捕捉了所執行的操作拋出的I/O異常,並將這些異常作爲BuildExceptions重新拋出。現在可以使用javac編譯器或從某個IDE內編譯這個自定義的任務。爲了解決所使用的Ant類的引用問題,您需要把ant.jar文件的位置添加到classpath中。這個文件應該在Ant安裝路徑下的lib目錄。

 

使用自定義的任務

     現在我們已經開發和編譯了這個自定義的任務,下面可以從生成文件中利用它了。在能夠調用自定義的任務之前,我們需要給它指定一個名稱來定義它,並告訴Ant關於實現這個任務的類文件的信息,以及定位該類文件所必需的任何classpath設置。這是使用taskdef任務來完成的,如下所示:

<taskdefname="filesorter"classname="FileSorter"classpath="bin"/>

     大功告成!現在可以像使用Ant的核心任務一樣使用這個自定義的任務了。下面是一個完整的生成文件,它顯示了這個自定義任務的定義和用法:

<?xml version="1.0"?>

<projectname="CustomTaskExample"default="main"basedir=".">

    <taskdefname="filesorter"classname="FileSorter"classpath="bin"/>

    <targetname="main">

       <filesorterfile="input.txt"tofile="output.txt"/>

    </target>

</project>

 

現在在當前工作目錄中創建一個input.txt文件來測試這個自定義的任務。例如:

//input.txt

Hellothere

Thisis a line

Andhere is another one

下面是運行上面的生成文件之後產生的控制檯輸出:

Buildfile: D:\workspace\ejb3\HelloWorld\build6.xml

main:

[filesorter] Sortingfile=D:\workspace\ejb3\HelloWorld\input.txt

BUILD SUCCESSFUL

Total time: 360 milliseconds

 

     注意input.txt的相對路徑名稱被轉換成了當前目錄中的一個絕對路徑名稱。這是因爲我們將setter方法的參數指定爲java.io.File類型而不是java.lang.String類型。

     您可以嘗試指定一個不存在的輸入文件,以確定該任務是如何向Ant報告“filenot found”異常的。

參考資料

1.   訪問Apache Ant主頁。這是一個極好的信息源,它包括下載、Ant手冊以及指向其他參考資料的鏈接。

2.   查找關於Ant的IDE和編輯器集成的更多信息。

3.   developerWorks開放源代碼項目專區包含豐富的基於Eclipse的內容。

4.   如果您是XML新手,請訪問developerWorksXML專區。

5.   developerWorks特別貢獻了許多關於Ant的文章:

      (1)“Incrementaldevelopment with Ant and JUnit”,Malcolm Davis撰稿,(2000年11月)。

      (2)“ExtendingAnt to support interactive builds”,Anthony Young-Garner撰稿,(2001年11月)。

      (3)“EnhanceAnt with XSL transformations”,Jim Creasman撰稿(2003年9月)。

6.   在developerWorks Java技術專區可以找到關於Java編程的方方面面的數百篇文章。

7.    參閱Java技術專區教程主頁,從developerWorks獲得免費的Java相關教程的完整列表。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章