使用 Apache Ant

编写简单的构建文件

Apache Ant 的构建文件是用 XML 编写的。每个构建文件包含一个项目和至少一个(默认)目标。目标包含任务元素。构建文件的每个任务元素都可以有一个 id 属性,并且以后可以通过提供给此属性的值来引用。该值必须是唯一的。(有关更多信息,请参见下面的 任务 部分。)

项目

一个 项目 有三个属性

属性 描述 必需
name 项目的名称。
default 当没有提供目标时要使用的默认目标。 否;但是,从 Ant 1.6.0 开始,每个项目都包含一个隐式目标,其中包含所有顶级任务和/或类型。即使在使用 -projecthelp 选项运行 Ant 时,此目标也将始终作为项目初始化的一部分执行。
basedir 所有路径计算都以此为基础目录。相对于包含构建文件的目录解析相对路径。 否;默认为构建文件的父目录,除非被项目的 basedirbasedir 属性覆盖

可以选择提供项目的描述作为顶级 <description> 元素(参见 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 之外设置的。属性具有名称和值;名称区分大小写。属性可以在任务属性的值中或在支持它们的嵌套文本中使用。这是通过将属性名称放在构建文件中的 ${} 之间来完成的。例如,如果有一个名为 builddir 的属性,其值为 build,那么它可以在属性中使用,如下所示:${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}"/>

此外,可以将一个或多个 资源集合 指定为嵌套元素(这些元素必须仅包含 file 类型的资源)。此外,应该注意的是,尽管资源集合按遇到的顺序进行处理,但某些资源集合类型(例如 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> 相同的级别上使用 <path> 元素定义它们,并通过它们的 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 元素。

属性 描述 必需
value 单个命令行参数;可以包含空格字符。 这些中只有一个。
file 文件名称作为单个命令行参数;将被替换为文件的绝对文件名。
path 将被视为路径状字符串的字符串作为单个命令行参数;您可以使用 ;: 作为路径分隔符,Ant 将将其转换为平台的本地约定。
pathref 对在其他地方定义的路径的 引用。Ant 将将其转换为平台的本地约定。
line 以空格分隔的命令行参数列表。
prefix 要放在参数前面的固定字符串。对于分成多个部分的行,它将放在每个部分的前面。从 Ant 1.8 开始。
suffix 要放在参数后面的固定字符串。对于分成多个部分的行,它将放在每个部分的后面。从 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(类 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. 实现(并分享)您自己的