Ant基本使用

Hello world

1
2
3
4
5
6
package test.ant;
public class HelloWorld{
public static void main(String[] args){
System.out.println("Hello world1");
}
};

假设我们要将上的代码编译,移动,打包,允许。最直接的方法是手动用java, cp, jar, java完成。如果需要测试操作还需要对测试代码进行同样的操作。

当然这里的情况是很简单的,但是考虑到如果有成百上千个类,包,子项目,存在复杂的相互依赖,外部依赖等情况,同样多的测试类,以及复杂的部署过程。而且这样的过程在开发过程可能需要反复进行很多次。

手动完成几乎是不可能的任务,而且非常慢和不可靠。
通过Ant我们可以简洁优雅的完成这些复杂的任务,只需要一个或几个命令。

Ant的配置这里就不在细述,可以参考官网,需要注意的是在配置Ant时需要包装java环境已经配置好。

Ant是一个Apache基金会下的跨平台的基于Java语言开发的构件工具。
其构建文件默认为build.xml,默认放在项目的根目录下。

对于上面的HelloWorld任务,我们可以编写build.xml如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?xml version="1.0" encoding="UTF-8" ?>
<project name="HelloWorld" default="run" basedir=".">
<property name="src" value="src"/>
<property name="dest" value="classes"/>
<property name="hello_jar" value="hello1.jar"/>

<target name="init">
<mkdir dir="${dest}"/>
</target>

<target name="compile" depends="init">
<javac srcdir="${src}" destdir="${dest}"/>
</target>

<target name="build" depends="compile">
<jar jarfile="${hello_jar}" basedir="${dest}"/>
</target>

<target name="run" depends="build">
<java classname="test.ant.HelloWorld" classpath="${hello_jar}"/>
</target>

<target name="clean">
<delete dir="${dest}" />
<delete file="${hello_jar}" />
</target>

<target name="rerun" depends="clean,run">
<ant target="clean" />
<ant target="run" />
</target>

</project>
1
2
<project name="HelloWorld" default="run" basedir=".">
# 项目名字以及一些属性配置,例如项目目录,默认的target
1
2
3
<property name="src" value="src"/>
<property name="dest" value="classes"/>
<property name="hello_jar" value="hello1.jar"/>

属性任务(相当于变量),一般会用于后面的target中。

Ant的构建过程是由一个一个的target组成的,每个target有其名字和行为,以及所依赖的target。

属性 描述
目标名 (name) 表示目标的名称。(必须)
依赖 (depends) 用于描述 target 之间的依赖关系,若与多个 target 存在依赖关系时,需要以“,”间隔。Ant 会依照 depends 属性中 target 出现的顺序依次执行每个 target。被依赖的 target 会先执行。(可选)
描述 (description) 关于 target 功能的简单描述。(可选)
如果 (if) 用于验证指定的属性是否存在,若不存在,所在 target 将不会被执行。(可选)
除非 (unless) 该属性的功能与 if 属性的功能正好相反,它也用于验证指定的属性是否存在,若不存在,所在 target 将会被执行。(可选)
1
2
3
4
5
6
7
<target name="init">
<mkdir dir="${dest}"/>
</target>

<target name="compile" depends="init">
<javac srcdir="${src}" destdir="${dest}"/>
</target>

如果一个target有所依赖的target,则在执行这个target之前,会先需要完成所依赖的target的执行。也就是,target之间有一个依赖关系,这种关系用depends来指定。即如果Target A依赖于Target B,那么在执行Target A之前会首先执行Target B。

完成build.xml后(复杂的项目时一般不需要自己些,ant提供了一系列工具自动生成build.xml),我们只需要通过下面命令

1
ant

就可以完成项目的编译,打包,运行,只输入ant时,会执行到默认的target,默认target所依赖(递归)的target也会被全部执行。

如果只希望执行到某个阶段,只需要打包,可以使用ant target-name

例如如果只需要打包,可以使用:

1
ant build

More

子项目

一个比较复杂的项目通常包括多个子项目,并且各个子项目是由不同的开发人员来完成的。在这种情况下,不同的子项目应该拥有不同的build.xml,整个项目有一个汇总build.xml,通过Ant任务或是AntCall任务调用子项目的build.xml,如下例:

1
2
3
4
5
<target name="core" depends="init">
<ant dir="components" target="core"/>
<ant dir="waf/src" target="core"/>
<ant dir="apps" target="core"/>
</target>

在各个子项目的耦合不是非常紧密的情况下,各个子项目应该有各自独立的目录结构,也就是说它们可以有自己的src、doc、build、dist等目录及自己的build.xml文件,但是可以共享lib和bin目录。而对于那些耦合紧密的子项目,则推荐使用同一个src目录,但是不同的子项目有不同的子目录,各个子项目的build.xml文件可以放在根目录下,也可以移到各个子项目的目录下。

属性文件

当你只需要对小部分属性进行设置时,可以选择直接在构建文件中设置。然而,对于大项目,最好将设置属性的信息存储在一个独立的文件中。使用外部的Property文件可以保存一些预设置的公共属性变量。这些属性可以在多个不同的Build文件中使用。

存储属性信息在一个独立的文件中将会提供以下好处:

  • 它可以让您重复使用相同的构建文件,该文件在不同的执行环境中使用不同的属性设置。例如,构建属性文件在 DEV , TEST , 和 PROD 环境中可以独立地被维护。
  • 当你事先不知道属性的值时(例如,在一个实际的环境中),这样处理是有益的。这样允许你在知道属性值后,在其他环境中执行生成 (build) 操作。

一般情况下,属性文件都被命名为build.properties, 并且与 build.xml 存放在同一目录层。 你可以基于部署环境,比如: build.properties.dev 和 build.properties.test 创建多个 build.properties文件。

在下面的例子中展示了 build.xml 文件和与之相联系的build.properties文件:

build.xml

1
2
3
4
5
6
7
8
9
10
<?xml version="1.0"?>
<project name="Hello World Project" default="info">

<property file="build.properties"/>

<target name="info">
<echo>Apache Ant version is ${ant.version} - You are at ${sitename} </echo>
</target>

</project>

build.properties

1
2
3
# The Site Name
sitename=wiki.jikexueyuan.com
buildversion=3.3.2

注意到上面的练习中,sitename 是一个自定义属性,执行后映射到一个地址为 “wiki.jikexueyuan.com” 的网站上。你可以用这种方式声明任意数量的属性。在上面的例子中,还有一个自定义属性 buildversioin,它表明了当前构建的版本号。

外部XML文件导入

可以将一个外部的XML文件导入Build文件中,这样多个项目的开发者可以通过引用来共享一些代码,同样,这也有助于Build文件的重用,示例代码如下所示:

1
2
3
4
5
6
7
8
<!DOCTYPE project [
<!ENTITY share-variable SYSTEM "file:../share-variable.xml">
<!ENTITY build-share SYSTEM "file:../build-share.xml">
]>
<project name="main" default="complie" basedir=".">
&share-variable;
&build-share;
... ...

数据类型

Ant 提供一些预定义的数据类型。不要将术语“数据类型”和那些在编程语言中可用的数据类型相混淆,而是将他们视作一组已经在产品中配置好的服务。

下述的数据类型是由 Apache Ant 提供的。

文件集

文件集的数据类型代表了一个文件集合。它被当作一个过滤器,用来包括或移除匹配某种模式的文件。

例如,参考下面的代码。这里,src 属性指向项目的源文件夹。

文件集选择源文件夹中所有的 .java 文件,除了那些包含有 ‘Stub’ 单词的文件。能区分大小写的过滤器被应用到文件集上,这意味着名为 Samplestub.java 的文件将不会被排除在文件集之外。

1
2
3
4
<fileset dir="${src}" casesensitive="yes">
<include name="**/*.java"/>
<exclude name="**/*Stub*"/>
</fileset>

模式集合

一个模式集合指的是一种模式,基于这种模式,能够很容易地过滤文件或者文件夹。模式可以使用下述的元字符进行创建。

  • ? -仅匹配一个字符
    • -匹配零个或者多个字符
  • ** -递归地匹配零个或者多个目录

下面的例子演示了模式集合的使用。

1
2
3
4
<patternset id="java.files.without.stubs">
<include name="src/**/*.java"/>
<exclude name="src/**/*Stub*"/>
</patternset>

该模式集合能够通过一个类似于下述的文件集进行重用:

1
2
3
<fileset dir="${src}" casesensitive="yes">
<patternset refid="java.files.without.stubs"/>
</fileset>

文件列表

文件列表数据类型与文件集相类似,除了以下几处不同:

文件列表包含明确命名的文件的列表,同时其不支持通配符。
文件列表数据类型能够被应用于现有的或者还不存在的文件中。
让我们来看一个下述的关于文件列表数据类型的例子。在这个例子中,属性 webapp.src.folder 指向该项目中的 Web 应用的源文件夹。

1
2
3
4
5
6
<filelist id="config.files" dir="${webapp.src.folder}">
<file name="applicationConfig.xml"/>
<file name="faces-config.xml"/>
<file name="web.xml"/>
<file name="portlet.xml"/>
</filelist>

过滤器集合

使用一个过滤器集合数据类型与拷贝任务,你可以在所有文件中使用一个替换值来替换掉一些与模式相匹配的文本。

一个常见的例子就是对一个已经发行的说明文件追加版本号,代码如下:

1
2
3
4
5
6
<copy todir="${output.dir}">
<fileset dir="${releasenotes.dir}" includes="**/*.txt"/>
<filterset>
<filter token="VERSION" value="${current.version}"/>
</filterset>
</copy>

在这段代码中:

  • 属性 output.dir 指向项目的输出文件夹。
  • 属性 releasenotes.dir 指向项目的发行说明文件夹。
  • 属性 current.version 指向项目的当前版本文件夹。
  • 拷贝任务,顾名思义,是用来将文件从一个地址拷贝到另一个地址。

路径

path数据类型通常被用来表示一个类路径。各个路径之间用分号或者冒号隔开。然而,这些字符在运行时被替代为执行系统的路径分隔符。

类路径被设置为项目中 jar 文件和类文件的列表,如下面例子所示:

1
2
3
4
5
6
<path id="build.classpath.jar">
<pathelement path="${env.J2EE_HOME}/${j2ee.jar}"/>
<fileset dir="lib">
<include name="**/*.jar"/>
</fileset>
</path>

在这段代码中:

  • 属性 env.J2EE_HOME 指向环境变量 J2EE_HOME 。
  • 属性 j2ee.jar 指向在 J2EE 基础文件夹下面的名为 J2EE jar 的文件。

Example project

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
C:\work\FaxWebApplication>tree
Folder PATH listing
Volume serial number is 00740061 EC1C:ADB1
C:.
+---db
+---src
. +---faxapp
. +---dao
. +---entity
. +---util
. +---web
+---war
+---images
+---js
+---META-INF
+---styles
+---WEB-INF
+---classes
+---jsp
+---lib

下面给出上述项目的 build.xml 文件的内容。让我们来一条语句接一条语句地来分析它。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<?xml version="1.0"?>
<project name="fax" basedir="." default="build">
<property name="src.dir" value="src"/>
<property name="web.dir" value="war"/>
<property name="build.dir" value="${web.dir}/WEB-INF/classes"/>
<property name="name" value="fax"/>

<path id="master-classpath">
<fileset dir="${web.dir}/WEB-INF/lib">
<include name="*.jar"/>
</fileset>
<pathelement path="${build.dir}"/>
</path>

<target name="build" description="Compile source tree java files">
<mkdir dir="${build.dir}"/>
<javac destdir="${build.dir}" source="1.5" target="1.5">
<src path="${src.dir}"/>
<classpath refid="master-classpath"/>
</javac>
</target>

<target name="clean" description="Clean output directories">
<delete>
<fileset dir="${build.dir}">
<include name="**/*.class"/>
</fileset>
</delete>
</target>

<target name="build-jar">
<jar destfile="${web.dir}/lib/util.jar"
basedir="${build.dir}/classes"
includes="faxapp/util/**"
excludes="**/Test.class">

<manifest>
<attribute name="Main-Class" value="com.tutorialspoint.util.FaxUtil"/>
</manifest>

</jar>
</target>
</project>

首先,让我们来声明一些源文件,web 文件和构建文件的一些属性信息。

1
2
3
<property name="src.dir" value="src"/>
<property name="web.dir" value="war"/>
<property name="build.dir" value="${web.dir}/WEB-INF/classes"/>

在上面的例子中:

src.dir 表示这个项目的源文件目录,也就是存储 java 文件的地方。
web.dir 表示这个项目的 web 文件目录,也就是存储 JSPs 文件,web.xml,css,javascript 以及其它与 web 相关的文件的地方。
build.dir 表示该项目的输出文件。
属性也可以引用其它属性。在上面的例子中,build.dir 属性引用了 web.dir 属性。

在上面的例子中,src.dir 就是项目源文件存放的地方。

我们项目的默认目标是编译目标。但是首先让我们来看一下 clean 目标。

clean 目标,就像它的名字所表明的意思一样,删除构建文件夹中的所有文件。

1
2
3
4
5
6
7
<target name="clean" description="Clean output directories">
<delete>
<fileset dir="${build.dir}">
<include name="**/*.class"/>
</fileset>
</delete>
</target>

控制类路径 (master-classpath) 保存类路径的相关信息。在这种情况下,它包含了构建文件夹和 jar 文件夹中的所有的类文件。

1
2
3
4
5
6
<path id="master-classpath">
<fileset dir="${web.dir}/WEB-INF/lib">
<include name="*.jar"/>
</fileset>
<pathelement path="${build.dir}"/>
</path>

最后,构建目标构建这些文件。首先,我们创建一个构建目录,如果该目录不存在,我们就执行 javac 命令(具体以 jdk 1.5 作为我们目标的编译环境)。 我们对 javac 任务提供源文件夹和类路径,并且通过执行 javac 任务将类文件存放在构建文件夹中。

1
2
3
4
5
6
7
8
<target name="build" description="Compile main source tree java files">
<mkdir dir="${build.dir}"/>
<javac destdir="${build.dir}" source="1.5" target="1.5" debug="true"
deprecation="false" optimize="false" failonerror="true">
<src path="${src.dir}"/>
<classpath refid="master-classpath"/>
</javac>
</target>

在这个文件上执行 Ant,编译 java 源文件,并将编译后的类文件存放在构建文件夹的地方。

运行 Ant 文件后,能看到以下输出:

1
2
BUILD SUCCESSFUL
Total time: 6.3 seconds

文件被编译后,将存储在 build.dir 文件夹中。

JUnit集成

参考Apache Ant教程, Ant教程