From Gossip@caterpillar

JUnit Gossip: buildfile

Ant透過buildfile來進行組態,它是一個XML檔案,預設命名為build.xml。

在Ant的buildfile中可以定義構建專案時的「屬性」(property)、「任務」(task),一個build.xml中可以定義多個任務, 這些任務可能是建立目錄、編譯Java原始碼、搬移檔案、產生doc文件、進行測試、產生測試報告等等,這些任務通常組織為一個「目標」 (target)。

我們以一個簡單的HelloWorld程式來示範如何建立buildfile,並大致瞭解「屬性」(property)、「任務」(task)與「目標」 (target)的作用,雖然這個程式使用Ant來構建顯示過於誇張,但可以作為一個快速瞭解Ant的例子,首先我們在src目錄中編輯檔案:

  • HelloWorld.java
package onlyfun.caterpillar;

public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World!!");
}
}

先描述一下即將進行的工作,您要編譯src中的HelloWorld.java,其編譯成果將儲存至bin目錄中,如果 bin目錄不存在就建立它,每一次編譯將bin中前一次編譯的成果複製至bak目錄,如果bak目錄不存在就建立它,最後執行程式,您在src外編輯 build.xml如下:
  • build.xml
<?xml version="1.0"?>
<project name="example" default="run">

<property name="src.dir" value="src"/>
<property name="bin.dir" value="bin"/>
<property name="bak.dir" value="bak"/>

<target name="prepare">
<mkdir dir="${bin.dir}"/>
<mkdir dir="${bak.dir}"/>
<copy todir="${bak.dir}">
<fileset dir="${bin.dir}"/>
</copy>
</target>

<target name="compile" depends="prepare">
<javac srcdir="${src.dir}" destdir="${bin.dir}"/>
</target>

<target name="run" depends="compile">
<java classname="onlyfun.caterpillar.HelloWorld">
<classpath path="${bin.dir}"/>
</java>
</target>

</project>

<project>中包括數個<target>,每一個<target>有一個名稱, <project>的進入點預設是"run" <target>,而"run"的完成依賴於"compile"的完成,"compile"的完成又依賴於"prepare"的完成,所以在 構建開始時,會先執行"prepare" <target>,完成後再執行"compile",最後執行"run" <target>。

<property>標籤可以讓您設定一些在構建專案時的常用屬性值,每一個屬性值會有一個名稱對應,以這個例子而言,您設定了程式碼來源位置、編譯結果目標目錄與備份目錄。

一個構建中可以包括數個<target>,在這個例子中主要分作三個<target>:編譯前的準備工作、進行編譯、執行。在編 譯之前先建立所需要的目錄,如果目錄已經存在就自動跳過該項工作,然後複製build中的檔案至bak目錄。接下來進行編譯,您指定編譯的原始檔案來源與 目標,然後執行程式。

編輯好build.xml之後,在文字模式下直接下ant指令即可開始構建專案,ant預設會讀取同一個目錄下的build.xml,第一次執行ant時會出現以下的文字提示建構過程:

D:\temp>ant
Buildfile: build.xml

prepare:
    [mkdir] Created dir: D:\temp\bin
    [mkdir] Created dir: D:\temp\bak

compile:
    [javac] Compiling 1 source file to D:\temp\bin

run:
     [java] Hello World!!

BUILD SUCCESSFUL
Total time: 1 second


您可以看看ant是不是已經完成了您所指定的工作,現在假設您修改了HelloWorld.java並存檔,接下來再次下達ant指令,這次出現以下的文字提示建構過程:
D:\temp>ant
Buildfile: build.xml
 
prepare:
 
compile:
    [javac] Compiling 1 source file to D:\temp\bin

run:
     [java] Hello World!!

BUILD SUCCESSFUL
Total time: 2 seconds


這次由於bin與bak目錄已經存在,就不用再進行新建目錄的工作,ant檢查bin中有之前構建的檔案,於是將它複製至bak目錄中。

在構建程式時,有些目標所定義的任務可能是可選的,您可以藉由在設定<target>時設定一個條件,並在建構時指定該條件為true或 false,以決定該目標是否要執行,例如您可以這麼設定:
<target name="sometarget" if="conditional">
    some task.....
</target>
 

使用if設定條件目標時,表示只有在conditional被設置時才會被執行,在執行ant指令時,您就可以如下來決定sometarget是否要執行:
ant -buildfile build.xml -Dconditional=true
 

您也可以使用unless來設定條件目標:
<target name="sometarget" unless="conditional">
    some task.....
</target>
 

由於您使用了unless來設定條件目標,所以只有在conditional沒有被設定時,目標才會被執行。由於ant可以在一個buildfile中調 用另一個buildfile,在調用的時候,兩個buildfile之間可能會有一些重複的屬性設定,如果您想要避免某個屬性被重複設定了,您可以這麼撰 寫:
<target name="setProperties" unless="setProperties">
    <property name="someproperty" value="somevalue"/>
    ...
</target>
 

您可以在一個buildfile中調用另一個buildfile,一個簡單的例子如下:
<project name="someproject" default="all">
    ......
    <target name="sometarget" depends="othertarget">
        <ant antfile="./project2/build.xml">
            <property name="condition_name" value="true">
        </ant>
    <target>
    ......
</project>
 

在<property>中設定value為true,當您在另一個buildfile中有目標使用了條件式(if或unless)時,這可以給予該條件一個true的特性值。

您也可以指定專案的目錄來調用預設的build.xml,例如:
<project name="someproject" default="all">
    ....
    <target name="sometarget" depends="othertarget">
        <ant dir="./project2">
            <property name="condition_name" value="true">
        </ant>
    <target>
    ....
</project>
 

您也可以僅僅調用另一個build中的某個目標,例如:
<ant antfile="./project2/build.xml" target="prepare"/>