自动构建工具Ant的使用-笔记

第一:什么是Ant?

Apache Ant是一个基于Java的生成工具。据最初的创始人James Duncan Davidson的介绍,这个工具的名称是another neat tool(另一个整洁的工具)的首字母的缩写。

第二:到哪儿去找ant工具,地址:http://ant.apache.org/bindownload.cgi

第三:配置环境变量:如下图:

第四:用命令行测试下:

如果出现上述界面则证明Ant配置成功

第五:Ant的作用是什么?

生成工具在软件开发中用来将源代码和其他的输入文件转换为可执行文件的形式(也有可能转换为可安装的产品映像形式)。随着应 用程序的生产过程变得更加的复杂,确保在每次生成期间都使用精确相同的生成步骤,同时实现尽可能多的自动化,以便及时生产一致的生成版本,这就变得更加重 要。

Ant定义生成文件之间的依赖关系,它使用跨平台的Java类。使用Ant,就能够编写单个文件,这个生成文件在任何Java平台上都一致地操作(因为Ant本身也是使用Java语言来实现的),这就是Ant最大的优势

第六:初步使用,

例子:编写简单的build.xml(默认的,Ant会自动的在当前目录下去寻找这个文件,如果找的就去构建,如果找不到就会提示:build.xml does not exist!

在d盘下创建build.xml文件内容如下:

<?xml version=”1.0″ encoding=”utf-8″?>

<project default=”init”>

<target name=”init”>

<mkdir dir=”helloworld” />

</target>

</project>

意思是:在当前目录下创建一个名字为helloworld的目录,对应到windows磁盘就是一个文件夹

然后,运行ant:

看当前目录:

我们创建了一个helloworld这样的一个目录

第七:Ant生成文件解析

Ant没有定义自己的自定义语法;相反,它的生成文件是使用XML编写的。存在Ant能够理解的预定义XML元素,而且还可以定义新的元素来扩展Ant的功能。每个生成文件由单个project元素组成,该元素又包含一个或多个target元素。一个目标(target)是生成过程中已定义的一个步骤,它执行任意数量的操作,比如编译一组源文件。并且这些操作本身是由其他专用的任务标签执行的

顶级的project元素需要包含一个default属性,如果在Ant被调用时没有指定目标,这个属性将指定要执行的目标。然后需要使用target元素来定义该目标本身。

Ant中的属性类似编程语言中的变量,它们都具有名称和值。然而与通常的变量不同,一经设置,Ant中是属性就不可更改;它们是不可变的,就像Java语言中的String对象。这起初看起来似乎很有限制性,但这样是为了遵循Ant的简单原则:毕竟,它是一个生成工具,而非一种编程语言。如果尝试给某个现有的属性赋予一个新的值,这不会看作是一个错误,但是该属性仍然会保留其现有的值

定义与使用属性

<property name=”metal” value=”beryllium”/>

为了在生成文件的其他部分引用这个属性,使用以下语法:

${metal}

例如,为了使用这样一个值,它是另一个属性的值的组成部分,将标签写成下面这样

<property name=”metal-database” value=”${metal}.db”/>

属性经常用于引用文件系统上的文件或目录,但是对于使用不同路径分隔符(例如,/与|的平台来说,这样可能再跨越不同平台时导致问题。Ant的location属性专门设计用于以平台无关的方式包含文件系统路径。像下面这样使用location替代value:

<property name=”database-file” location=”archive/database/${metal}.db”/>

用于location属性的路径分隔字符将被转换为当前平台的正确格式;而且由于文件名是相对的,它被认为是相对于项目的基目录。我们同样可以容易地写为下面这样:

<property name=”database-file” location=”archive\database\${metal}.db”/>

这个标签的两个版本都会在不同的平台具有相同的行为

定义依赖关系:

生成一个项目一般需要许多步骤–例如首先要编译源代码,然后将它打包为Java归档文件(Java Archive File,JAR)。这其中许多步骤都具有清楚定义的顺序–例如,在编译器从源代码生成类文件之前,您不能打包类文件。与顺序指定target所不同的是,Ant采用一种更灵活的方法来定义依赖关系。每个目标的定义依据的是它能够执行之前必须完成的其他所有目标。这是使用target元素的depends属性来实现的

例如:

<target name=”init”/>

<target name=”preprocess” depends=”init”/>

<target name=”compile” depends=”init,preprocess”/>

<target name=”package” depends=”compile”/>

这种方法允许您执行项目任何阶段的生成过程;Ant会首先执行已定义的先决阶段。在上面的例子中,如果让Ant完成complie步骤,它将判断出需要首先执行init和和preprocess这两个目标。Init目标不依赖其他任何目标,因此它将首先被执行。然后Ant检查preprogress target,发现它依赖init目标;由于已经执行后者,Ant不会再次执行它,因此开始执行preprocess目标。最后可以执行compile任务本身。

注意目标出现在生成文件中的顺序并不重要:执行顺序是由depends属性唯一确定的。

第八:尝试在IDE里面是有

在工程的根目录下建议个build.xml

内容:

<?xml version=”1.0″ encoding=”utf-8″?>

<project name=”myAntProject” default=”package” basedir=”.”>

<property name=”hello” value=”hello123″></property>

<property name=”welcome” value=”welcome123″></property>

<target name=”init”/>

<target name=”preprocess” depends=”init”>

<mkdir dir=”${hello}”/>

<mkdir dir=”${welcome}”/>

</target>

<target name=”compile” depends=”init,preprocess”/>

<target name=”package” depends=”compile”/>

</project>

跑一下:

生成hello123、和welcom123两个文件夹

备注:Ant还支持debug

从命令提示符调用Ant可以简单键入单独的Ant。如果这样做,Ant将使用默认的生成文件;该生成文件中指定的默认目录就是Ant尝试要生成的目标。还可以指定许多命令行选项,后面跟着任意数量的生成目录,Ant将按顺序生成这其中的每个目标,并在此过程中解决所有依赖关系

默认情况下,Ant寻找一个名为build.xml的文件。因此,如果你的生成文件使用这个名称,就不需要在命令行指定它。当然,有时使用具有其他名称的生成文件更方便,在那样的情况下,你需要对Ant使用-buildfie<file>参数(-f  <file>是其简写形式)

另一个有用的选项是-D,它用于设置随后可以再生成文件中使用的属性。。这对于配置你想要的以某种方式开始的生成过程非常有用。例如,为了将name属性设置为某个特定的值,你会使用类似下面这样的选项:

–Dmetal!=beryllium

这个功能可用于覆盖生成文件中初始属性设置。正如前面指出过的,属性的值一经设置就不能改变。-D标志在读取生成文件中的任何信息之前设置某个属性;由于生成文件中的指派落在这个初始指派之后,因此它不会改变其值

编译java源代码

build.xml

<?xml version=”1.0″ encoding=”utf-8″?>

<project name=”myAntProject” default=”package” basedir=”.”>

<property name=”compile” value=”compile”></property>

<target name=”init”></target>

<target name=”preprocess” depends=”init”>

<mkdir dir=”${compile}”/>

</target>

<target name=”compile” depends=”init,preprocess”></target>

<target name=”package” depends=”compile”></target>

<target name=”myCompile” depends=”preprocess”>

<javac srcdir=”src” destdir=”${compile}”></javac>

</target>

</project>

其他有用的属性包括

-classpath:等级于javac的-classpath选项。

-debug=”true”:指示编译器应该带调试信息编译源文件。

Javac任务的一个重要的特点在于,它仅编译那些它认为需要编译的源文件。如果某个类文件已经存在,并且对应的源文件自从该类文件生成以来还没改变过,那么该源文件就不会被重新编译。

Javac任务的输出显示了实际被编译的源文件的数目。

这种行为刻画了Ant的许多任务的特点:如果某个任务能够确定所请求的操作不需要执行,那么该操作就会被跳过。

创建JAR文件

在编译Java源文件之后,结果类文件通常被打包到一个JAR文件中,这个文件类似zip归档文件,它可以指定该JAR文件的属性

下面是Ant中jar任务的一个简单使用例子:

<jar destfile=”package.jar” basedir=”classes”/>

这将创建一个名为package.jar的jar文件,并把classes目录中所有文件添加到其中(JARwe文件能够包含任意类型的文件,而不只是类文件)。此处没有指定清单文件,因此Ant将提供一个基本的清单文件

Mainifest属性允许指定一个用作JAR文件的清单的文件。清单文件的内容还可以使用mainifest任务在生成文件中指定。这个任务能够向文件系统写入一个清单文件,或者能够实际嵌套在Jar之内,以便一次性地创建清单文件和JAR文件。

Build.xml

<?xml version=”1.0″ encoding=”utf-8″?>

<project name=”myAntProject” default=”package” basedir=”.”>

<property name=”compile” value=”compile”></property>

<property name=”dist” value=”dist”></property>

<target name=”init”></target>

<target name=”preprocess” depends=”init”>

<mkdir dir=”${compile}”/>

<mkdir dir=”${dist}”/>

</target>

<target name=”compile” depends=”init,preprocess”></target>

<target name=”package” depends=”compile”></target>

<target name=”myCompile” depends=”preprocess”>

<javac srcdir=”src” destdir=”${compile}”>

</javac>

</target>

<target name=”dist” depends=”myCompile”>

<jar destfile=”${dist}/package.jar” basedir=”${compile}”></jar>

</target>

</project>

生成双击运行的程序

新建一个类:

内容:

import java.awt.Color;

import java.awt.Dimension;

import javax.swing.JButton;

import javax.swing.JFrame;

public class Test3 {

public static void main(String[] args){

JFrame frame = new JFrame();

frame.setSize(new Dimension(200,300));

frame.setBackground(new Color(200,200,200));

frame.setAlwaysOnTop(true);

frame.getContentPane().add(new JButton(“Ant”));

frame.setVisible(true);

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

}

}

Build.xml

<?xml version=”1.0″ encoding=”utf-8″?>

<project name=”myAntProject” default=”package” basedir=”.”>

<property name=”compile” value=”compile”></property>

<property name=”dist” value=”dist”></property>

<target name=”init”></target>

<target name=”preprocess” depends=”init”>

<mkdir dir=”${compile}”/>

<mkdir dir=”${dist}”/>

</target>

<target name=”compile” depends=”init,preprocess”></target>

<target name=”package” depends=”compile”></target>

<target name=”myCompile” depends=”preprocess”>

<javac srcdir=”src” destdir=”${compile}”>

</javac>

</target>

<target name=”dist” depends=”myCompile”>

<jar destfile=”${dist}/package.jar” basedir=”${compile}”>

<manifest>

<attribute name=”Built-By” value=”${user.name}”/>

<attribute name=”Main-Class” value=”Test3″/>

</manifest>

</jar>

</target>

</project>

MAINIFEST.MF文件:

Manifest-Version: 1.0

Ant-Version: Apache Ant 1.7.1

Created-By: 11.3-b02 (Sun Microsystems Inc.)

Built-By: Administrator

Main-Class: Test3

时间戳生成

-在生成环境中使用当前时间和日期,以某种方式标记某个生成任务的输出,以便记录它是何时生成的,这经常是可取的。这可能涉及编辑一个文件,以便插入一个字符串来指定日期和时间,或将这个信息合并到JAR或者zip文件的文件名中

-这种需要是通过简单但是非常有用的tstamp任务来解决的。这个任务通常在某次生成过程开始时调用,比如在一个init目标中。这个任务不需要属性,许多情况下只需要<tstamp/>就足够了

在调用tstamp任务之后,我们能够根据日期命名该JAR文件,如下所示:

<jar destfile=”package-${DSTAMP}.jar” basedir=”classes”/>

Build.xml

<?xml version=”1.0″ encoding=”utf-8″?>

<project name=”myAntProject” default=”package” basedir=”.”>

<property name=”compile” value=”compile”></property>

<property name=”dist” value=”dist”></property>

<target name=”init”></target>

<target name=”preprocess” depends=”init”>

<mkdir dir=”${compile}”/>

<mkdir dir=”${dist}”/>

</target>

<target name=”compile” depends=”init,preprocess”></target>

<target name=”package” depends=”compile”></target>

<target name=”myCompile” depends=”preprocess”>

<javac srcdir=”src” destdir=”${compile}”>

</javac>

</target>

<target name=”dist” depends=”myCompile”>

<tstamp></tstamp>

<jar destfile=”${dist}/package-${DSTAMP}.jar” basedir=”${compile}”>

<manifest>

<attribute name=”Built-By” value=”${user.name}”/>

<attribute name=”Main-Class” value=”Test3″/>

</manifest>

</jar>

</target>

</project>

生成带时间戳的Jar包

创建和删除目录

-最基本的文件系统操作之一就是创建目录或文件夹。做这项工作的任务名称为mkdir

-<mkdir dir=”archive/metals/zinc”/>

-mkdir任务的另一个有用的特性是它的如下能力:在父目录还不存在时创建它们

-如果目标目录已经存在,mkdir任务不会发出错误消息,而只是假设它的工作已经完成,从而什么也不做

删除目录

-<delete dir=”archive/metals/zinc”/>

-这将删除指定的目录连同它包含的所有文件以及子目录。使用file属性而不是dir属性可以指定要删除的单个文件

复制移动文件目录

<copy file=”src/Test.java” tofile=”src/TestCopy.java”/>

还可以使用move来执行重命名操作而不是拷贝文件

<move file=”src/Test.java” tofile=”src/TestCopy.java”/>

另一个常用的文件系统操作是将文件复制或在移动到另一个目录

<copy file=”src/Test.java” todir=”archive”/>

<move file=”src/Test.java” tofile=”archive”/>

默认情况下,Ant仅输出它执行的移动和复制操作的摘要,包括诸如已移动或复制的文件的数量等信息。如果想看到更详细的信息,包括涉及的文件名称等,可以将verbose属性设置为true

创建和压缩zip文件

<zip destfile=”output.zip” basedir=”output”/>

解压缩和提取文件

<unzip src=”output.tar.gz” dest=”extnacDir”/>

替换文件中的标记

replace任务,它执行文件中的查找和替换操作

Token属性指定要查找的字符串,value属性指定一个新的字符串,查找到的标记字符串的所有实例都被替换为这个新的字符串。例如:

<replace file=”input.txt” token=”old” value=”new”/>

还有其他很多的功能,如模式匹配、与CVS版本控制结合等等的高端功能

–自定义属性

使用自定义任务来扩展ANT

我们将考察一个简单的自定义任务的构造过程。这个任务将对文件中的行执行顺序操作,并将排序后的行集写到一个新文件中

为实现一个简单的自定义任务,我们所需做的就是扩展org.apache.tools.ant.Task类,并重写execute()方法。

大多数任务,不过是核心任务还是自定义任务,都利用属性来控制它们的行为。对应这个简单的任务,我们需要一个属性来指定要排序的文件,需要另一个属性来指定排序内容的输出。我们把这两个属性分别叫做file和tofile

例子:

新建一个名称为:com.shengsiyuan.ant.tools.extend的包

新建类FileSorter:

package com.shengsiyuan.ant.tools.extend;

import java.io.BufferedReader;

import java.io.BufferedWriter;

import java.io.File;

import java.io.FileReader;

import java.io.FileWriter;

import java.util.ArrayList;

import java.util.Collections;

import java.util.List;

import java.util.ListIterator;

import org.apache.tools.ant.BuildException;

import org.apache.tools.ant.Task;

public class FileSorter extends Task{

private File srcFile;

private File destFile;

public File getSrcFile() {

return srcFile;

}

public void setSrcFile(File srcFile) {

this.srcFile = srcFile;

}

public File getDestFile() {

return destFile;

}

public void setDestFile(File destFile) {

this.destFile = destFile;

}

@Override

public void execute() throws BuildException

{

try{

BufferedReader fromFile = new BufferedReader(new FileReader(srcFile));

BufferedWriter toFile = new BufferedWriter(new FileWriter(destFile));

List<String> list = new ArrayList<String>();

String line = fromFile.readLine();

while(line != null)

{

list.add(line);

line = fromFile.readLine();

}

Collections.sort(list);

for(ListIterator<String> li = list.listIterator();li.hasNext();){

String str = li.next();

toFile.write(str);

toFile.newLine();

}

fromFile.close();

toFile.close();

}catch(Exception ex){

ex.printStackTrace();

}

}

}

定义任务:

<taskdef name=”myFileSorterExample” classname=”com.shengsiyuan.ant.tools.extend.FileSorter” classpath=”bin”>

</taskdef>

使用自定义任务:

<target name=”myFileSorter”>

<myFileSorterExample srcFile=”input.txt” destFile=”output.txt”/>

</target>

Input.txt的内容:

模式匹配

可以对目录执行模式匹配。例如,模式src*/*.java将匹配带src前缀的任何目录中的所有Java文件。

还有另一种模式结构:**,它匹配任意数量的目录。例如,模式**/*.java将匹配当前目录结构下的所有Java文件。

<copy todir=”archive”>

-<fileset dir=”src”>

.<include name=”*.java”/>

-</fileset>

</copy>

fileset默认情况下包含指定src目录下的所有文件,因此为了仅选择Java文件,我们对模式使用一个include元素。类似地,我们可以对另一个模式添加一个exclude元素,从而潜在地排除include指定的匹配项。甚至可以指定多个include和exclude元素;这样将得到一组文件和目录,它们包含include模式的所有匹配项的并集,但排除了exclude模式的所有匹配项