屬性

屬性是 Apache Ant 在執行期間嘗試將 ${key} 擴充為 value 的鍵值對。

有許多任務可以設定屬性;最常見的是 property 任務。此外,屬性也可以透過 命令列參數 或 Ant 外部的類似機制定義。

通常屬性值無法變更:一旦設定屬性,大多數任務都不允許修改其值。一般而言,屬性屬於全域範圍,亦即一旦定義屬性,後續呼叫的任何任務或目標都可以使用該屬性—不過,無法在透過 antantcallsubant 任務建立的子建置程序中設定屬性,並讓呼叫建置程序使用該屬性。

自 Ant 1.8.0 起local 任務可用於建立屬性,這些屬性是局部範圍,適用於目標或 sequential 元素,例如 macrodef 任務的元素。

內建屬性

Ant 提供對所有系統屬性的存取,就像使用 <property> 任務定義這些屬性一樣。例如,${os.name} 會擴充為作業系統名稱。

如需系統屬性清單,請參閱 System.getProperties 的 javadoc

此外,Ant 有一些內建屬性

basedir
專案 basedir 的絕對路徑(使用 <project>basedir 屬性設定)。
ant.file
建置檔的絕對路徑。
ant.version
Ant 的版本
ant.project.name
目前正在執行的專案名稱;在 <project>name 屬性中設定。
ant.project.default-target
目前正在執行的專案預設目標名稱;透過 <project>default 屬性設定。
ant.project.invoked-targets
呼叫目前專案時指定的目標清單(在命令列、IDE 內部、<ant> 任務中等等)。
在執行第一個目標時,會正確設定此屬性。如果您在隱式目標中使用此屬性(直接在 <project> 標籤下方),如果未指定目標,清單將為空;如果已指定目標,則在此情況下,清單將包含專案的預設目標,適用於嵌套在目標中的任務。
ant.java.version
Ant 偵測到的 JVM 版本;目前可以儲存的值為 91.81.71.61.51.41.31.2
ant.core.lib
ant.jar 檔案的絕對路徑。

還有一個屬性,但這是由啟動器腳本設定的,因此可能未在 IDE 內部設定

ant.home
Ant 的主目錄

如果透過 Launcher 類別啟動 Ant,才會設定下列屬性(表示 IDE 內部可能不會設定)

ant.library.dir
用於載入 Ant jar 的目錄。大部分情況下,這是 ANT_HOME/lib

PropertyHelpers

Ant 的屬性處理是由與目前專案相關聯的 org.apache.tools.ant.PropertyHelper 實例完成。你可以透過檢查 Ant 的 Java API 來深入了解這個類別。在 Ant 1.8 中,PropertyHelper 類別經過大幅修改,現在本身採用許多輔助類別(實際上是 org.apache.tools.ant.PropertyHelper$Delegate 標記介面的實例)來處理個別任務,例如屬性設定、擷取、剖析等。這使得 Ant 的屬性處理高度可擴充;另一個重點是新的 propertyhelper 任務,用於從 Ant 建置檔的內容處理 PropertyHelper 及其委派。

有三個 Delegate 子介面可能有助於實作

預設的 PropertyExpander 類似於

public class DefaultExpander implements PropertyExpander {
    public String parsePropertyName(String s, ParsePosition pos,
                                    ParseNextProperty notUsed) {
        int index = pos.getIndex();
        if (s.indexOf("${", index) == index) {
            int end = s.indexOf('}', index);
            if (end < 0) {
                throw new BuildException("Syntax error in property: " + s);
            }
            int start = index + 2;
            pos.setIndex(end + 1);
            return s.substring(start, end);
        }
        return null;
    }
}

${toString:some-id} 替換為目前建置中具有 id some-id 的物件字串化表示的邏輯包含在類似下列程式碼的 PropertyEvaluator

public class ToStringEvaluator implements PropertyHelper.PropertyEvaluator {
    private static final String prefix = "toString:";
    public Object evaluate(String property, PropertyHelper propertyHelper) {
        Object o = null;
        if (property.startsWith(prefix) && propertyHelper.getProject() != null) {
            o = propertyHelper.getProject().getReference(
                    property.substring(prefix.length()));
        }
        return o == null ? null : o.toString();
    }
}

屬性擴充

當 Ant 遇到結構 ${some-text} 時,確切的解析語意會受到已設定的屬性輔助程式委派影響。

$$ 擴充

在預設設定中,Ant 會將文字 $$ 擴充為單一 $,並抑制緊接在後面的文字的正常屬性擴充機制,也就是說,$${key} 擴充為 ${key},而不是 value,即使已定義名稱為 key 且值為 value 的屬性。這可以用來跳脫文字 $ 字元,並在看起來像屬性擴充的結構中或當您想要提供診斷輸出時很有用,例如

<echo>$${builddir}=${builddir}</echo>

這會顯示此訊息

${builddir}=build/classes

如果屬性 builddir 的值為 build/classes

為了維持與舊版 Ant 版本的相容性,會將單一 $ 字元(除了屬性結構,包括一對匹配的花括號)解釋為文字,也就是 $。不過,指定此文字字元的「正確」方式是無條件使用跳脫機制,因此 $$ 是透過指定 $$$$ 取得的。混合這兩種方法會產生無法預測的結果,因為 $$$ 會產生 $$

括號巢狀

在預設組態中,Ant 不會嘗試平衡屬性擴充中的大括弧,在建立屬性名稱時,它只會使用到第一個閉合大括弧之前的文字。例如,當擴充類似 ${a${b}} 的內容時,它會轉換成兩個部分

  1. 屬性 a${b 的擴充,可能沒有什麼用處。
  2. 第二個閉合大括弧產生的文字 }

這表示您無法輕易地擴充儲存在屬性中的屬性名稱,但對於舊版的 Ant,有一些 解決方法自 Ant 1.8.0 起,如果您需要此功能,可以使用 props Antlib,將 Ant 設定為使用其中定義的 NestedPropertyExpander

擴充屬性參照

在最簡單的形式中,${key} 應尋找名為 key 的屬性,並擴充為該屬性的值。不過,其他 PropertyEvaluator 可能會對 key 有不同的詮釋。

props Antlib 提供一些有趣的評估器,但也有一些內建的評估器。

使用 ${toString:} 取得參照的值

任何已宣告為參照的 Ant 類型項目,也可以使用 ${toString:} 操作來擷取其字串值,參照的名稱列在 toString: 文字之後。會呼叫所參照的 Java 類別實例的 toString() 方法,所有內建類型都會盡力在這種情況下產生有用且相關的輸出。

例如,以下是取得檔案集中的檔案清單的方法

<fileset id="sourcefiles" dir="src" includes="**/*.java"/>
<echo> sourcefiles = ${toString:sourcefiles} </echo>

無法保證外部類型在這種情況下會提供有意義的資訊

使用 ${ant.refid:} 取得參照的值

任何已宣告為參照的 Ant 類型項目,也可以使用 ${ant.refid:} 操作作為屬性,參照的名稱列在 ant.refid: 文字之後。此操作與 ${toString:} 的差異在於,${ant.refid:} 會擴充為參照的物件本身。在大部分情況下,toString() 方法仍會被呼叫,例如,如果 ${ant.refid:} 被其他文字包圍。

當使用接受 String 以外物件的屬性設定器時,此語法最為有用。例如,如果設定器接受 Resource 物件,如下所示

public void setAttr(Resource r) { ... }

那麼語法可以用來傳入先前定義為參照的資源子類別,如下所示

<url url="https://ant.dev.org.tw/" id="anturl"/>
<my:task attr="${ant.refid:anturl}"/>

If/Unless 屬性

<target> 元素和各種任務(例如 <fail>)和任務元素(例如 <junit> 中的 <test>)支援 ifunless 屬性,可用於控制項目是否執行或產生其他效果。

在 Ant 1.7.1 及更早版本中,這些屬性只能是屬性名稱。如果定義了具有該名稱的屬性(即使是空字串或 false),則啟用項目;如果未定義屬性,則停用項目。例如,下列範例有效,但無法以否定方式(只能以肯定方式)覆寫檔案存在檢查

<target name="-check-use-file">
    <available property="file.exists" file="some-file"/>
</target>
<target name="use-file" depends="-check-use-file" if="file.exists">
    <!-- do something requiring that file... -->
</target>
<target name="lots-of-stuff" depends="use-file,other-unconditional-stuff"/>

自 Ant 1.8.0 起,您可以改用屬性擴充;true(或 onyes)的值將啟用項目,而 false(或 offno)的值將停用項目。其他值仍假設為屬性名稱,因此只有在定義了已命名屬性的情況下才會啟用項目。

與舊式相比,這提供了額外的彈性,因為您可以從命令列或父指令碼覆寫條件

<target name="-check-use-file" unless="file.exists">
    <available property="file.exists" file="some-file"/>
</target>
<target name="use-file" depends="-check-use-file" if="${file.exists}">
    <!-- do something requiring that file... -->
</target>
<target name="lots-of-stuff" depends="use-file,other-unconditional-stuff"/>

現在 ant -Dfile.exists=false lots-of-stuff 將執行 other-unconditional-stuff,但不會執行 use-file,正如您所預期的那樣,您也可以從另一個指令碼停用條件

<antcall target="lots-of-stuff">
    <param name="file.exists" value="false"/>
</antcall>

類似地,unless 屬性會在下列情況下停用項目:已定義的屬性名稱,或評估為類似 true 的值。例如,下列範例允許您在 my-prefs.properties 中定義 skip.printing.message=true,並產生您可能預期的結果

<property file="my-prefs.properties"/>
<target name="print-message" unless="${skip.printing.message}">
    <echo>hello!</echo>
</target>