使用 Apache Ant

撰寫簡單的建置檔

Apache Ant 的建置檔以 XML 編寫。每個建置檔包含一個專案和至少一個(預設)目標。目標包含工作元素。建置檔的每個工作元素都可以有一個 id 屬性,並可以在稍後透過提供給此屬性的值來參照。此值必須是唯一的。(如需更多資訊,請參閱下列 工作 區段。)

專案

專案 有三個屬性

屬性 說明 必要
name 專案名稱。
default 當未提供目標時要使用的預設目標。 否;不過,自 Ant 1.6.0 起,每個專案都包含一個隱含目標,其中包含所有頂層工作和/或類型。即使在使用 -projecthelp 選項執行 Ant 時,此目標仍會作為專案初始化的一部分執行。
basedir 進行所有路徑計算的基礎目錄。相對路徑會根據包含建置檔的目錄解析。 否;預設為建置檔的父目錄,除非專案的 basedirbasedir 屬性另有規定

您也可以選擇提供專案說明,方式是提供頂層 <description> 元素(請參閱 說明 類型)。

每個專案定義一個或多個目標。目標是一組您要執行的工作。在啟動 Ant 時,您可以選擇要執行的目標。如果未提供目標,則會使用專案的 default

目標

目標可以依賴其他目標。例如,您可能有編譯目標和建立可發行套件的目標。您只能在先編譯後才能建立可發行套件,因此 distribute 目標依賴於compile 目標。Ant 會解析這些依賴關係。

不過,請注意,Ant 的 depends 屬性只會指定目標應該執行的順序,它不會影響指定依賴關係的目標是否會執行,即使依賴目標未執行(或不需要執行)。

您可以在專屬的 手冊頁面 中找到更多資訊。

工作

工作是一段可執行的程式碼。

工作可以有多個屬性(或參數,如果你喜歡)。屬性的值可能包含對某個屬性的參照。在執行工作之前,這些參照將會被解析。

工作有一個共同的結構

<name attribute1="value1" attribute2="value2" ... />

其中name是工作的名稱,attributeN是屬性名稱,而valueN是此屬性的值。

有一組內建工作,但要撰寫你自己的工作也很容易。

所有工作都可以有一個name屬性。此屬性的值將用於 Ant 所產生的記錄訊息中。

工作可以指定一個id屬性

<taskname id="taskID" ... />

其中taskname是工作的名稱,而taskID是此工作的唯一識別碼。你可以透過此名稱在腳本或其他工作中參照對應的工作物件。例如,在腳本中你可以執行

<script ... >
  task1.setFoo("bar");
</script>

來設定此特定工作實例的foo屬性。在另一個工作(以 Java 撰寫)中,你可以透過project.getReference("task1")存取實例。

注意 1:如果task1尚未執行,則尚未設定(即尚未設定任何屬性),而且如果稍後要設定,你對實例所做的任何變更都可能會被覆寫。

注意 2:Ant 的未來版本很可能不會向下相容於此行為,因為很可能根本不會有任何工作實例,只有代理程式。

屬性

屬性是自訂建置程序或僅為建置檔中重複使用的字串提供捷徑的重要方式。

在最簡單的形式中,屬性會在建置檔中定義(例如透過 property 任務)或可能在 Ant 外部設定。屬性有一個名稱和一個值;名稱區分大小寫。屬性可以用在任務屬性的值中,或支援它們的任務的巢狀文字中。這是透過將屬性名稱放在屬性值中 ${} 之間來完成的。例如,如果有一個值為 buildbuilddir 屬性,那麼它可以用在像這樣的屬性中:${builddir}/classes。這會在執行時解析為 build/classes

自 Ant 1.8.0 起,屬性擴充變得比單純的鍵值對更強大,更多詳細資料可以在本手冊的 概念區段 中找到。

範例建置檔

<project name="MyProject" default="dist" basedir=".">
  <description>
    simple example build file
  </description>
  <!-- set global properties for this build -->
  <property name="src" location="src"/>
  <property name="build" location="build"/>
  <property name="dist" location="dist"/>

  <target name="init">
    <!-- Create the time stamp -->
    <tstamp/>
    <!-- Create the build directory structure used by compile -->
    <mkdir dir="${build}"/>
  </target>

  <target name="compile" depends="init"
        description="compile the source">
    <!-- Compile the Java code from ${src} into ${build} -->
    <javac srcdir="${src}" destdir="${build}"/>
  </target>

  <target name="dist" depends="compile"
        description="generate the distribution">
    <!-- Create the distribution directory -->
    <mkdir dir="${dist}/lib"/>

    <!-- Put everything in ${build} into the MyProject-${DSTAMP}.jar file -->
    <jar jarfile="${dist}/lib/MyProject-${DSTAMP}.jar" basedir="${build}"/>
  </target>

  <target name="clean"
        description="clean up">
    <!-- Delete the ${build} and ${dist} directory trees -->
    <delete dir="${build}"/>
    <delete dir="${dist}"/>
  </target>
</project>

請注意,我們在任何目標外部宣告屬性。自 Ant 1.6 起,所有任務都可以在目標外部宣告(較早的版本只允許 <property><typedef><taskdef>)。當你這麼做時,它們會在任何目標執行之前評估。如果在目標外部使用某些任務,它們會產生建置失敗,因為它們可能會造成無限迴圈(例如 <antcall>)。

我們已經提供了一些目標說明;這會導致 -projecthelp 呼叫選項將它們列為具有說明的公開目標;其他目標是內部的,不會列出。

最後,為了讓這個目標運作,src 子目錄中的來源應該儲存在與套件名稱相符的目錄樹中。查看 <javac> 任務以取得詳細資料。

權杖篩選器

專案可以有一組權杖,如果在複製檔案時找到這些權杖,它們可能會自動擴充,當支援此功能的任務中選擇過濾複製行為時。這些權杖可能會在建置檔中由 filter 任務設定。

由於這可能會造成非常有害的行為,檔案中的權杖必須@token@ 形式,其中 token 是在 <filter> 任務中設定的權杖名稱。這個權杖語法與執行此類過濾的其他建置系統的語法相符,並且與大多數程式設計和指令碼語言以及文件系統保持足夠的正交性。

注意:如果在檔案中找到格式為 @token@ 的令牌,但沒有與該令牌關聯的篩選器,則不會進行任何變更;因此,沒有可用的跳脫方法,但只要您為令牌選擇適當的名稱,這就不會造成問題。

警告:如果您在開啟篩選功能的情況下複製二進位檔案,可能會損毀檔案。此功能應用於文字檔案。

路徑結構

您可以使用 :; 作為分隔符號,使用 :; 指定 PATHCLASSPATH 類型的參考。Ant 會將分隔符轉換為當前作業系統的正確字元。

無論何時需要指定類似路徑的值,都可以使用巢狀元素。這採用一般形式

<classpath>
  <pathelement path="${classpath}"/>
  <pathelement location="lib/helper.jar"/>
</classpath>

location 屬性指定相對於專案基本目錄(或絕對檔案名稱)的單一檔案或目錄,而 path 屬性接受以冒號或分號分隔的位置清單。path 屬性旨在與預定義路徑一起使用,在任何其他情況下,應優先使用具有 location 屬性的多個元素。

自 Ant 1.8.2 起location 屬性也可以在其最後路徑組件中包含萬用字元(即它可以以 * 結尾),以支援 Java 6 中引入的萬用字元 CLASSPATH。Ant 不會展開或評估萬用字元,而產生的路徑可能無法用於任何其他用途,除了 CLASSPATH,甚至無法用於 Java 6 之前的 JVM 的 CLASSPATH

作為捷徑,<classpath> 標籤支援自己的 pathlocation 屬性,因此

<classpath>
  <pathelement path="${classpath}"/>
</classpath>

可以簡寫為

<classpath path="${classpath}"/>

此外,可以將一個或多個 資源集合 指定為巢狀元素(這些元素必須僅包含 檔案 類型的資源)。此外,應注意,儘管資源集合會按遇到的順序進行處理,但某些資源集合類型(例如 filesetdirsetfiles)在順序方面未定義。

<classpath>
  <pathelement path="${classpath}"/>
  <fileset dir="lib">
    <include name="**/*.jar"/>
  </fileset>
  <pathelement location="classes"/>
  <dirset dir="${build.dir}">
    <include name="apps/**/classes"/>
    <exclude name="apps/**/*Test*"/>
  </dirset>
  <filelist refid="third-party_jars"/>
</classpath>

這會建立一個路徑,其中包含 ${classpath} 的值,後跟 lib 目錄中的所有 jar 檔案、classes 目錄、${build.dir}apps 子目錄下所有名為 classes 的目錄,但排除名稱中包含文字 Test 的目錄,以及在引用的 FileList 中指定的檔案。

如果您想對多個任務使用相同的類似路徑結構,則可以在與 <target> 相同層級定義它們,並透過其 id 屬性參照它們,請參閱 參照 以取得範例。

預設情況下,路徑結構會在每次使用時重新評估所有巢狀資源集合,這可能會導致不必要的檔案系統重新掃描。自 Ant 1.8.0 起,路徑有一個可選的 cache 屬性,如果將其設定為 true,路徑實例只會掃描其巢狀資源集合一次,並假設它在建置過程中不再變更(cache 的預設值仍為 false)。即使您只在單一任務中使用路徑,如果使用複雜的巢狀結構,將 cache 設定為 true 仍可能改善整體效能。

路徑結構可以透過巢狀 <path> 元素包含對另一個路徑結構(路徑本身是一個資源集合)的參考

<path id="base.path">
  <pathelement path="${classpath}"/>
  <fileset dir="lib">
    <include name="**/*.jar"/>
  </fileset>
  <pathelement location="classes"/>
</path>

<path id="tests.path" cache="true">
  <path refid="base.path"/>
  <pathelement location="testclasses"/>
</path>

先前提到的 <classpath> 捷徑也適用於 <path>。例如

<path id="base.path">
  <pathelement path="${classpath}"/>
</path>

可以寫成

<path id="base.path" path="${classpath}"/>

路徑捷徑

自 Ant 1.6 起,有一個捷徑可以在屬性中將路徑轉換為作業系統特定的字串。可以使用表達式 ${toString:pathreference} 將路徑元素參考轉換為可作為路徑參數使用的字串。例如

<path id="lib.path.ref">
  <fileset dir="lib" includes="*.jar"/>
</path>
<javac srcdir="src" destdir="classes">
  <compilerarg arg="-Xbootclasspath/p:${toString:lib.path.ref}"/>
</javac>

命令列參數

多個任務會取得參數,這些參數會在命令列傳遞給另一個程序。為了讓指定包含空白字元的參數更為容易,可以使用巢狀 arg 元素。

屬性 說明 必要
單一命令列參數;可以包含空白字元。 只能有一個。
檔案 檔案名稱作為單一命令列參數;會替換為檔案的絕對檔名。
路徑 會被視為路徑字串的字串,作為單一命令列參數;可以使用 ;: 作為路徑分隔符號,而 Ant 會將其轉換為平台的當地慣例。
路徑參考 參考其他地方定義的路徑。Ant 會將其轉換為平台的當地慣例。
以空白分隔的命令列參數清單。
前置詞 固定字串,置於參數之前。如果將列拆成多個部分,它會置於每個部分之前。自 Ant 1.8 起。
後置詞 固定字串,置於參數之後。如果將列拆成多個部分,它會置於每個部分之後。自 Ant 1.8 起。

強烈建議在可能的情況下避免使用 line 版本。Ant 會嘗試以類似於(Unix)shell 的方式分割命令列,但在某些情況下可能會產生與您預期非常不同的結果。

範例

<arg value="-l -a"/>

是包含空白字元的單一命令列參數,不是 分開的選項 -l-a

<arg line="-l -a"/>

這是包含兩個選項 -l-a 的命令列。

<arg path="/dir;/dir2:\dir3"/>

在基於 DOS 的系統上為單一命令列引數,其值為 \dir;\dir2;\dir3,而在類 Unix 系統上為 /dir:/dir2:/dir3

參考

任何專案元素都可以使用其 id 屬性指定識別碼。在大部分情況下,元素可透過在相同類型的元素上指定 refid 屬性來進行後續參照。如果您要重複使用相同的 XML 片段,這會很有用,例如重複使用 <classpath> 結構。

下列範例

<project ... >
  <target ... >
    <rmic ...>
      <classpath>
        <pathelement location="lib/"/>
        <pathelement path="${java.class.path}/"/>
        <pathelement path="${additional.path}"/>
      </classpath>
    </rmic>
  </target>

  <target ... >
    <javac ...>
      <classpath>
        <pathelement location="lib/"/>
        <pathelement path="${java.class.path}/"/>
        <pathelement path="${additional.path}"/>
      </classpath>
    </javac>
  </target>
</project>

可以改寫成

<project ... >
  <path id="project.class.path">
    <pathelement location="lib/"/>
    <pathelement path="${java.class.path}/"/>
    <pathelement path="${additional.path}"/>
  </path>

  <target ... >
    <rmic ...>
      <classpath refid="project.class.path"/>
    </rmic>
  </target>

  <target ... >
    <javac ...>
      <classpath refid="project.class.path"/>
    </javac>
  </target>
</project>

所有使用 PatternSetFileSetZipFileSet類似路徑的結構 的巢狀元素的任務,都接受對這些結構的參照,如範例所示。在任務上使用 refid 通常會有相同的效果(參照已宣告的任務),但使用者應注意,此屬性的詮釋取決於其指定元素的實作。有些任務(property 任務是一個實用的範例)會故意將不同的意義指定給 refid

使用外部任務

Ant 支援外掛機制,可使用第三方任務。若要使用它們,您必須執行兩個步驟

  1. 將其實作放置在 Ant 可以找到它們的地方。
  2. 宣告它們。

不要在 CLASSPATH 環境變數中新增任何內容,這通常是造成非常難以理解的錯誤的原因。使用 Ant 自有的 機制 來新增函式庫

有數種宣告方式

如果您需要特殊功能,您應該
  1. 查看本手冊,因為 Ant 提供許多任務
  2. 查看外部任務頁面 線上
  3. 查看外部任務 wiki 頁面
  4. Ant 使用者 清單中詢問
  5. 實作(並分享)你自己的