转自:http://www.iteye.com/topic/78973
Ant是一款类似make的工具,用来编译/运行/打包Java程序。在构建、包装和发布过程中几乎每一件事都可以由Ant的任务来处理。
Ant的buildfile是用XML写的。编写build.xml时,下面是一个基本的模板。一个构建文件主要由三部分组成:Project、Target、Task。
<project name="" basedir="" default="">
<property />
<taskdef name="" classpathref="" resource="" />
<target name="all" depends="">
<copy />
<javac />
<jar />
<exec />
<customTask />
</target>
</project>
1、project
每个build.xml有且只能存在一个project。project的基本属性:
name:项目名称
default:缺省开始执行的target
basedir:用于计算所有其他路径的基路径。该属性可以被basedir property覆盖。当覆盖时,该属性被忽略。如果属性和basedir property都没有设定,就使用buildfile文件的父
目录。
2、target
一个项目可以定义一个或多个target。
一个target是一系列你想要执行的任务,如编译任务、打包任务、混淆任务。
Ant在执行时以target为单位,执行顺序是从下至上,依次执行;如果某个Target有depends属性,那么就先顺序执行depends属性中的被依赖的target,然后再执行该target。
一个target只能被执行一次,即使有多个target依赖于它。
target的基本属性:
name:target的名字
depends:该target依赖关系。用逗号分隔的target的名字列表,也就是依赖表。Ant在执行时以target为单位,target的depends属性又决定了哪个Target先执行,因此我们可以
通过Target来构造编译顺序。;如果被依赖的target无法运行,这种depends对于指定了依赖关系的target就没有影响。Ant会依照depends属性中target出现的顺序(从左到
右)依次执行每个target。然而,要记住的是只要某个target依赖于一个target,后者就会被先执行。
举例:
<target name="D" depends="C,B,A"/>
<target name="A"/>
<target name="B" depends="A"/>
<target name="C" depends="B"/>
<target name="E"/>
假定我们要执行target D。从它的依赖属性来看,你可能认为先执行C,然后B,最后A被执行。错了,C依赖于B,B依赖于A,所以先执行A,然后B,然后C,最后D被执行。
3、Task
Ant执行的一系列任务是由target构成的,而target又是由数个小的task构成的,task任务是最小的运行单位。
task主要有两类:Ant内置的task类和自定义task。
3.1. Ant内置的task类
(1)Javac
编译Java源代码,样例:
<javac
srcdir="{src}:{src2}"
destdir="{build}"
includes="mypackage/p1/**,mypackage/p2/**"
excludes="mypackage/p1/testpackage/**"
classpath="xyz.jar"
debug="on"/>
解释:
编译{src}和{src2}目录及其子目录下的所有Java文件;多个目录时用":"分割。
但是package/p1/**,mypackage/p2/**将被编译,而mypackage/p1/testpackage/**将不会被编译。
Class文件将放在{build}指定的目录下
classpath表示需要用到的类文件或者目录,仅指我们定义的类库;而bootclasspath参数(启动类库):它已经包含了jre/lib目录下的rt.jar,以及我们自定义的类库。
debug设置为on表示输出debug信息
(2)Java
执行指定的Java类
A.运行一个类
<java classname="${main}" classpath="${classpath}"/>
解释:classname中指定要执行的类,classpath设定要使用的环境变量;
B.运行某一特定类,并加上运行参数
<java fork="true" classname="proguard.ProGuard" classpath="${proguard.classpath}">
<arg line="-libraryjars ${proguard.classpath}"/>
<arg line="-injars temp/${app.project}_tmp.jar"/>
<arg line="-outjar temp/${app.project}_obf.jar"/>
<arg line="-defaultpackage ''"/>
<arg line="-dontusemixedcaseclassnames"/>
<arg line="-keep public class ${app.midlet}"/>
</java>
解释:fork参数:为true时,在新的JVM实例中运行,不影响当前JVM工作。
(3)Jar
将编译好的CLASS文件编译成jar包。样例:
<jar destfile="{dist}/lib/app.jar"
basedir="{build}/classes"
includes="mypackage/test/**"
excludes="**/Test.class"
manifest="my.mf"
/>
解释:将{build}/classes下面的所有文件打包到{dist}/lib/app.jar中,但是包括mypackage/test/所有文件不包括所有的Test.class;manifest属性指定自己的META-INF/MANIFEST.MF文件,而不是由系统生成。
(4)exec
用来调用外部程序
<exec executable="${LIB_PATH}/preverify.exe">
<arg line="-classpath ${compile.classpath} -d temp/build temp/obfuscate"/>
</exec>
(5)File(Directory)类
A、mkdir
创建一个目录,如果他的父目录不存在,也会被同时创建。
例子:
<mkdir dir="build/classes"/>
解释:如果build不存在,也会被同时创建
B、copy
拷贝一个(组)文件、目录
例子:
b1.拷贝单个的文件:
<copy file="myfile.txt" tofile="mycopy.txt"/>
b2.拷贝单个的文件到指定目录下
<copy file="myfile.txt" todir="../some/other/dir"/>
b3.拷贝一个目录到另外一个目录下
<copy todir="../new/dir">
<fileset dir="src_dir"/>
</copy>
b4.拷贝一批文件到指定目录下
<copy todir="../dest/dir">
<fileset dir="src_dir">
<include name="**/*.java"/>
<iexclude name="**/Test.java"/>
</fileset>
</copy>
<copy todir="../dest/dir">
<fileset dir="src_dir" excludes="**/*.java"/>
</copy>
b5.拷贝一批文件到指定目录下,将文件名后增加.bak后缀
<copy todir="../backup/dir">
<fileset dir="src_dir"/>
<mapper type="glob" from="*" to="*.bak"/>
</copy>
b6.替换拷贝
<copy todir="temp/build">
<fileset dir="temp/classes" includes="*.class" />
<filterset>
<filter token="@Time@" value="${app.time}"/>
</filterset>
</copy>
解释:<filterset>过滤集,可以将temp/classes文件夹下的存在@Time@标记的文件,替换为变量${app.time}值。这样在完成拷贝的同时也完成了替换任务。
删除一个(组)文件或者目录
例子:
1.删除一个文件
<delete file="/lib/ant.jar"/>
2.删除指定目录及其子目录
<delete dir="lib"/>
3.删除指定的一组文件
<delete>
<fileset dir="." includes="**/*.bak"/>
</delete>
4.删除指定目录及其子目录,包括他自己
<delete includeEmptyDirs="true">
<fileset dir="build"/>
</delete>
D、move
移动或重命名一个(组)文件、目录
例子:
1.移动或重命名一个文件
<move file="file.orig" tofile="file.moved"/>
2.移动或重命名一个文件到另一个文件夹下面
<move file="file.orig" todir="dir/to/move/to"/>
3.将一个目录移到另外一个目录下
<move todir="new/dir/to/move/to">
<fileset dir="src/dir"/>
</move>
4.将一组文件移动到另外的目录下
<move todir="some/new/dir">
<fileset dir="my/src/dir">
<include name="**/*.jar"/>
<exclude name="**/ant.jar"/>
</fileset>
</move>
5.移动文件过程中增加.bak后缀
<move todir="my/src/dir">
<fileset dir="my/src/dir">
<exclude name="**/*.bak"/>
</fileset>
<mapper type="glob" from="*" to="*.bak"/>
</move>
自定义task必须保证该类是从Task类继承过来的。在脚本中使用标签为<taskdef> </taskdef>,样例及用法如下:
<taskdef name="filesorter" classname="com.duomi.ant.FileSorter" classpath="${duomi.home}/ant/pkgutil.jar" />
<target name="main">
<filesorter file="input.txt" tofile="output.txt" />
</target>
在打包脚本pkgutil.jar中,FileSorter类的源码实现如下:
public class FileSorter extends Task {
private File file;
private File tofile;
// ant在进行任务处理时会调用execute()方法
public void execute() throws BuildException {
System.out.println("Sorting file=" + file);
try {
BufferedReader from = new BufferedReader(new FileReader(file));
BufferedWriter to = new BufferedWriter(new FileWriter(tofile));
List allLines = new ArrayList();
// read in the input file
String line = from.readLine();
while (line != null) {
allLines.add(line);
line = from.readLine();
}
from.close();
// sort the list
Collections.sort(allLines);
// write out the sorted list
for (ListIterator i = allLines.listIterator(); i.hasNext();) {
String s = (String) i.next();
to.write(s);
to.newLine();
}
to.close();
} catch (FileNotFoundException e) {
throw new BuildException(e);
} catch (IOException e) {
throw new BuildException(e);
}
}
// file参数
public void setFile(File file) {
this.file = file;
}
// tofile参数
public void setTofile(File tofile) {
this.tofile = tofile;
}
}
这个task类FileSorter,在使用时需要设置源文件和输出文件两个参数。Ant通过setter方法将build.xml中用到的属性名与task类中的实现方法相关联,方法就是:自定义task类中的方法名称命名规则为"set"+属性名。属性名是作为字符串来指定的,因此我们的 setter 方法的参数可以是一个字符串。在这样的情况下,Ant 将在展开值所引用的任何属性之后,使用该属性的字符串值来调用我们的方法。但有时我们想把属性的值看作是一种不同的类型。上述示例任务就是这种情况,其中的属性值引用文件系统上的文件,而不只是引用任意的字符串。可以通过将方法参数声明为 java.io.File 类型来容易地做到这点。Ant 将接受属性的字符串值,并把它解释为一个文件,然后传递给我们的方法。
Ant 能够对其他类型执行类似的转换,比如 boolean 和 int 类型。但如果提供具有相同名称但是具有不同参数的两个方法,Ant 将使用更明确的那一个方法,因此文件类型将优先于字符串类型。