Ant build .xml 文件结构介绍

Project

每个 project 可以有以下三个属性:

  • name:项目的名称。该属性为非必需。

  • default:当没有提供 target 时指定所用的默认的 target,在不指名 target 的情况下调用 ant 命令,会执行该 attribute 所指定的 target。。然而,自 Ant 1.6.0 开始,每个项目都包含一个隐式的 target,该 target 包含任意 target 和顶级的 target 和(或)类型,该 target 将作为 project 初始化的一部分而总会被执行,即使在使用 -projecthelp 选项来运行 Ant 时也会执行该默认 target。该属性为非必需。

  • basedir:所有路径计算的基础目录。该 attribute 会被提前设置的 "basedir" property 所覆盖。如果设置了 "basedir" property,则 project 元素中的该 attribute 将会被忽略。如果两者都未设置,将使用 buildfile 的父级目录作为 attribute 的值,该值将被解析为一个相对于包含 buildfile 的的相对目录。该属性为非必需。

Description

元素下的顶级 元素(它是一个 Ant 类型)是可选的,其内容将会包含在 ant -projecthelp 命令的输出中。比如:


    This build file is used to build the Foo sub project within the large, complex Bar project.

Properties

Property 就是 key-value 对,key 和 value 的值都是大小写敏感的。Property 可用于 target 的 attribute 中,或者用在支持引用 property 的 Task 的嵌套文本中。Property 是不可变的:一旦设置一个 property,就会在构建时固定而无法修改。

Ant 可以通过 ${key} 的形式来方便地在运行时重复引用对应的 value。通常,Property 定义在 build.xml 文件中, 或定义在 Ant 外部。例如,如果有一个 "buildDir" 的 property,其 value 为 "build",可以在一个 attribute 中这样使用该属性值:${buildDir}/classes,这将在运行时被解析为 build/classes。

设置 Property

一共有八种设置 property 的方式:

  • 提供 name attribute,以及 value 和 location 这两个 attribute 之一;

  • 提供 name attribute 和嵌套文本;

  • 提供 name attribute 和 refid attribute;

  • 设置 file attribute,并为其指定一个需要加载 property 的 *.properties 本地文件位置。这些 *.properties 文件需要具有 java.util.Properties 类所用文件的格式,其中的 non-ISO-8859-1 字符(比如中文)需要进行转义,包含文件中注释;

  • 设置 url attribute,并为其指定一个需要加载 property 的 URL 。该 URL 必需指向一个具有 java.util.Properties 类所用文件格式的文件;

  • 设置 resource attribute 并为其指定一个需要加载属性的资源文件名。资源文件名是一个位于当前 classpath 或特定 classpath 下的 *.properties 文件;

  • 设置 environment attribute 并为其指定一个前缀。通过在变量前面添加前缀和句点来为每个环境变量定义 property;

  • 设置 runtime attribute 并为其指定一个前缀。prefix.availableProcessorsprefix.freeMemoryprefix.totalMemoryprefix.maxMemory 等 property 的值将通过 Runtime 类中相关方法的返回值来定义;

设置 property 的最常用方式就是 Task 。虽然以上这些设置 property 的方法可以组合使用,但一次只能使用一种。否则,property 设置的顺序可能会出现问题。

所设置的 property 的 value 部分可能包含对其他 property 的引用,会在设置这些 property 时解析这些引用。这种解析也适用于从 *.properties 文件加载 property。

一般来说,property 是全局范围的,也就是说,一旦定义了 property,它们就可以用于随后调用的任何 Task 或 Target,但是不可能在通过 ant、antcall 或 subant 等 Task 创建的子构建过程中设置 property 并使其可用于调用的构建过程。有一个例外情况,自 Ant 1.8.0 开始,有个 local Task 可以用来创建 property,这些 property 在本地作用于一个 Target 或一个 sequential 元素,比如 macrodef Task 。

Task 中可设置 attribute 如下:

  • name:该 property 的名称;

  • value:该 property 的值;

  • location:将 property 设置为给定文件的文件路径。如果此属性的值是绝对路径,则保持不变(将 /\ 字符转换为当前平台的路径分隔符)。如果是相对路径,则它将为相对于当前 project 的 basedir 的路径;

  • refid:对别处定义的对象的引用。只有对 path-like 的结构或 property 引用才会产生合理的结果;

  • resource:classpath 下的 *.properties 文件名称。比如,

  • file:指定一个需要加载 property 的 *.properties 本地文件位置;

  • url:指向一个需要加载 property 的 URL 资源;

  • environment:用来检索环境变量时所使用的前缀。因此,如果指定 environment="myenv",则可以通过属性名 myenv.PATHmyenv.TERM 访问特定于操作系统的环境变量。请注意,如果提供了一个 name 值以 . 字符结尾的 property, . 也不会重复解析。比如,如果设置了 environment="myenv.",仍然允许通过 myenv.PATHmyenv.TERM 来访问环境变量。该 attribute 仅在某些特定的平台上可用,后续会通过不定来增加。另外,需要注意:property 是区分大小写的,即使某些平台的环境变量不区分大小写。从 Ant 1.8.2 开始,如果 Ant 检测到它正在 Java 5 或更新版本的 Java 环境上运行,Ant 将使用System.getenv() 而不是依赖于操作系统的本机实现来获取环境变量;

  • runtime:用来检索运行时 property 时使用的前缀。因此,如果指定 runtime="myrt",则可以通过属性名 myrt.availableProcessorsmyrt.maxMemorymyrt.totalMemorymyrt.freemory 访问 Runtime 类中的相应方法的返回值。请注意,如果提供了一个 name 值以 . 字符结尾的 property, . 也不会重复解析。比如,如果设置了 runtime="myrt.",仍然允许通过 myrt.maxMemory 来访问运行时属性。从 Ant 1.10.4 开始引入。Ant 提供对所有系统属性的访问,就像它们是使用 Task 定义的一样。例如,${os.name} 为操作系统的名称。所有可用的系统属性可参考 System.getProperties 的 javadoc 文档。除此之外,Ant 还提供了一些内置的 property:

    • basedir:项目的 basedir 的绝对路径。引用 的 basedir attribute 的值);

    • ant.file:构建文件 build.xml 的绝对路径;

    • ant.version:Ant 的版本;

    • ant.project.name:当前正在执行的项目的名称。引用 的 name attribute 的值;

    • ant.project.default-target:当前正在执行的项目的默认 target 的名称;引用 的 default attribute 的值;

    • ant.project.invoked-targets:调用当前项目时(在命令行上、IDE内部、由 任务等)指定的逗号分隔的目标列表。此 property 在执行第一个目标时设置。如果在隐式目标中使用它(直接在 标记下),则如果未指定目标,列表将为空,而在这种情况下,它将包含项目默认目标(任务嵌套到目标中)。

    • ant.java.version:检测到的 JVM 版本;目前它的值有:9、1.8、1.7、1.6、1.5、1.4、1.3 和 1.2;

    • ant.core.lib:ant.jar 文件的绝对路径;

    • ant.home:Ant 的主目录;

    • ant.library.dir:用于从中加载 Ant 的 jar 包的目录。该值在大多数情况下为 ANT_HOME/lib。该 property 仅在 Ant 通过 Launcher 类的程序脚本在启动时设置(这意味着它也不能在 IDE 中设置)。

  • classpath:查找资源时要使用的 classpath;

  • classpathref:查找资源时要使用的 classpath。作为对别处定义的 的引用;

  • prefix:应用于使用 file、resource 或 url 等 attribute 加载的 property 的前缀。将会在前缀之后添加一个 . (如果未设置的话);

  • prefixValues:在解析使用 file、resource 或 url 等 attribute 所加载的 property 右侧时是否应用前缀。从 Ant 1.8.2 开始引入;

  • relative:如果设置为 true,则设置 basedir 的相对路径。从 Ant 1.8.2 开始引入;

  • basedir:从中计算相对路径的 basedir。从 Ant 1.8.2 开始引入。

常见的 property 示例如下:

  1. 将 foo.dist 的 property 值设置为 dist: ,与 dist 等同;

  2. 从名为 foo.properties 的文件中读取一组 property:

  3. 从地址 https://www.mysite.com/bla/props/foo.properties 读取一组 property:

  4. 从名为 foo.properties 的资源中读取一组 property:

  5. 为所有 Ant 构建引用全局 .properties 文件:user.home property 在文件系统中解析到的位置取决于操作系统版本和 JVM 实现。在基于 Unix 的系统上,这将映射到用户的主目录。在现代 Windows 变体上,这很可能解析为 Documents and Settings 或 Users 文件夹中的用户目录。

  6. 下面的 buildfile 读取系统环境变量并将其存储在以 env 为前缀的属性中:

   
   
   
  1. 下面的 buildfile 使用 build.properties 中定义的 property,对于先声明的包含环境变量 STAGE 的 .properties 文件中定义的 property 和其他 .properties 文件中定义的 property,都可能被后面的build.properties 文件定义的 property 所覆盖,例如,STAGE=testtest.properties 具有特殊的值(比如测试服务器的另一个名称)。最后,所有这些值都可以通过每个用户一个文件的个人设置来覆盖。
   
   
   
   
  1. 在 foo property 值中存储相对路径 ../my/file.txt

  2. 在 foo property 值中存储相对路径 cvs/my/file.txt

上面所用的 .properties 文件具有以下几个特性:

  • 如果所指定的文件不存在,除了使用 -verbose 级别的日志外,不会打印任何内容。这让您可以为每个项目提供可选的配置文件,团队成员可以自定义这些文件;

  • 此文件的格式需要与 java.util.Properties 所能解析的文件的格式相同;

  • 不会删除默认的空格,这可能是您所想要的;

  • 如果您想在这些 .properties 文件中使用 non-ISO-8859-1 字符(比如中文)或其他特殊字符(比如反斜线 \),则需要对这些字符进行转义,比如 \u0456\"\\的格式;

  • 如果要解析多个位于同一 .properties 文件中定义的 property,并且使用 Task 的 prefix 属性,则在解析 property 时必须使用相同的前缀,或将prefixValues 设置为 true。

根据 java.util.Properties 的 load(Reader reader) 方法的文档中所述:Property 是按行处理的。有两种行,自然行和逻辑行。自然行定义为以行终止符(\n\r\r\n)结尾或以流结束终止的行。自然行可以是空行、注释行,也可以包含全部或 key-value 对的部分内容。逻辑行包含一个 key-value 对的所有内容,逻辑行可以通过使用反斜杠字符 \ 转义行终止序列,从而使逻辑行跨越多个自然行。注意:注释行不能以这种方式跨越多行,作为注释的每个自然行都必须有自己的注释标识符,如下所述。行从输入读取,一直读到流的末尾。

仅包含空白字符的自然行被视为空白,将被忽略。注释行具有 ASCII 的 #! 字符来作为其第一个非空白字符;注释行也被忽略,不编码 key-value 信息。除行终止符外,该格式还将空格(\u0020)、水平制表符(\t\u0009)和换页符(form feed,\f\u000C)视为空白符。

如果一条逻辑行跨越多条自然行,则转义行终止符序列的反斜杠、行终止符序列和下一行开始处的任何空白对 key 和 value 没有影响。关于 key 和 value 的解析(加载时)的其余讨论将假定构成 key 和 value 的所有字符在删除行连续字符后出现在单个自然行上。请注意,仅检查行终止符序列前面的字符以确定是否转义了行终止符还是不够的,必须有奇数个连续反斜杠才能转义行终止符。由于输入是从左到右处理的,因此在转义处理之后,行终止符(或其他位置)之前的第 2n 个(非零偶数编码)的连续反斜杠会编码第 n 个反斜杠。

其中,key 包含每行中从第一个非空白字符开始,到第一个未转义的 =: (不含)字符,或到除行终止符以外的第一个空白字符之间的所有字符(如果没有未转义的 =: 字符),但不包括这些字符(第一个未转义的 =: (不含)字符,或除行终止符以外的第一个空白字符)。所有这些 key 终止字符(=:)都可以通过在前面使用反斜杠字符转义而包含在 key 中;例如 \:\= 将是两个字符的 key(:=)。可以使用转义序列 \r\n 来包含行终止符。key 后面的任何空白符都将会跳过。如果 key 后面的第一个非空白符是 =:,则忽略它,并跳过它之后的任何空白字符。行上剩余的所有其他字符将成为对应 value 的一部分,如果没有剩余字符,则 value 为空字符串。一旦识别出构成 key 和 value 的原始字符序列,如上所述执行转义处理。

例如,以下三行中的每一行都指定了 key 为 true 和 value 为 Beauty 的 Property:

Truth = Beauty                    # key 包含从第一个非空白字符开始,到第一个未转义的 = 字符之间的所有字符
  Truth:Beauty                    # key 包含从第一个非空白字符开始,到第一个未转义的 : 字符之间的所有字符
Truth                  :Beauty 

作为另一个示例,以下三行指定了一个 property:

fruits                           apple, banana, pear, \  
                            cantaloupe, watermelon, \
                            kiwi, mango # key 包含从第一个非空白字符开始,到除行终止符以外的第一个空白字符之间的所有字符(如果没有=、:)

其中,key 是 fruits,对应的 value 是:apple, banana, pear, cantaloupe, watermelon, kiwi, mango

请注意,每个 \ 前面都会出现一个空格,这样在最终结果中,每个逗号后面都会出现一个空格;\、行终止符、续行上的前导空格将被丢弃,不会被一个或多个其他字符替换。

作为第三个示例,

cheeses

上面的行指定 key 为 cheeses,对应的 value 为空字符串。

key 和 value 中的字符可以用转义序列表示,转义序列类似于用于字符和字符串文本的转义序列(参见 Java 语言规范 §3.3 和 §3.10.6)。与用于字符和字符串的字符转义序列和 Unicode 转义的区别是:

  • 在这里,不会识别八进制的转义字符。
  • 在这里,字符序列 \b 不表示退格字符。
  • 该方法不会将无效转义字符之前的反斜杠字符 \ 视为错误;反斜杠会自动删除。例如,在 Java 字符串中,序列 \z 将导致编译时错误。相反,该方法会自动删除反斜杠。因此,将 \b 这两个字符序列视为等同于单个字符 b
  • 单引号和双引号不需要转义;但是,根据上面的规则,前面带有反斜杠的单引号和双引号字符仍然分别产生单引号和双引号字符。
  • Uniocde 转义序列中只允许一个 u 字符。

该方法返回后,指定的流仍会保持打开状态。

如果 key 中包含 =:,则需要将这些字符转移,由于该方法只识别第一个未转义的 =: (不含)字符,所以,value 中 =: 不需要转移

Property 的解析

解析原理

Ant 的 property 处理由与当前项目关联的 org.apache.tools.Ant.PropertyHelper 实例完成。您可以通过检查 Ant 的 Java API 来了解更多关于这个类的信息。在Ant 1.8 中,PropertyHelper 类经过了大量的修改,现在它自己使用了许多助手类(实际上是 org.apache.tools.Ant.PropertyHelper$Delegate marker 接口的实例)来处理不连续的任务,如 property 设置、检索、解析等,这使得 Ant 的 property 处理具有高度的可扩展性;另一个有趣的是新的 propertyhelper 的 Task ,该 Task 用于从 Ant 构建文件的上下文操纵 PropertyHelper 及其委托类的示例。Delegate 有四个子接口可用于实现:

  • org.apache.tools.ant.property.PropertyExpander 首先负责在字符串中查找 property 的名称(默认值从 ${foo} 中提取 foo)。默认实现对大括号的处理不是很好,如果您想发明自己的 property 语法或允许嵌套的 property 解析,那么可以实现这个接口(请参见 props Antlib 中的 NestedPropertyExpander 获取示例);

  • org.apache.tools.ant.PropertyHelper$PropertyEvaluator 用于将 ${some string} 解析为对象。如果您想独立于 Ant 的项目实例提供自己的存储,那么应该实现这个接口。这个接口表示读取端。例如 org.apache.tools.ant.property.LocalProperties,它实现了 local properties 的存储。实现这个接口的另一个原因是,如果您想提供自己的“property 协议”,比如通过查找项目中的引用 foo 并在其上调用 toString() 方法来解析 toString:foo(已经在Ant中实现了,见下文);

  • org.apache.tools.ant.PropertyHelper$PropertySetter 负责设置 property。如果您想独立于 Ant 的项目实例提供自己的存储,那么应该实现这个接口。这个接口代表了写入端。例如 org.apache.tools.ant.property.LocalProperties,它实现了 local properties 的存储;

  • org.apache.tools.ant.PropertyHelper$PropertyEnumerator 负责枚举 property 名称。如果您想独立于Ant的项目实例提供自己的存储,那么应该实现这个接口。这个接口表示读取端的一部分。例如 org.apache.tools.ant.property.LocalProperties,它实现了 local properties 的存储。这个接口是自 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;
    }
}

${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 将文本 $$ 解析为单个 $,并抑制紧跟其后的文本的常规 property 解析机制,即将 $${key} 扩展为 ${key},而不是 key 对应的值。这可用于转义 $ 字符,在仅展示 property 解析的构造中,或当您希望提供类似于诊断输出的场景时非常有用:

$${builddir}=${builddir}

如果名为 builddir 的 property 值为 build/classes,则上面一行将输出以下信息:

${builddir}=build/classes

为了保持与旧版 Ant 版本的向后兼容性,除了类似 property 的构造(包括一对匹配的大括号)以外遇到的单个 $ 字符将被逐字解释,即 $。然而,指定这个文字字符的正确方法应该是无条件地使用转义机制,这样就可以通过指定 $$$ 来获得 $$

嵌套花括号

在其默认配置中,Ant 不会尝试在 property 解析中智能的处理花括号,在创建 property 名称时,它只会使用第一个右花括号之前的文本。例如,当解析 ${a${b}} 这样的内容时,它将被转换为两部分:

  • 将解析出名为 a${b 的 property,这好像毫无用处;

  • 将再解析出第二个右花括号的字符 }

这意味着您不能轻松地解析名称存储在 property 中的 property,但是对于旧版本的 Ant 有一些应对方法。由于 Ant 1.8.0 使用了 props Antlib,如果需要这样的特性,可以将 Ant 配置为使用其中定义的 NestedPropertyExpander。

Property 引用的解析

在其最简单的形式中,${key} 应该查找名为 key 的 property 并到该属性的值。不过,PropertyEvaluators 可能会对名为 key 的 property 产生不同的解释。 props Antlib 提供了一些有趣的 evaluator,但也有一些内置的。

${toString:}

任何 Ant 类型项均可以使用 ${toString:} 操作来声明为一个值为字符串的 property 的引用。其中,toString:} 之间的内容即为引用的名称,而 property 的字符串值即为 Ant 类型对象的 toString() 方法的返回值。所有内置的 Ant 类型都尽量会在其 toString() 方法中生成有用的相关输出。

例如,下面是如何获取 fileset 中的文件列表:


 sourcefiles = ${toString:sourcefiles} 

但这种用法不能保证外部类型也可以通过 toString() 方法提供有意义的信息。

${ant.refid:}

任何 Ant 类型项均可以使用 ${ant.refid:} 操作来声明为一个 property 引用。其中,ant.refid: 后面的 ref-id 内容即为引用的名称。此操作与 ${toString:} 之间的区别在于 ${ant.refid:} 将解析到引用的对象本身。在大多数情况下,用的也是 toString() 方法的返回值来作为 property 的值,例如,当 ${ant.refid:} 被其他文本包围时。

对于那些使用字符串以外的对象作为 setter 的入参的 Task ,此语法非常有用。例如,如果 setter 接受资源对象,如:

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

然后,可以使用该语法传入以前定义为引用的资源子类,如:



If/Unless Attributes

元素和各种 Task (例如 )以及 Task 元素(例如 中的 )都支持名为 ifunless(如果不) 的 attribute,这些 attribute 可用于控制 Task 是否会运行或生效。

在 Ant 1.7.1 及更早版本中,这些 attribute 只能是 property 的名称。如果具有该名称的 property 被定义了(不管其值如何,即使为空字符串),则启用该 attribute 对应的项;如果具有该名称的 property 未定义,则禁用该项。例如,下面的方法可以工作,但是没有其它方式来覆盖文件存在性的否定检查(仅以肯定的方式):


    


    


从 Ant 1.8.0 开始,您可以改为使用 property 扩展:值 true(或 onyes)则启用该项,值为 false(或 offno)则禁用该项。其他值仍假定为 property 名称,因此,在使用其他值(除 trueonyesfalseoffno 以外的值)时,仅当定义了具有名称的 property 时才启用该项。与旧样式相比,这提供了更多的灵活性,因为可以从命令行或父脚本重写条件:


    


    


现在,ant -Dfile.exists=false lots-of-stuff 将会运行 other-unconditional-stuff,但不 use-file 不运行。如您所料,您还可以从另一个脚本中禁用该条件:


    

类似地,对于某个项来说,如果它是某个已定义的 property 的名称,或者如果它的计算结果为 true(或 onyes),则 unless attribute 会禁用这个项。例如,通过以下命令,可以在 my-prefs.propertie 中定义 skip.printing.message=true,并获得预期的结果(这里将打印 hello!):



    hello!

Token Filters

一个 Project 可以有一组 Token,当发生文件复制时,且在支持该 Token 的 Target 中选择了过滤复制行为,则这些 Token 可能会自动解析。这些可能由 filter 任务在构建文件中设置。

由于这可能是一种非常有害的行为,因此文件中的 Token 的形式必须为 @token@,其中 token 是在 Task 中设置的 Token 名称。此 Token 语法与执行此类过滤的其他构建系统的语法相匹配,并与大多数编程和脚本语言以及文档系统保持足够的正交性。

注意:如果在文件中找到格式为 @token@ 的 Token,但没有与该 Token 关联的过滤器,则不会发生任何更改;因此,没有可用的转义方法,但只要为 Token 选择适当的名称,这就不会导致问题。

警告:如果在启用筛选的情况下复制二进制文件,可能会损坏文件。此功能只能用于文本文件。

Path-like Structures

您可以使用冒号(:)和分号(;)作为分隔符来指定 PATHCLASSPATH 类型的引用。Ant 将把分隔符转换为当前操作系统的正确字符。无论在哪里指定了 path-like 的值,都可以使用嵌套元素。其一般形式为:


  
  

location attribue 指定相对于 Project 的 base 目录的单个文件或目录(或绝对文件名),而 path attribue 接受以冒号或分号分隔的位置列表。path attribue 用于预定义路径——在任何其他情况下,应首选具有 location attribue 的多个元素。

自 Ant 1.8.2 开始,location attribue 还可以在其最后一个路径组件中包含一个通配符(即,它可以以 * 结尾),以支持 Java 6 引入的通配符类路径。Ant 不会解析或计算通配符,并且生成的路径可能只作为类路径,甚至作为 Java 6 之前的 JVM 的类路径。

元素支持自己的 pathlocation attribue,因此:


  

可缩写为:


此外,可以将一个或多个资源集合指定为嵌套元素(这些集合只能由文件类型资源组成)。此外,应该注意的是,尽管资源集合是按遇到的顺序处理的,但某些资源集合类型(如文件集、目录集和文件)在顺序上没有定义。


  
  
    
  
  
  
    
    
  
  

这将构建一个路径,包含 ${classpath} ,后面是 lib 目录中的所有 jar 文件,classes 目录, ${build.dir} 的 apps 子目录下(除了那些名称中包含“Test”文本的目录)所有名为 classes 的目录,以及在 FileList 中引用的指定文件。

如果要对多个 Task 使用相同的 path-like 结构,可以使用与 处于相同级别的 元素定义它们,并通过它们的 id attribue 引用它们。

默认情况下,一个 path-like 结构在使用时将重新评估所有嵌套的资源集合,这可能会导致不必要的文件系统重新扫描。自 Ant 1.8.0 开始,path 有一个可选的cache attribue,如果将其设置为 true,则 path 实例将只扫描其嵌套的资源集合一次,并假定它在构建期间不再更改(缓存的默认值仍然为 false)。即使仅在单个 Task 中使用 path,如果使用复杂的嵌套构造,将缓存设置为 true 也可能会提高总体性能。

path-like 结构可以通过嵌套的 元素包含对另一 path-like 结构(路径本身就是资源集合)的引用:


  
  
    
  
  



  
  

前面提到的 快捷方式也适用于 。例如:


  

可以写为:


Path Shortcut

自 Ant 1.6 以来,在 attribue 中将路径转换为操作系统特定的字符串有一个快捷方式。可以使用表达式 ${toString:pathreference} 元素引用转换为可用于路径参数的字符串。例如:


  


  

Command-line Arguments

有几个 Task 接收传递给命令行上的另一个进程的参数。为了更容易地指定包含空格字符的参数,可以使用嵌套的 元素。

Attribute Description Required
value 单个的命令行参数,可以包含空格字符。 valuefilepathpathrefline 等 attribue 选择一个使用
file 作为单个的命令行参数的文件名,将被替换为文件的绝对路径。 valuefilepathpathrefline 等 attribue 选择一个使用
path 将被当作 path-like 字符串的单个命令行参数,您可以使用冒号和分号作为分隔符,Ant 可以将其转换为符合本地环境规范的分隔符。 valuefilepathpathrefline 等 attribue 选择一个使用
pathref 对其他地方所定义的 path 的引用。Ant 将其转换为符合本地环境规范的路径。 valuefilepathpathrefline 等 attribue 选择一个使用
line 空格分割的命令行参数列表。 valuefilepathpathrefline 等 attribue 选择一个使用
prefix 放在参数前的固定字符串。在 line 被分成多个部分的场景中,将会添加到各部分的前面。自 Ant 1.8 开始引入该 attribue。 No
suffix 立即放在参数前的固定字符串。在 line 被分成多个部分的场景中,将会添加到各部分的后面。自 Ant 1.8 开始引入该 attribue。 No

强烈建议尽可能避免使用 line 版本。Ant 将尝试以类似于(Unix)shell 的方式拆分命令行,但在某些情况下可能会创建与您所期望的非常不同的内容。

示例


是包含空格字符的单个命令行参数,而不是单独的选项 -l-a


这是一个具有两个独立选项的命令行,-l-a


在 DOS 的系统上是值为 \dir;\dir2;\dir3 的单个命令行参数,在 Unix-like 的系统上是值为 /dir:/dir2:/dir3 的单个命令行参数。

References

任何元素都可以使用其 id attribute 分配一个标识符。在大多数情况下,可以通过在相同类型的元素上指定 refid attribue 来引用该元素。例如,如果您要使用 结构多次重复同一个 XML 片段,那么这可能非常有用。 下面是一个例子:


  
    
      
        
        
        
      
    
  

  
    
      
        
        
        
      
    
  

可以改写为:


  
    
    
    
  

  
    
      
    
  

  
    
      
    
  

所有对 PatternSet,FileSet, ZipFileSet 或 path-like 结构使用嵌套元素的 Task 都接受对这些结构的引用,如示例所示。在 Task 上使用 refid 通常具有相同的效果(引用已声明的任务),但是用户应该知道,该 attribue 的解释取决于指定该 attribue 的元素的实现。某些 Task(property Task 是一个方便的示例)故意为 refid 指定不同的含义。

Target

目标(Target)是 Task 和 DataType 的容器,它们在构建过程中协作以达到所需的状态。

Target 可以依赖于其他 Target,Apache Ant 确保在当前 Target 执行之前执行其依赖的其他 Target。例如,您可能有一个用于编译的 Target 和一个用于创建可发布文件的 Target。只有在先编译之后才能构建发布文件,因此创建发布文件的 Target 依赖于编译的 Target。

Ant 尝试按照 depends attribute 值中的其所依赖的 Target 出现的顺序(从左到右)依次执行它们。但是,当一个较早的 Target B 依赖于某个 Target A 时,则 Target A 可能会更早地执行:





假设我们要执行 Target D。根据其 depends 属性,您可能认为首先执行 Target C,然后执行 Target B,然后执行 Target A。然而,Target C 依赖于 Target B,Target B 依赖于 Target A,所以首先执行 Target A,然后执行 Target B,然后执行 Target C,最后执行 Target D。

Call-Graph:  A → B → C → D

在从给定 Target(如上面的 D)向后延伸的依赖链中,每个 Target只执行一次,即使有多个 Target依赖它。因此,执行 D 将首先导致 C 被调用,而 C 又将首先调用 B,B 又将首先调用 A。在依次执行 A、B、C 之后,执行又返回到了 D 的依赖列表(depends="C,B,A"),但将不会再调用该依赖列表中的 B 和 A,因为它们已经分别作为 D 的依赖关系在 C 和 B 的依赖关系解析过程中被调用。如果在处理 C 和 B 中没有发现这样的依赖关系,那么 B 和 A 将在处理 D 的依赖关系列表中的 C 之后执行。

Target 可以根据是否设置了某个 property 来选择是否执行该 Target。这样就可以根据系统的状态(Java 版本、OS、命令行属性定义等)更好地控制构建过程。要使 Target 感知到某个 property,应该该该 Target 添加名为 ifunless 的 attribute ,且该 attribute 的值即为该 property 的名称。注意:在最简单的情况下,Ant 只检查 property 是否已设置,值并不重要(Ant 1.7.1 及更早版本),但是使用 property 扩展(从 Ant 1.8.0 开始)可以构建更复杂的条件。详情请参考 Property 部分的介绍。例如:



在第一个 Target 示例中,如果 module-A-present 属性被设置(为任何值,例如 false)了,那么目标将运行。在第二个 Target 示例中,如果 module-A-present 属性被设置(同样,设置为任何值),那么目标将不会运行。

if/unless attribute 中只能指定一个属性名。如果要检查多个条件,可以使用某个依赖的 Target 来计算检查结果:


    Files foo.txt and bar.txt are present.



    
        
            
            
        
    

Call-Graph:  myTarget.check → maybe(myTarget)

如果不存在 ifunless attribute,则将始终执行 Target 。

重要提示: ifunless attribute 只启用或禁用它们所附加到的 Target 。它们不控制具备 ifunless attribute 的 Target 所依赖的其他 Target 是否得到执行。事实上,这些被依赖的 Target 在依赖它们的 Target 执行之前甚至都不会被解析。

可选的 description attribute 可用于为 Target 提供单行描述,该描述由 -projecthelp 命令行选项打印。除非使用 -verbose-debug 选项,否则没有此类描述的 Target 将被视为内部 Target ,并且不会被列出。

将 tstamp Task 放置在所谓的 initialization Target 中是一种很好的做法,所有其他 Target 都依赖于此。确保 Target 始终是其他 Target 依赖列表中的第一个。在本手册中,大多数初始化 Target 的名称为“init”。


    
        
    
    
        ...
    

特别是如果只有几个 Task ,还可以将这些 Task 直接放在 project 标签下(自 Ant 1.6.0 以来):


    

如果设置了 depends attribute 和 if/unless attribute,则首先执行 depends attribute。

Target 具有以下 attribute:

  • name:target 的名称。该 attribute 为必需,其他 attribute 为非必需;

  • depends:此 target 所依赖的 target 名称的逗号分隔列表;

  • if:为执行此 target 必须设置的 property 的名称,或计算为 true 的内容;

  • unless:为执行此目标而没有设置的 property 的名称,或计算为 false 的内容;

  • description:此 target 功能的简短描述;

  • extensionOf:将当前 target 添加到命名扩展点的依赖列表中。从 Ant1.8.0 开始;

  • onMissingExtensionPoint:如果此 target 尝试扩展丢失的扩展点(fail、warn、ignore)该怎么办。除非存在 extensionOf,否则不允许该 attribute,默认为 fail。从 Ant 1.8.2 开始。

Target 名称可以是 XML 文件编码中有效的任何字母数字字符串。空字符串在这个集合中,逗号和空格也是。请避免使用这些,因为它们在未来的 Ant 版本中将不受支持,因为它们会在命令行和 IDE 中造成混乱。IDE 对不寻常的 Target 名称或任何包含空格的 Target 名称的支持因 IDE 而异。

以连字符(如-restart)开头的 Target 名称是有效的,可以用于命名不应直接从命令行调用的 Target 。对于 Ant 的主类,每个以连字符开头的选项都是 Ant 本身的选项,而不是 Target 。因此,从命令行调用这些 Target 是不可能的。另一方面,IDE 通常不使用 Ant 的主类作为入口点,从 IDE 调用它们通常是可能的。

扩展点

从 Ant 1.8.0 开始,扩展点(extension-point)与 Target 类似,因为它们有一个名称和依赖列表,可以从命令行执行。就像 Target 一样,它们在构建过程中代表一种状态。与 Target 不同的是,扩展点不包含任何 Task ,它们的主要目的是在依赖列表中收集对所需状态有贡献的 Target (如下面的 xml 配置所示)。一个 Target 可以通过其 extensionOf attribute 将自己添加到扩展点的 depends attribute 列表中。如果扩展点的 depends 列表中有多个 Target ,则不定义其相对顺序。

扩展点的主要用途是使用 imported Task 来导入外部构建文件作为当前构建文件的扩展。在使用 import Task 导入的外部构建文件中,扩展点定义了必须达到的状态,并且来自其他构建文件的 Target 可以加入当前构建文件中扩展点的依赖列表,以便对该状态作出贡献。

例如,导入的外部构建文件可能需要编译代码,它可能如下所示:


   ...



   ...

Call-Graph:  create-directory-layout → 'empty slot' → compile

在编译之前需要生成一些源代码,然后在当前的主构建文件中可以使用:


   ...

Call-Graph:  create-directory-layout → generate-sources  → compile

这将确保在 compile Target 之前执行 generate-sources Target 。不要依赖于扩展点的 depends 列表中的 Target 名称顺序,如果 generate-sources 依赖于 create-directory-layout,那么它必须通过自己的 dependens attribute 显式依赖它。

Task

任务(Task )是一段可以执行的代码。一个 Task 可以有多个 attribute(或者参数)。attribute 的值可能包含对 property 的引用。这些引用将在 Task 执行之前解析。Task 有一个共同的结构:


其中 name 是 Task 的名称,每种 Task 都有其固定的名称,attributeN 和 valueN 是分别是某个 attribute 的名称和值。所有 Task 都可以有一个名为 name 的 attribute。这个 attribute 的值将在 Ant 生成的日志消息中使用。

可以为 Task 分配 id 属性:


其中,taskname 是 Task 的名称,taskID 是此 Task 的唯一标识符。可以通过此名称在脚本或其他 Task 中引用相应的 Task 对象。例如,在脚本中,可以执行以下操作:


设置此特定 Task 实例的 foo attribute。在另一个 Task (用 Java 编写)中,可以通过 project.getReference("task1") 访问该 Task 实例。

注意:如果 task1 还没有运行,那么它还没有被配置(比如,没有设置 attribute)。如果稍后要配置它,那么对实例所做的任何操作都可能被覆盖。

Ant 有一组内置的 Task ,也很容易编写自己的 Task 。

你可能感兴趣的:(Ant build .xml 文件结构介绍)