第1章 Ant的安装和配置
本章实际开始实际接触Ant。本章首先将介绍如何在主流的操作系统下安装Ant,并详细解释Ant的安装文件;其次还会介绍如何在主流的IDE中集成Ant,以及Ant安装的最佳实践。
1.1 检查JDK安装
在安装Ant之前,首先要确认你已经正确安装了JDK。Ant可以运行在JDK 1.4及以上的版本上。本书的所有样例都基于JDK 5及以上版本。打开Windows的命令行,运行如下的命令来检查你的Java安装:
C:\>echo %JAVA_HOME%
C:\ >java -version
结果如图1-1所示:
C:\>echo %JAVA_HOME%
D:\Program Files\Java\jdk1.6.0_20
C:\>java -version
java version "1.6.0_20"
Java(TM) SE Runtime Environment (build 1.6.0_20-b02)
Java HotSpot(TM) Client VM (build 16.3-b01, mixed mode, sharing)
图1-1 Windows中检查Java安装
1.2 下载Ant
请访问Ant的下载页面:http://ant.apache.org/bindownload.cgi,其中包含针对不同平台的各种版本的Ant下载文件。
在本博文编写的时候,Ant 1.8的最新版本是1.8.2。
1.3 本地安装
将安装文件解压到你指定的目录中,这里的Ant安装目录是D:\Program Files\apache-ant-1.8.2,接着需要设置环境变量,将Ant安装配置到操作系统环境中。
打开系统属性面板(桌面上右键单击“我的电脑”→“属性”),点击高级系统设置,再点击环境变量,在系统变量中新建一个变量,变量名为ANT_HOME,变量值为Ant的安装目录D:\Program Files\apache-ant-1.8.2。点击确定,接着在系统变量中找到一个名为Path的变量,在变量值的末尾加上%ANT_HOME%\bin;,注意多个值之间需要有分号隔开,然后点击确定。至此,环境变量设置完成。
这里需要提一下的是Path环境变量,当我们在cmd中输入命令时,Windows首先会在当前目录中寻找可执行文件或脚本,如果没有找到,Windows会接着遍历环境变量Path中定义的路径。由于我们将%M2_HOME%\bin添加到了Path中,而这里%ANT_HOME%实际上是引用了我们前面定义的另一个变量,其值是Ant的安装目录。因此,Windows会在执行命令时搜索目录D:\Program Files\apache-ant-1.8.2\bin,而ant执行脚本的位置就是这里。
明白了环境变量的作用,现在打开一个新的cmd窗口(这里强调新的窗口是因为新的环境变量配置需要新的cmd窗口才能生效),运行如下命令检查Maven的安装情况:
C:\>echo %ANT_HOME%
C:\>ant -v
运行结果如图1-3所示:
C:\>echo %ANT_HOME%
D:\Program Files\apache-ant-1.8.2
C:\>ant -version
Apache Ant(TM) version 1.8.2 compiled on December 20 2010
图1-3 Windows中检查Ant安装
第一条命令echo %ANT_HOME%用来检查环境变量ANT_HOME是否指向了正确的ANT安装目录;而ant –version执行了第一条Ant命令,以检查Windows是否能够找到正确的ant执行脚本。
1.4 升级Ant
Ant还比较年轻,更新比较频繁,因此用户往往会需要更新Ant安装以获得更多更酷的新特性,以及避免一些旧的bug。
在Windows上更新Ant非常简便,只需要下载新的Ant安装文件,解压至本地目录,然后更新ANT_HOME环境变量便可。例如,假设ANT推出了新版本1.8.3,我们将其下载然后解压至目录D:\Program Files\apache-ant-1.8.3,接着遵照前一节描述的步骤编辑环境变量ANT_HOME,更改其值为D:\Program Files\apache-ant-1.8.2。至此,更新就完成了。同理,如果你需要使用某一个旧版本的Ant,也只需要编辑ANT_HOME环境变量指向旧版本的安装目录。
第2章 Ant使用入门
到目前为止,我们已经大概了解并安装好了Ant,现在,我们开始创建一个最简单的Hello World项目。如果你是初次接触Ant,我建议你按照本章的内容一步步地编写代码并执行,可能你会碰到一些概念暂时难以理解,不用着急,记下这些疑难点,相信后续章节会帮你逐一解答。
2.1 编写build
Ant项目的核心是build.xml。build定义了项目的基本信息,用于描述项目如何构建,声明项目依赖,等等。现在我们先为Hello World项目编写一个最简单的build.xml。
首先创建一个名为ant-jar的文件夹,打开该文件夹,新建一个名为build.xml的文件,输入其内容如代码清单3-1:
代码清单2-1:Hello World的build.xml
<?xml version="1.0" encoding="UTF-8"?>
<project name="ant-jar" basedir="." default="main">
<property name="resources.dir" value="src/main/resources" />
<property name="src.dir" value="src/main/java" />
<property name="test.dir" value="src/test/java" />
<property name="build.dir" value="build" />
<property name="classes.dir" value="${build.dir}/classes" />
<property name="test-classes.dir" value="${build.dir}/test-classes" />
<property name="jar.dir" value="${build.dir}/jar" />
<property name="report.dir" value="${build.dir}/report" />
<property name="correctreport.dir" value="${report.dir}/html" />
<property name="main-class" value="net.csdn.common.Luncher" />
<property name="lib.dir" value="lib" />
<path id="classpath">
<fileset dir="${lib.dir}" includes="**/*.jar" />
</path>
<target name="clean">
<delete dir="${build.dir}" />
</target>
<target name="compile" depends="clean">
<mkdir dir="${classes.dir}" />
<mkdir dir="${test-classes.dir}" />
<javac srcdir="${src.dir}" destdir="${classes.dir}" classpathref="classpath" includeantruntime="true" />
<javac srcdir="${src.dir}" destdir="${test-classes.dir}" classpathref="classpath" includeantruntime="true" />
<javac srcdir="${test.dir}" destdir="${test-classes.dir}" classpathref="classpath" includeantruntime="true" />
<copy todir="${classes.dir}">
<fileset dir="${resources.dir}" />
</copy>
<copy todir="${test-classes.dir}">
<fileset dir="${resources.dir}" />
</copy>
</target>
<target name="test" depends="compile">
<mkdir dir="${report.dir}" />
<mkdir dir="${correctreport.dir}" />
<junit printsummary="yes">
<classpath>
<path refid="classpath" />
<pathelement path="${test-classes.dir}" />
</classpath>
<formatter type="xml" />
<batchtest fork="yes" todir="${report.dir}">
<fileset dir="${test.dir}">
<include name="**/*Test.java" />
</fileset>
</batchtest>
</junit>
<junitreport todir="${report.dir}">
<fileset dir="${report.dir}">
<include name="TEST-*.xml" />
</fileset>
<report format="frames" todir="${correctreport.dir}" />
</junitreport>
</target>
<target name="jar" depends="compile">
<mkdir dir="${jar.dir}" />
<jar destfile="${jar.dir}/${ant.project.name}.jar" basedir="${classes.dir}">
<manifest>
<attribute name="Main-Class" value="${main-class}" />
</manifest>
<zipfileset src= "${lib.dir}/log4j-1.2.15.jar" />
</jar>
</target>
<target name="run" depends="jar">
<java fork="true" classname="${main-class}">
<classpath>
<path refid="classpath" />
<path id="application" location="${jar.dir}/${ant.project.name}.jar" />
</classpath>
</java>
</target>
<target name="main" depends="test,run">
<echo message="Ant Build Successful" />
</target>
</project>
图 2-1
2.2 编写主代码
项目主代码和测试代码不同,项目的主代码会被打包到最终的构件中(比如jar),而测试代码只在运行测试时用到,不会被打包。假设Ant项目主代码位于src/main/java目录,创建该目录,然后在该目录下创建文件net/csdn/common/Luncher.java,其内容如代码清单2-2:
代码清单2-2:Hello World的主代码
package net.csdn.common;
import org.apache.log4j.Logger;
public class Luncher {
private static Logger logger = Logger.getLogger(Luncher.class);
public Luncher() {
if (logger.isInfoEnabled())
logger.info("net.csdn.common.Luncher#Luncher");
}
public static void main(String[] args) {
if (logger.isInfoEnabled())
logger.info("net.csdn.common.Luncher#main");
System.out.println(call());
}
public static String call() {
if (logger.isInfoEnabled())
logger.info("net.csdn.common.Luncher#call");
return "Hello Ant";
}
}
图 2-2
这是一个简单的Java类,它有一个call()方法,返回一个String。同时这个类还带有一个main方法,创调用call()方法,并将结果输出到控制台。
代码编写完毕后,我们使用Ant进行编译,在项目根目录下运行命令 ant compile,我们会得到如下输出:
D:\ant-jar>ant compile
Buildfile: D:\ant-jar\build.xml
clean:
[delete] Deleting directory D:\ant-jar\build
compile:
[mkdir] Created dir: D:\ant-jar\build\classes
[mkdir] Created dir: D:\ant-jar\build\test-classes
[javac] Compiling 1 source file to D:\ant-jar\build\classes
[javac] Compiling 1 source file to D:\ant-jar\build\test-classes
[javac] Compiling 1 source file to D:\ant-jar\build\test-classes
[copy] Copying 1 file to D:\ant-jar\build\classes
[copy] Copying 1 file to D:\ant-jar\build\test-classes
BUILD SUCCESSFUL
Total time: 1 second
clean告诉Ant清理输出目录build/,compile告诉Ant编译项目主代码,从输出中我们看到Ant首先执行了clean任务,删除build/目录,Ant构建的所有输出都在 build/目录中;接着执行compile任务,将项目主代码编译至build/classes目录(编译好的类为net/csdn/common/Luncher.Class)。
至此,Ant在没有任何额外的配置的情况下就执行了项目的清理和编译任务,接下来,我们编写一些单元测试代码并让Ant执行自动化测试。
2.3 编写测试代码
为了使项目结构保持清晰,主代码与测试代码应该分别位于独立的目录中。Ant项目中测试代码目录是src/test/java。因此,在编写测试用例之前,我们先创建该目录。
在Java世界中,由Kent Beck和Erich Gamma建立的JUnit是事实上的单元测试标准。要使用JUnit,我们首先需要为Hello World项目添加一个JUnit依赖。
代码清单2-3:为Hello World的测试代码如下
......
<target name="test" depends="compile">
<mkdir dir="${report.dir}" />
<mkdir dir="${correctreport.dir}" />
<junit printsummary="yes">
<classpath>
<path refid="classpath" />
<pathelement path="${test-classes.dir}" />
</classpath>
<formatter type="xml" />
<batchtest fork="yes" todir="${report.dir}">
<fileset dir="${test.dir}">
<include name="**/*Test.java" />
</fileset>
</batchtest>
</junit>
<junitreport todir="${report.dir}">
<fileset dir="${report.dir}">
<include name="TEST-*.xml" />
</fileset>
<report format="frames" todir="${correctreport.dir}" />
</junitreport>
</target>
......
图 2-3
配置了测试依赖,接着就可以编写测试类,回顾一下前面的Luncher类,现在我们要测试该类的call()方法,检查其返回值是否为“Hello Ant”。在src/test/java目录下创建文件,其内容如代码清单3-4,Hello World的测试代码
package net.csdn.common;
import junit.framework.Assert;
import org.apache.log4j.Logger;
import org.junit.Test;
public class LuncherTest {
private static Logger logger = Logger.getLogger(LuncherTest.class);
@Test
public void testCall() {
if (logger.isInfoEnabled())
logger.info("net.csdn.common.LuncherTest#testCall");
Assert.assertEquals("Hello Ant", Luncher.call());
}
}
图 3-4
Java代码
一个典型的单元测试包含三个步骤:一,准备测试类及数据;二,执行要测试的行为;三,检查结果。上述样例中,我们首先初始化了一个要测试的HelloWorld实例,接着执行该实例的call()方法并保存结果到result变量中,最后使用JUnit框架的Assert类检查结果是否为我们期望的”Hello Ant”。在JUnit 3中,约定所有需要执行测试的方法都以test开头,这里我们使用了JUnit 4,但我们仍然遵循这一约定,在JUnit 4中,需要执行的测试方法都应该以@Test进行标注。
测试用例编写完毕之后就可以调用Ant执行测试,运行 ant test
D:\ant-jar>ant test
Buildfile: D:\ant-jar\build.xml
clean:
[delete] Deleting directory D:\ant-jar\build
compile:
[mkdir] Created dir: D:\ant-jar\build\classes
[mkdir] Created dir: D:\ant-jar\build\test-classes
[javac] Compiling 1 source file to D:\ant-jar\build\classes
[javac] Compiling 1 source file to D:\ant-jar\build\test-classes
[javac] Compiling 1 source file to D:\ant-jar\build\test-classes
[copy] Copying 1 file to D:\ant-jar\build\classes
[copy] Copying 1 file to D:\ant-jar\build\test-classes
test:
[mkdir] Created dir: D:\ant-jar\build\report
[mkdir] Created dir: D:\ant-jar\build\report\html
[junit] Running net.csdn.common.LuncherTest
[junit] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 0.078 sec
[junitreport] Processing D:\ant-jar\build\report\TESTS-TestSuites.xml to C:\DOCU
ME~1\ADMINI~1\LOCALS~1\Temp\null1722353399
[junitreport] Loading stylesheet jar:file:/D:/Program%20Files/apache-ant-1.8.2/l
ib/ant-junit.jar!/org/apache/tools/ant/taskdefs/optional/junit/xsl/junit-frames.
xsl
[junitreport] Transform time: 422ms
[junitreport] Deleting: C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\null1722353399
BUILD SUCCESSFUL
Total time: 2 seconds
我们看到test任务执行成功了,测试代码通过编译之后在build/test-classes下生成了二进制文件,紧接着test任务运行测试,这里它运行测试用例LuncherTest,并且输出测试报告,显示一共运行了多少测试,失败了多少,出错了多少,跳过了多少。显然,我们的测试通过了——BUILD SUCCESSFUL。
2.4 打包和运行
将项目进行编译、测试之后,下一个重要步骤就是打包(package)。Hello World的Ant指定打包类型为jar,我们可以简单地执行命令 ant jar 进行打包,可以看到如下输出:
D:\ant-jar>ant jar
Buildfile: D:\ant-jar\build.xml
clean:
[delete] Deleting directory D:\ant-jar\build
compile:
[mkdir] Created dir: D:\ant-jar\build\classes
[mkdir] Created dir: D:\ant-jar\build\test-classes
[javac] Compiling 1 source file to D:\ant-jar\build\classes
[javac] Compiling 1 source file to D:\ant-jar\build\test-classes
[javac] Compiling 1 source file to D:\ant-jar\build\test-classes
[copy] Copying 1 file to D:\ant-jar\build\classes
[copy] Copying 1 file to D:\ant-jar\build\test-classes
jar:
[mkdir] Created dir: D:\ant-jar\build\jar
[jar] Building jar: D:\ant-jar\build\jar\ant-jar.jar
BUILD SUCCESSFUL
Total time: 1 second
类似地,Ant会在打包之前执行编译、测试等操作。这里我们看到ant jar任务负责打包,实际上就是jar插件的jar目标将项目主代码打包成一个名为ant-jar.jar的文件,该文件也位于build/输出目录中。
至此,我们得到了项目的输出,如果有需要的话,就可以复制这个jar文件到其他项目的Classpath中从而使用HelloWorld类。
到目前为止,我们还没有运行Hello World项目,不要忘了HelloWorld类可是有一个main方法的。默认打包生成的jar是不能够直接运行的,因为带有main方法的类信息不会添加到manifest中(我们可以打开jar文件中的META-INF/MANIFEST.MF文件,将无法看到Main-Class一行)。为了生成可执行的jar文件,我们需要配置如下:
......
<target name="jar" depends="compile">
<mkdir dir="${jar.dir}" />
<jar destfile="${jar.dir}/${ant.project.name}.jar" basedir="${classes.dir}">
<manifest>
<attribute name="Main-Class" value="${main-class}" />
</manifest>
<zipfileset src= "${lib.dir}/log4j-1.2.16.jar" />
</jar>
</target>
......
我们可以看到ant-jar.jar,打开ant-jar.jar的META-INF/MANIFEST.MF,可以看到它包含这样一行信息:
Main-Class: net.csdn.common.Luncher
现在,我们在项目根目录中执行该jar文件:
D: \ant-jar\build\jar\java -jar ant-jar.jar
Hello Ant
控制台输出为Hello Ant,这正是我们所期望的。
本小节介绍了Hello World项目,侧重点是Ant而非Java代码本身,介绍了build、Ant项目结构、以及如何编译、测试、打包,等等。D:\ant-jar\
│ build.xml
│
├─lib
│ log4j-1.2.16.jar
│
└─src
├─test
│ ├─java
│ │ └─net
│ │ └─csdn
│ │ └─common
│ │ LuncherTest.java
│ │
│ └─resources
│ log4j.properties
│
└─main
├─resources
│ log4j.properties
│
└─java
└─net
└─csdn
└─common
Luncher.java