文章目录
Ant是什么
Apache Ant是一个基于Java的生成工具。据最初的创始人James Duncan Davidson介绍,这个工具的名称是another neat tool(另一个整洁的工具)的首字母缩写
Ant的作用
生成工具在软件开发中用来将源代码和其他输入文件转换可执行文件的形式(也有可能转换为可安装的产品映像形式)。随着应用沉痼的生成过程变得更加复杂,确保在每次生成期间都使用精确相同的生成步骤,同时实现尽可能多的自动化,以便及时产生一致的生成版本,这就变得更加重要。
Ant的优势
Ant定义生成文件之间的依赖关系,它使用跨平台的Java类。使用Ant,您能够编写单个生成文件,这个生成文件在任何Java平台上都一致地操作(因为Ant本身也是使用Java语言来实现的),这就是Ant最大的优势。
例子 创建文件夹
在D盘下新建一个build.xml文件
<?xml version="1.0" encoding="utf-8"?>
<project default="init">
<target name="init">
<mkdir dir="helloworld/a/b/c/d"/>
</target>
</project>
在cmd转到D盘输入ant
创建了文件夹
将文件夹里面放入文件
<?xml version="1.0" encoding="utf-8"?>
<project default="init">
<target name="init">
<delete dir="helloworld"/>
</target>
</project>
删除文件夹无论里面有什么
如果以其他方式命名,在cmd中要加上参数,例如创建一个hello.xml文件,然后再cmd中输入ant -f hello.xml就会执行成功。
Ant生成文件剖析
Ant没有定义它自己的自定义语法;相反,它的生成文件是用XML编写的。存在一组Ant能够理解的预定义XML元素,而且还可以定义新的元素来扩展Ant的功能。每个生成文件由单个project元素组成,该元素又包含一个或多个target元素。一个目标(target)是生成过程中已定义的一个步骤,它执行任意数量的操作,比如编译一组源文件。并且这些操作本身是由其他专用任务标签执行的
然后这些任务将根据需要被分组到各个target元素中。一次生成过程所必需的所有操作可以放入单个target元素中,但是那样会降低灵活性。将那些操作划分为逻辑生成步骤,每个步骤包含再它自己的target元素中,这样通常更为可取。这样可以执行整体生成过程的单独部分,却不一定要执行其他部分。
例如,通过仅调用某些目标,您可以编译项目的源代码,却不必创建可安装的项目文件。
<?xml version="1.0" encoding="utf-8"?>
<project default="second">
<target name="init">
<delete dir="helloworld"/>
<delete dir="helloworld"/>
<delete dir="helloworld"/>
<delete dir="helloworld"/>
</target>
<target name="second" depends="init">
<mkdir dir="abc"/>
<mkdir dir="xyz"/>
<mkdir dir="abcxyz"/>
</target>
</project>
先执行second发现他要依赖init然后先执行init然后执行second
顶级project元素需要包含一个default属性,如果再Ant被调用时没有指定目标,这个属性将指定要执行的目标。然后需要使用target元素来定义该目标本身。
显示的指定目标:ant -f helloworld.xml init secound
<?xml version="1.0" encoding="utf-8"?>
<project default="second">
<!--hello-->
<description>
hello world
</description>
<target name="init" description="hello world">
<mkdir dir="helloworld"/>
<mkdir dir="hello"/>
<mkdir dir="world"/>
<mkdir dir="welcome"/>
</target>
<target name="second" depends="init">
<delete dir="helloworld"/>
<delete dir="hello"/>
<delete dir="world"/>
<delete dir="welcome"/>
</target>
</project>
属性
Ant中的属性类似编程语言中的变量,它们都具有名称和值。然而与通常的变量不同,一经设置,Ant中的属性就不可更改;它们时不可变的,就像Java语言中的String对象。 这起初看来似乎很有限制性,但这样是为了遵循Ant的简单原则:毕竟,它是一个生成工具,而不是一种编程语言。如果尝试给某个现有属性赋予一个新的值,这不会被看作是一个错误,但是该属性仍然会保留其现有值。
定义与使用属性
<property name="metal" value="beryllium"/>
为了再生成文件的其他部位引用这个属性,使用以下语法:
${metal}
例如,为了使用这样一个值,它是另一个属性的值的组成部分,将标签写成下面这样
<property name="metal-database" value="${metal}.db"/>
lovation属性
属性经常用于引用文件系统上的文件或目录,但是对于使用不同路径分隔符(例如,\或/)的平台来说,这样可能在跨越不同平台时导致问题。Ant的location属性专门设计用于以平台无关的方式包含文件系统路径。像下面这样使用location来代替value:
<property name="database-file" location="archive/databases/${metal}.db"/>
用于location属性的路径分割字符将被转换为当前平台的正确格式;而且由于文件名是相对的,它被认为是相对于项目的基目录。我们同样额可以容易地写为下面这样:
<property name="database-file" location="archive\databases\${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完成compile步骤,它将判断出需要首先执行init和preprocess这两个目标。init目标不依赖其他任何目标,因此它将首先被执行。然后Ant检查preprocess target,发现它依赖init目标;由于已经执行了后者,Ant不会再次执行它,因而开始执行preprocess目标。最后可以执行compile任务本身。
注意目标出现在哎生成文件中的顺序并不重要:执行顺序是由depends属性唯一确定的。
从命令行运行Ant
从命令提示符调用Ant可以简单得只需键入单独的ant。如果您这样做,Ant将使用默认的生成文件;该生成文件中指定的默认目标就是Ant尝试要生成的目标。还可以指定许多命令行选项,后面跟着任意数量的生成目标,Ant将按顺序生成这其中的每个目标,并在此过程中解决所有依赖关系。
默认情况下,Ant寻找一个名为build.xml的文件。因此,如果您的生成文件使用这个名称,就不需要在命令行指定它。当然,有时使用具有其他名称的生成文件更方便,在那样的情况下,您需要对Ant使用-buildfile< file>参数(-f< file>是其简写形式>
另一个有用的选项是-D,它用于设置随后可以在生成文件中使用的属性。这对于配置您想要以某周方式开始的生成过程是非常有用的。例如,为了将name属性设置为某个特定的值,您会使用一个类似下面这样的选项:
-Dmetal = beryllium
这个功能可用于覆盖生成文件中的初始属性设置。正如前面指出过的,属性的值一经设置就不能改变。-D标志在读取生成文件中的任何信息之前设置某个属性;由于生成文件中的指派落在这个初始指派之后,因此它不会改变其值。
IDE集成
编译源代码
<javac srcdir="src"/>
这个标签寻找src目录中以.java为扩展名的所有文件,并对它们调用javac编译器,从而在相同的目录中生成类文件。当然将类文件放在一个单独的目录结构中通常会更清晰;可以通过添加destdir属性来让Ant做到这点。
<?xml version="1.0" encoding="UTF-8"?>
<project name="myAntProject" basedir="." default="package">
<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>
Buildfile: E:\workspace\Ant\build.xml
init:
preprocess:
[mkdir] Created dir: E:\workspace\Ant\compile
myCompile:
[javac] Compiling 1 source file to E:\workspace\Ant\compile
BUILD SUCCESSFUL
Total time: 582 milliseconds
其他有用的属性包括:
classpath:等价于javac的-classpath选项。
debug=“true":指示编译器应该带调试信息编译源文件
javac任务的一个重要特点在于,它仅编译那些它认为需要编译的源文件。如果某个类文件已经存在,并且对应的源文件自从该类文件生成以来还没有改变过,那么该源文件就不会被重新编译。
javac任务的输出显示了实际被编译的源文件的数目。
这种行为刻画了Ant的许多任务的特点:如果某个任务能够确定所请求的操作不需要执行,那么该操作就会被跳过。
创建JAR文件
在编译Java源文件之后,结果类文件通常被打包到一个JAR文件中,这个文件类似zip归档文件。每个JAR文件都包含一个清单文件,它可以指定该JAR文件的属性。
下面是Ant中jar任务的一个简单使用例子:
<jar destfile="package.jar" basedir="classes"/>
这将创建一个名为package.jar的JAR文件,并把classes目录中的所有文件添加到其中(JAR文件能够包含任意类型的文件,而不只是类文件)。此处没有指定清单文件,因此Ant将提供一个基本的清单文件
<?xml version="1.0" encoding="UTF-8"?>
<project name="myAntProject" basedir="." default="package">
<property name="compile" value="compile"></property>
<property name="dist" value="dist"></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}" includeAntRuntime="false"></javac>
</target>
<target name="dist" depends="myCompile">
<jar destfile="${dist}/package.jar" basedir="${compile}"></jar>
</target>
</project>
Buildfile: E:\workspace\Ant\build.xml
init:
preprocess:
myCompile:
dist:
[jar] Building jar: E:\workspace\Ant\dist\package.jar
BUILD SUCCESSFUL
Total time: 314 milliseconds
package learn.ant;
import java.awt.Color;
import java.awt.Dimension;
import javax.swing.JButton;
import javax.swing.JFrame;
public class test {
public static void main(String[] args) {
JFrame frame = new JFrame("Hello Ant");
frame.setSize(new Dimension(400, 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);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<project name="myAntProject" basedir="." default="package">
<property name="compile" value="compile"></property>
<property name="dist" value="dist"></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}" includeAntRuntime="false"></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="learn.ant.test"/>
</manifest>
</jar>
</target>
</project>
Buildfile: E:\workspace\Ant\build.xml
init:
preprocess:
myCompile:
dist:
[jar] Building jar: E:\workspace\Ant\dist\package.jar
BUILD SUCCESSFUL
Total time: 210 milliseconds
manifest属性允许指定一个用作该JAR文件的清单的文件。清单文件的内容还可以使用manifest任务在生成文件中指定。这个任务能够向文件系统写入一个清单文件,或者能够实际嵌套在jar之内,以便一次性地创建亲蛋文件和JAR文件。
时间戳生成
在生成环境中使用当前时间和日期,以某种方式标记某个生成任务的输出,以便记录它是何时生成的,这经常是可取的。这可嫩涉及编辑一个文件,以便插入一个字符串来指定日期和时间,或将这个信息合并到JAR或zip文件的文件名中。
这种需要是通过简单但是非常有用的tstamp任务来解决的。这个任务通常在某次生成过程开始时调用,比如在一个init目标中。这个任务不需要属性,许多情况下只需< tstamp/>就足够了
tstamp不产生任何输出;相反,它根据当前系统时间和日期设置Ant属性。下面是tstamp设置的一些属性、对每个属性的说明,以及这些属性可被设置到的值的例子。
属性 | 说明 | 例子 |
---|---|---|
DSTAMP | 设置为当前时日,默认格式为yyymmdd | 20031217 |
TSTAMP | 设置为当前时间,默认格式为hhmm | 1603 |
TODAY | 设置为当前日期,带完整的月份 | 2003年12月17日 |
在调用tstamp任务之后,我们能够根据日期命名该JAR文件,如下所示:
<jar destfile="package-${DSTAMP}.jar" basedir="classes"/>
文件系统操作
创建目录
最基本的额文件系统操作之一就是创建目录或文件夹。做这项工作的任务名为mkdir
<mkdir dir="archive/metals/zinc"/>
mkdir任务的另一个有用特性是它的如下能力:
在父目录还不存在时创建它们,如果目标目录已经存在,mkdir任务不会发出错误消息而只是假设它的工作已经完成,从而什么也不做
删除目录
<delete dir="archive/metals/zinc"/>
这将删除指定的目录连同它包含的所有文件以及子目录。使用file属性而不是dir属性可以指定要删除的单个文件
复制和移动文件以及目录
<copy file="src/Test.java" tofile="sec/TestCopy.java"/>
还可以使用move来执行重命名操作而不是拷贝文件
<move file="src/Test.java" tofile="src/TestCopy.java"/>
另一个常用的文件系统操作是将文件复制或移动到另一个目录
<copy file="src/Test.java" todir="archive"/>
<move file="src/Test.java" todir="archive"/>
<!--写成todir="D:/"和下面的效果一样,但是不能重命名-->
<target name="copyFile">
<copy file="src/learn/ant/test.java" tofile="D:/TestCopy.java"></copy>
</target>
<target name="moveFile">
<move file="src/learn/ant/test.java" todir="D:/"></move>
</target>
默认情况下,Ant仅输出它执行的移动和复制操作的摘要,包括诸如已移动或复制的文件的数量等信息。如果想看到更详细的信息,包括涉及的文件名称等,可以将verbose属性设置为true。
创建压缩zip文件
<zip destfile="output.zip" basedir="output"/>
<target name="compress" depends="myCompile">
<zip destfile="${dist}/package.zip" basedir="${compile}"></zip>
</target>
解压缩和提取文件
<unzip src="output.tar.gz" dest="extractDir"/>
还可以包括overwrite属性来控制覆盖行为。默认设置是覆盖与正在被提取的归档文件中的条目相匹配的所有现有文件
<target name="uncompress" depends="compress">
<unzip dest="${dist}" src="${dist}/package.zip"></unzip>
</target>
替换文件中的标记
replace任务,它执行文件中的查找和替换操作。
token属性指定要查找的字符串,value属性指定一个新的字符串,查找到的标记字符串的所有实例都被替换为这个新的字符串。例如:
<replace file="input.txt" token="old" value="new"/>
替换操作将在文件本身之内的额适当位置进行。为了提供更详细的输出,可把summary属性设置为true。这将导致该任务输出找到和替换的标记字符串实例的数目
<target name="replaceOperation">
<replace file="test.txt" token="what" value="how"></replace>
</target>
Buildfile: E:\workspace\Ant\build.xml
replaceOperation:
BUILD SUCCESSFUL
Total time: 263 milliseconds
加上summary="on"结果显示
Buildfile: E:\workspace\Ant\build.xml
replaceOperation:
[replace] Replaced 1 occurrences in 1 files.
BUILD SUCCESSFUL
Total time: 264 milliseconds
使用CVS
CVS任务的主要属性是cvsRoot,它是对CVS库的完整引用,包括连接方法和用户详细信息。
这个参数的额格式如下
[:method:][user][:password]@hostname[:[port]]/path/to/repository
提取是CVS任务的默认操作;其他操作可通过使用command属性来指定
模式匹配
可以对目录执行模式匹配。例如,模式src*/*.java将匹配带src前缀的任何目录中的所有Java文件。
还有另一种模式结构:**,它匹配任意数量的目录。例如,模式 **/*.java将匹配当前目录结构下的所有Java文件。
<copy todir="srchive">
<fileset fir="src">
<include name="*.java"/>
</fileset>
</copy>
<property name="dist" value="dist"></property>
<property name="src" value="src"></property>
<target name="copy2">
<copy todir="${dist}">
<fileset dir="${src}">
<include name="**/*.java"/>
</fileset>
</copy>
</target>
Buildfile: E:\workspace\Ant\build.xml
copy2:
[copy] Copying 1 file to E:\workspace\Ant\dist
BUILD SUCCESSFUL
Total time: 197 milliseconds
fileset默认情况下包含指定src目录下的所有文件,因此为了仅选中Java文件,我们对模式使用一个include元素。类似地,我们可以对另一个模式添加一个exclude元素,从而潜在地排除include指定的匹配项。甚至可以指定多个include和exclude元素;这样将得到一组文件和目录,它们包含include模式的所有匹配项的并集,但排除了exclude模式的所有匹配项。
自动从文件集内容中排除的内置模式列表。该列表包括与名为CVS的目录相匹配的条目,以及以~字符结尾的文件,它们可能是备份文件。通常不想在文件系统操作中包含这类文件和目录,因此排除这些文件是默认行为。然而,如果确实想无例外地选择所有文件和目录,可以将文件集的defaultexcludes属性设置为no
使用自定义任务来扩展ANT
我们将考察一个简单自定义任务的构造过程。这个任务将对文件中的行执行排序操作,并将排序后的行集写到一个新文件中
为实现一个简单的自定义任务,我们所需做的就是扩展org.apche.tools.ant.Task类,并重写execute()方法
大多数任务,不管是核心任务还是自定义任务,都利用属性来控制它们的行为。对于这个简单任务,我们需要一个属性来指定要排序的文件,需要另一个属性来指定排序内容的输出。我们把这两个属性分别叫做file和tofile