属性是键值对,Apache Ant 尝试在运行时将 ${key}
扩展为 value
。
许多任务可以设置属性;最常见的是 property 任务。此外,属性可以通过 命令行参数 或类似机制从 Ant 外部定义。
通常属性值不能更改:一旦属性被设置,大多数任务将不允许修改其值。通常,属性具有全局范围,即,一旦属性被定义,它们就可以用于随后调用的任何任务或目标——虽然无法在通过 ant、antcall 或 subant 任务创建的子构建过程中设置属性,并使其可用于调用构建过程。
从 Ant 1.8.0 开始,local 任务可用于创建对目标或 sequential 元素(如 macrodef 任务中的元素)具有局部范围的属性。
Ant 提供对所有系统属性的访问,就好像它们是使用 <property>
任务定义的一样。例如,${os.name} 扩展为操作系统的名称。
有关系统属性的列表,请参阅 System.getProperties 的 javadoc。
此外,Ant 还有一些内置属性
basedir
ant.file
ant.version
ant.project.name
<project>
的 name 属性中设置。ant.project.default-target
<project>
的 default 属性设置。ant.project.invoked-targets
<ant>
任务等中)目标的逗号分隔列表。<project>
标记下)中使用它,则如果未指定任何目标,则列表将为空,而对于嵌套在目标中的任务,在这种情况下,列表将包含项目的默认目标。ant.java.version
9、
1.8、
1.7、
1.6、
1.5、
1.4、
1.3和
1.2。
ant.core.lib
还有一个属性,但它由启动器脚本设置,因此可能不会在 IDE 中设置
ant.home
以下属性仅在通过 Launcher 类启动 Ant 时设置(这意味着它也可能不会在 IDE 中设置)
ant.library.dir
Ant 的属性处理由与当前项目关联的 org.apache.tools.ant.PropertyHelper
实例完成。您可以通过检查 Ant 的 Java API 来了解更多关于此类的信息。在 Ant 1.8 中,PropertyHelper
类被大量重写,现在它本身使用许多辅助类(实际上是 org.apache.tools.ant.PropertyHelper$Delegate
标记接口的实例)来处理离散任务,例如属性设置、检索、解析等。这使得 Ant 的属性处理高度可扩展;同样令人感兴趣的是新的 propertyhelper 任务,用于从 Ant 构建文件的上下文中操作 PropertyHelper 及其委托。
有三个可能有用实现的 Delegate
子接口
org.apache.tools.ant.property.PropertyExpander
负责首先在字符串中找到属性名称(默认情况下从 ${foo} 中提取 foo)。如果您想发明自己的属性语法——或允许嵌套属性扩展,因为默认实现不平衡括号(请参阅 props Antlib 中的 NestedPropertyExpander
以获取示例),则您将实现此接口。
org.apache.tools.ant.PropertyHelper$PropertyEvaluator
用于将 ${some-string} 扩展为 Object
。如果您想提供自己的独立于 Ant 项目实例的存储,则您将实现此接口——该接口表示读取端。一个例子是 org.apache.tools.ant.property.LocalProperties
,它实现了 局部属性 的存储。
实现此接口的另一个原因是,如果您想提供自己的“属性协议”,例如通过查找项目引用 foo 并调用其上的 toString()
来扩展 toString:foo
(这已经在 Ant 中实现,见下文)。
org.apache.tools.ant.PropertyHelper$PropertySetter
负责设置属性。如果您想提供自己的独立于 Ant 项目实例的存储,则您将实现此接口——该接口表示写入端。一个例子是 org.apache.tools.ant.property.LocalProperties
,它实现了 局部属性 的存储。
org.apache.tools.ant.PropertyHelper$PropertyEnumerator
负责枚举属性名称。如果您想提供自己的独立于 Ant 项目实例的存储,则您将实现此接口——该接口表示读取端的一部分。一个例子是 org.apache.tools.ant.property.LocalProperties
,它实现了 局部属性 的存储。
此接口已在 Ant 1.10.9 中添加。
默认的 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}} 的内容时,它将被转换为两部分
这意味着您不能轻松扩展其名称存储在属性中的属性,但对于旧版本的 Ant,有一些 解决方法。从 Ant 1.8.0 开始,使用 props Antlib,如果您需要此功能,可以配置 Ant 使用其中定义的 NestedPropertyExpander
。
在最简单的形式中,${key} 应该查找名为 key
的属性,并扩展为该属性的值。但是,额外的 PropertyEvaluator
可能会导致对 key
的不同解释。
props Antlib 提供了一些有趣的评估器,但也有一些内置的评估器。
任何使用引用声明的 Ant 类型项也可以通过使用 ${toString:} 操作来提取其字符串值,其中引用的名称列在 toString:
文本之后。将调用被引用的 Java 类实例的 toString()
方法——所有内置类型都努力在这种情况下产生有用且相关的输出。
例如,以下是如何获取文件集中的文件列表
<fileset id="sourcefiles" dir="src" includes="**/*.java"/> <echo> sourcefiles = ${toString:sourcefiles} </echo>
不能保证外部类型在这种情况下提供有意义的信息
任何使用引用声明的 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}"/>
<target>
元素以及各种任务(例如 <fail>
)和任务元素(例如 <junit>
中的 <test>
)支持 if 和 unless 属性,这些属性可用于控制是否运行该项或是否生效。
在 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
(或 on
或 yes
)将启用该项,而 false
(或 off
或 no
)将禁用该项。其他值仍被假定为属性名称,因此只有在定义了指定名称的属性时才启用该项。
与旧样式相比,这为您提供了额外的灵活性,因为您可以从命令行或父脚本中覆盖条件。
<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>