属性

属性是键值对,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;
    }
}

在当前构建中用具有 id some-id 的对象的字符串化表示替换 ${toString: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.apache.org/" 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>