[这个贴子最后由starlight在 2006/01/15 10:49pm 第 1 次编辑]
使用选择器
正如我们已经看到的,文件集用于指定一组文件,并且这个组的内容可以使用 include 和 exclude
模式来指定。也可以结合称为 选择器 的特殊元素使用include 和 exclude 来选择文件。下面是
对 Ant 可用的核心选择器的列表:
· size:这个选择器用于根据文件的字节大小选择文件(除非使用 units 属性来指定了不同
的单位)。when 属性用于设置比较的性质(less、more 或者 equal),value 属性定义每
个文件将与之作比较的目标大小。
· contains:只有包含给定文本字符串(由text 属性指定)的文件才匹配这个选择器。默认
情况下,查找操作是大小写敏感的;添加casesensitive="no" 可以改变默认设置。
· filename:name 属性指定文件名要与之匹配的模式。它本质上与 include 元素相同,以及
与指定了negate="yes" 时的 exclude 元素相同。
· present:从当前目录结构中选择如下文件:它们与指定的 targetdir 目录中的文件具有相
同的名称和相对目录结构。
· depend:这个选择器与 present 选择器具有相同的效果,只不过匹配的文件被限制到相对
于 targetdir 位置中的对应文件来说,最近已修改过的那些文件。
· date:这个选择器基于其最后修改日期选择文件。when 属性指定作比较的性质是 before、
after 还是 equal,datetime 属性指定与之作比较的日期和时间,这个日期和时间具有给
定的固定格式 MM/DD/YYYY HH:MM AM_or_PM。注意 Windows 平台上有一个内置的 2 秒偏移,
以允许底层文件系统的不精确性 —— 这可能导致匹配的文件数量超过预期。允许的回旋时
间量可以使用 granularity 属性来更改(以毫秒为单位来指定)。
· depth:这个选择器检查每个文件的目录结构层次数目。min 和/或 max 属性用于选择具有
想要的目录层次数目的的文件。
还可以通过在一个选择器 容器 内嵌套一个或多个选择器来组合选择器。 最常用的选择器容器 and
仅选择它包含的所有选择器都选择了的文件。其他选择其容器包括 or、not、none 和 majority。
下面是一个文件集的例子,它仅选择那些大于 512 字节并且包含字符串“hello”的文件。
将生成文件链接起来
有两种生成大型项目的不同方法。一种是让一个单一的生成文件做所有事情;另一种是让高级别的
生成文件调用其它生成文件以执行特定任务,从而将生成过程划分为许多较小的部分。
使用 ant 任务来从一个 Ant 生成中调用另一个 Ant 生成是很容易的。在简单的情况下,您可以使
用 antfile 属性,仅指定那些要使用的生成文件,Ant 将生成该生成文件中的默认目标。例如:
在父生成文件中定义的任何属性默认将传递给子生成文件,虽然这可以通过指定
inheritAll="false"来避免。通过使用 property 元素来传入显式的属性也是可以做到的 —— 即
使将 inheritAll 设置为 false,这些属性也仍然适用于子生成文件。这个功能很适合用于给子生
成文件传入参数。
让我们来考虑一个例子。下面是我们想要调用的一个生成文件:
(我们在前面还没有遇到过 echo 任务 —— 它简单地输出给定的消息。)
下面是调用第一个生成文件的第二生成文件,它还给第一个生成文件传入 message 属性:
运行第二个生成文件所得到的输出如下:
Buildfile: build.xml
callSub:
showMessage:
[echo] Message=Hello from parent build
BUILD SUCCESSFUL
Total time: 0 seconds
使用 CVS 知识库
CVS 是 concurrent versions system(并发版本控制系统) 的缩写。它是一个源代码控制系统,
设计用于跟踪许多不同开发人员做出的更改。它非常流行,在开放源代码项目中特别受欢迎。Ant 提
供了与 CVS 的紧密集成。这对于自动化生成环境是非常有用的,因为单个生成文件也可以从源代码
知识库中提取出一个或多个模块,生成项目,甚至基于自从前次执行生成以来所作的变更生成批处
理文件。
注意,为了利用 Ant 中的 cvs 任务,您需要在机器上安装 cvs 命令,并使其从命令行可用。这个
命令包括在大多数 Linux 发行套件中;它也以多种形式对 Windows 可用 —— 例如作为宝贵的
Cygwin 环境的一部分。(请参阅参考资料以了解关于 Cygwin 的更多信息。)
下面是从 CVS 知识库提取模块的一个例子生成文件:
cvs 任务的主要属性是 cvsRoot,它是对 CVS 知识库的完整引用,包括连接方法和用户详细信息。
这个参数的格式如下:
[:method:][[user][:password]@]hostname[:[port]]/path/to/repository
在上面的例子中,我们作为匿名用户连接到 Eclipse 项目的中央知识库。然后其他属性指定了我们
希望提取的模块以及放置提取文件的目的地。提取是 CVS 任务的默认操作;其他操作可通过使用
command 属性来指定。
请参阅参考资料以了解关于 CVS 的更多信息。
第七章 使用自定义任务来扩展ANT
简介
正如我们从前述几节中所看到的,Ant 非常强大,具有涵盖广泛功能集的许多核心任务。它还有许
多这里没有介绍的附加任务,再加上提供广泛附加功能的许多可选任务,以及作为 Ant-Contrib 项
目的一部分来提供的其他任务;最后,Apache Ant 主页上还列出了外部可用的更多任务。面对 Ant
提供的所有这些任务,您似乎再也不需要其他任务了,但是 Ant 的真正力量在于它的易于扩展性。
事实上,恰恰正是这种可扩展性促使人们开发了如此多的附加任务。
可能会存在这样的场合,在那样的场合下创建自定义的任务更为合适。例如,假设您创建了一个命
令行工具来执行某个特定操作;这个工具可能是将对 Ant 可用的任务的恰当候选者(当该工具是用
Java 语言编写的时更是这样,虽然该工具不一定是用 Java 语言编写的。)与其让 Ant 使用 exec
任务外部地调用该工具(这样将引入依赖关系,并使得生成文件在跨越不同平台时更难于使用),
您可以将它直接合并到生成文件中。还可以使得 Ant 的常规文件集和通配符匹配功能对自定义的任
务可用。
在本节中,我们将考察一个简单自定义任务的构造过程。这个任务将对文件中的行执行排序操作,
并将排序后的行集写到一个新文件中。
创建自定义的任务
为实现一个简单的自定义任务,我们所需做的就是扩展 org.apache.tools.ant.Task 类,并重写
execute() 方法。因此,作为这个文件排序自定义任务的框架,我们将编写如下代码:
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;
public class FileSorter extends Task {
// The method executing the task
public void execute() throws BuildException {}
}
注意我们声明 execute() 方法抛出一个 BuildException 异常。如果这个任务出了任何错误,我们
将抛出这个异常以便向 Ant 指出故障。
大多数任务,不管是核心任务还是自定义任务,都利用属性来控制它们的行为。对于这个简单任务,
我们需要一个属性来指定要排序的文件,需要另一个属性来指定排序内容的输出。我们把这两个属
性分别叫做 file 和 tofile。
Ant 使得支持自定义任务中的属性非常容易。为此,我们只需实现一个具有特别格式化的名称的方
法,Ant 能够使用生成文件中指定的对应属性的值来调用这个方法。这个方法的名称需要是 set 加
上属性的名称,因此在这个例子中,我们需要名为 setFile() 和 setTofile() 的方法。当 Ant 遇
到生成文件中的一个属性设置时,它会寻找相关任务中具有适当名称的方法(称为 setter 方法)。
生成文件中的属性是作为字符串来指定的,因此我们的 setter 方法的参数可以是一个字符串。在
这样的情况下,Ant 将在展开值所引用的任何属性之后,使用该属性的字符串值来调用我们的方法。
但有时我们想把属性的值看作是一种不同的类型。这里的示例任务就是这种情况,其中的属性值引
用文件系统上的文件,而不只是引用任意的字符串。可以通过将方法参数声明为 java.io.File 类
型来容易地做到这点。Ant 将接受属性的字符串值,并把它解释为一个文件,然后传递给我们的方
法。如果文件是使用相对路径名称来指定的,则会被转换为相对于项目基目录的绝对路径。Ant 能
够对其他类型执行类似的转换,比如 boolean 和 int 类型。如果您提供具有相同名称但是具有不
同参数的两个方法,Ant 将使用更明确的那一个方法,因此文件类型将优先于字符串类型。
这个自定义任务需要的两个 setter 方法类似如下:
// The setter for the "file" attribute
public void setFile(File file) {}
// The setter for the "tofile" attribute
public void setTofile(File tofile) {}
实现自定义的任务
使用前一小节开发的框架,现在我们能够完成这个简单的文件排序任务的实现:
import java.io.*;
import java.util.*;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;
/**
* A simple example task to sort a file
*/
public class FileSorter extends Task {
private File file, tofile;
// The method executing the task
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);
}
}
// The setter for the "file" attribute
public void setFile(File file) {
this.file = file;
}
// The setter for the "tofile" attribute
public void setTofile(File tofile) {
this.tofile = tofile;
}
}
两个 setter 方法简单地对属性的值排序,以便这些值能够在 execute() 方法中使用。这里,输入
文件被逐行地读入一个列表中,然后被排序并逐行地输出到输出文件。注意,为简单起见,我们很
少执行错误检查 —— 例如,我们甚至没有检查生成文件是否设置了必需的属性。不过我们的确至
少捕捉了所执行的操作抛出的 I/O 异常,并将这些异常作为 BuildExceptions 重新抛出。
现在可以使用 javac 编译器或从某个 IDE 内编译这个自定义的任务。为了解决所使用的 Ant 类的
引用问题,您需要把 ant.jar 文件的位置添加到 classpath 中。这个文件应该在 Ant 安装路径下
的 lib 目录。
使用自定义的任务
现在我们已经开发和编译了这个自定义的任务,下面可以从生成文件中利用它了。
在能够调用自定义的任务之前,我们需要给它指定一个名称来 定义 它,并告诉 Ant 关于实现这个
任务的类文件的信息,以及定位该类文件所必需的任何 classpath 设置。这是使用 taskdef 任务
来完成的,如下所示:
大功告成!现在可以像使用 Ant 的核心任务一样使用这个自定义的任务了。下面是一个完整的生成
文件,它显示了这个自定义任务的定义和用法:
现在在当前工作目录中创建一个 input.txt 文件来测试这个自定义的任务。例如:
Hello there
This is a line
And here is another one
下面是运行上面的生成文件之后产生的控制台输出:
Buildfile: build.xml
main:
[filesorter] Sorting file=E:\tutorial\custom\input.txt
BUILD SUCCESSFUL
Total time: 0 seconds
注意 input.txt 的相对路径名称被转换成了当前目录中的一个绝对路径名称。这是因为我们将
setter 方法的参数指定为 java.io.File 类型而不是 java.lang.String 类型。
现在看一下这个任务实际是否能工作。这时应该已经在同一目录中创建了名为 output.txt 的文件,
它包含以下内容:
And here is another one
Hello there
This is a line
您可以尝试指定一个不存在的输入文件,以确定该任务是如何向 Ant 报告 “file not found”异
常的。
祝贺您:您现在已经开发和使用了一个自定义的 Ant 任务!创建更复杂的任务还会涉及其他许多方
面,参考资料包含了指向此主题的进一步信息源的链接。
第八章 结束语和参考资料
结束语
我们希望您会发现这次 Ant 之旅很有帮助。同时,Ant 的目标是保持尽可能简单,它通过大量的任
务提供了大量的功能,每个任务都有许多选项,有时这可能是令人难以招架的。我们无法在单个教
程中探索 Ant 的所有功能,但愿我们已经介绍了所有的基本概念和足够的基本功能,以使您步入开
始在现实现目中使用 Ant 的轨道。下面总结一下本教程介绍过的内容:
· Ant 生成文件是如何构造的
· 如何从命令行以及从 Eclipse 内运行 Ant
· 如果通过编译源代码、创建 JAR 文件以及时间戳文件(以识别每次生成过程的输出)来生
成简单的 Java 项目
· 如何在 Ant 中执行基本的文件系统操作
· 模式匹配和选择器的基本概念,再加上如何从一个生成文件调用另一个生成文件,以及如何
执行 CVS 操作
· 如何通过编写 Java 类来扩展 Ant 的标准功能
要继续 Ant 之旅,请访问下一小节中的某些链接,同时熟悉 Ant 手册中的内容,该手册是编写 Ant
生成文件时的权威参考源。祝您好运! |