在阅读有关编写任务 [1]的教程后,本教程将解释如何获取和设置属性,以及如何使用嵌套文件集和路径。最后,它将解释如何将任务贡献给 Apache Ant。
目标是编写一个任务,该任务在路径中搜索文件并将该文件的路径保存到属性中。
我们可以使用另一个教程中的构建文件并对其进行一些修改。这就是使用属性的优势 - 我们可以重复使用几乎整个脚本。 :-)
<?xml version="1.0" encoding="UTF-8"?>
<project name="FindTask" basedir="." default="test">
...
<target name="use.init" description="Taskdef's the Find-Task" depends="jar">
<taskdef name="find" classname="Find" classpath="${ant.project.name}.jar"/>
</target>
<!-- the other use.* targets are deleted -->
...
</project>
构建文件位于存档tutorial-tasks-filesets-properties.zip [2]中的/build.xml.01-propertyaccess(未来版本保存为 *.02...,最终版本保存为 build.xml;源代码也是如此)。
我们的第一步是将属性设置为一个值并打印该属性的值。因此,我们的场景将是
<find property="test" value="test-value"/>
<find print="test"/>
好的,它可以用核心任务重写
<property name="test" value="test-value"/>
<echo message="${test}"/>
但我必须从已知的基础开始 :-)
那么该怎么做呢?处理三个属性(property、value、print)和一个执行方法。因为这只是一个介绍性示例,所以我不会进行太多检查
import org.apache.tools.ant.BuildException;
public class Find extends Task {
private String property;
private String value;
private String print;
public void setProperty(String property) {
this.property = property;
}
// setter for value and print
public void execute() {
if (print != null) {
String propValue = getProject().getProperty(print);
log(propValue);
} else {
if (property == null) throw new BuildException("property not set");
if (value == null) throw new BuildException("value not set");
getProject().setNewProperty(property, value);
}
}
}
如另一个教程中所述,属性访问是通过 Project 实例完成的。我们通过公共 getProject() 方法获取此实例,该方法继承自 Task(更准确地说,继承自 ProjectComponent)。读取属性是通过 getProperty(propertyname) 完成的(非常简单,不是吗?)。此属性将值作为 String 返回,如果未设置则返回 null。
设置属性... 并不难,但有多个 setter。您可以使用 setProperty() 方法,该方法将按预期执行。但 Ant 中有一个黄金法则:属性是不可变的。此方法将属性设置为指定的值 - 无论它之前是否有值。因此,我们使用另一种方法。 setNewProperty() 仅在没有同名属性时才设置属性。否则,将记录一条消息。
(顺便说一下,简要解释一下 Ant 的“命名空间” - 不要与 XML 命名空间混淆:<antcall> 为属性名称创建一个新的空间。所有来自调用者的属性都传递给被调用者,但被调用者可以在没有调用者注意的情况下设置自己的属性。)
还有一些其他 setter(但我没有使用它们,所以对此我无话可说,抱歉 :-)
将上面的两行示例放入名为 use.simple 的目标后,我们可以从测试用例中调用它
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.apache.tools.ant.BuildFileRule;
public class FindTest {
@Rule
public final BuildFileRule buildRule = new BuildFileRule();
@Before
public void setUp() {
configureProject("build.xml");
}
@Test
public void testSimple() {
buildRule.executeTarget("useSimple");
Assert.assertEquals("test-value", buildRule.getLog());
}
}
一切正常。
Ant 提供了一种捆绑文件的通用方法:文件集。因为您正在阅读本教程,我认为您了解它们,我不必花更多时间解释它们在构建文件中的用法。我们的目标是在路径中搜索文件。在此步骤中,路径只是一个文件集(或更准确地说:文件集的集合)。因此,我们的用法将是
<find file="ant.jar" location="location.ant-jar">
<fileset dir="${ant.home}" includes="**/*.jar"/>
</find>
我们需要什么?一个具有两个属性(file、location)和嵌套文件集的任务。因为我们已经在上面的示例中解释了属性处理,并且在另一个教程中描述了嵌套元素的处理,所以代码应该非常容易
public class Find extends Task {
private String file;
private String location;
private List<FileSet> filesets = new ArrayList<>();
public void setFile(String file) {
this.file = file;
}
public void setLocation(String location) {
this.location = location;
}
public void addFileset(FileSet fileset) {
filesets.add(fileset);
}
public void execute() {
}
}
好的 - 该任务不会做太多事情,但我们可以在没有错误的情况下以描述的方式使用它。在下一步中,我们必须实现 execute 方法。在此之前,我们将实现相应的测试用例(TDD - 测试驱动开发)。
在另一个教程中,我们重复使用了构建文件已经编写的目标。现在,我们将通过 Java 代码配置大多数测试用例(有时编写目标比通过 Java 代码编写更容易)。可以测试什么?
也许您会找到更多测试用例。但现在这些就足够了。
对于每个点,我们创建一个 testXX 方法。
public class FindTest {
@Rule
public final BuildFileRule buildRule = new BuildFileRule();
@Rule
public ExpectedException tried = ExpectedException.none();
... // constructor, setUp as above
@Test
public void testMissingFile() {
tried.expect(BuildException.class);
tried.expectMessage("file not set");
Find find = new Find();
find.execute();
}
@Test
public void testMissingLocation() {
tried.expect(BuildException.class);
tried.expectMessage("location not set");
Find find = new Find();
find.setFile("ant.jar");
find.execute();
}
@Test
public void testMissingFileset() {
tried.expect(BuildException.class);
tried.expectMessage("fileset not set");
Find find = new Find();
find.setFile("ant.jar");
find.setLocation("location.ant-jar");
}
@Test
public void testFileNotPresent() {
buildRule.executeTarget("testFileNotPresent");
String result = buildRule.getProject().getProperty("location.ant-jar");
assertNull("Property set to wrong value.", result);
}
@Test
public void testFilePresent() {
buildRule.executeTarget("testFilePresent");
String result = buildRule.getProject().getProperty("location.ant-jar");
assertNotNull("Property not set.", result);
assertTrue("Wrong file found.", result.endsWith("ant.jar"));
}
}
如果我们运行此测试类,所有测试用例(除了 testFileNotPresent)都会失败。现在,我们可以实现我们的任务,以便这些测试用例通过。
protected void validate() {
if (file == null) throw new BuildException("file not set");
if (location == null) throw new BuildException("location not set");
if (filesets.size() < 1) throw new BuildException("fileset not set");
}
public void execute() {
validate(); // 1
String foundLocation = null;
for (FileSet fs : filesets) { // 2
DirectoryScanner ds = fs.getDirectoryScanner(getProject()); // 3
for (String includedFile : ds.getIncludedFiles()) {
String filename = includedFile.replace('\\','/'); // 4
filename = filename.substring(filename.lastIndexOf("/") + 1);
if (foundLocation == null && file.equals(filename)) {
File base = ds.getBasedir(); // 5
File found = new File(base, includedFile);
foundLocation = found.getAbsolutePath();
}
}
}
if (foundLocation != null) // 6
getProject().setNewProperty(location, foundLocation);
}
在 //1 上,我们检查任务的前提条件。在 validate() 方法中执行此操作是一种常见方法,因为我们将前提条件与实际工作分开。在 //2 上,我们遍历所有嵌套文件集。如果我们不想处理多个文件集,则 addFileset() 方法必须拒绝进一步的调用。我们可以通过其 DirectoryScanner 获取文件集的结果,如 //3 中所示。之后,我们创建文件路径的平台无关字符串表示(//4,当然可以用其他方法完成)。我们必须执行 replace(),因为我们使用的是简单的字符串比较。Ant 本身是平台无关的,因此可以在使用斜杠 (/
,例如 Linux) 或反斜杠 (\
,例如 Windows) 作为路径分隔符的文件系统上运行。因此,我们必须统一这一点。如果我们找到我们的文件,我们将在 //5 上创建一个绝对路径表示,以便我们可以在不知道 basedir 的情况下使用这些信息。(这在使用多个文件集时非常重要,因为它们可能具有不同的 basedir,并且目录扫描器的返回值相对于其 basedir。)最后,如果我们找到一个文件,我们将文件的路径存储为属性(//6)。
好的,在这个简单的情况下,更容易的做法是将 file 添加为所有文件集的附加 include 元素。但我希望展示如何处理复杂情况而不变得复杂 :-)
测试用例使用 Ant 属性 ant.home 作为参考。此属性由启动 Ant 的 Launcher 类设置。我们可以在构建文件中使用它作为内置属性 [3]。但是,如果我们创建一个新的 Ant 环境,我们必须为自己的环境设置该值。我们使用 fork 模式下的 <junit> 任务。因此,我们必须修改构建文件
<target name="junit" description="Runs the unit tests" depends="jar">
<delete dir="${junit.out.dir.xml}"/>
<mkdir dir="${junit.out.dir.xml}"/>
<junit printsummary="yes" haltonfailure="no">
<classpath refid="classpath.test"/>
<sysproperty key="ant.home" value="${ant.home}"/>
<formatter type="xml"/>
<batchtest fork="yes" todir="${junit.out.dir.xml}">
<fileset dir="${src.dir}" includes="**/*Test.java"/>
</batchtest>
</junit>
</target>
提供对文件集的支持的任务非常方便。但还有另一种捆绑文件的方法:<path>。如果所有文件都在一个公共基目录下,则文件集很容易。但如果不是这种情况,您就会遇到问题。另一个缺点是它的速度:如果您在一个巨大的目录结构中只有几个文件,为什么不使用 <filelist> 而不是?<path> 以这种方式组合这些数据类型,即路径包含其他路径、文件集、目录集和文件列表。这就是为什么Ant-Contrib [4] <foreach> 任务被修改为支持路径而不是文件集。所以我们也想要这样。
从文件集更改为路径支持非常容易
将 Java 代码从 private List<FileSet> filesets = new ArrayList<>();
public void addFileset(FileSet fileset) {
filesets.add(fileset);
}
更改为
private List<Path> paths = new ArrayList<>(); *1
public void addPath(Path path) { *2
paths.add(path);
}
并将构建文件从
<find file="ant.jar" location="location.ant-jar">
<fileset dir="${ant.home}" includes="**/*.jar"/>
</find>
更改为
<find file="ant.jar" location="location.ant-jar">
<path> *3
<fileset dir="${ant.home}" includes="**/*.jar"/>
</path>
</find>
更改为
在 *1 上,我们只重命名列表。这只是为了更好地阅读源代码。在 *2 上,我们必须提供正确的方法:addName(Type t)。因此,在此处将文件集替换为路径。最后,我们必须修改 *3 上的构建文件,因为我们的任务不再支持嵌套文件集。因此,我们将文件集包装在路径中。
现在我们修改测试用例。哦,没什么好做的 :-) 重命名 testMissingFileset()(不是必须的,但最好将其命名为它所做的事情)并在该方法中更新 expected-String(现在期望 path not set 消息)。更复杂的测试用例基于构建脚本。因此,目标 testFileNotPresent 和 testFilePresent 必须以上面描述的方式进行修改。
public void execute() {
validate();
String foundLocation = null;
for (Path path : paths) { // 1
for (String includedFile : path.list()) { // 2
String filename = includedFile.replace('\\','/');
filename = filename.substring(filename.lastIndexOf("/") + 1);
if (foundLocation == null && file.equals(filename)) {
foundLocation = includedFile; // 3
}
}
}
if (foundLocation != null)
getProject().setNewProperty(location, foundLocation);
}
测试已完成。现在我们必须调整任务实现。最简单的修改是在 validate() 方法中,我们将最后一行更改为 if (paths.size()<1) throw new BuildException("path not set");。在 execute() 方法中,我们还有更多工作要做。... 嗯... 实际上工作量更少,因为 Path 类为我们完成了所有 DirectoryScanner 处理和创建绝对路径的工作。因此,execute 方法变为
当然,我们必须在 //1 上遍历路径。在 //2 和 //3 上,我们看到 Path 类为我们完成了工作:没有 DirectoryScanner(在 2 处)并且没有创建绝对路径(在 3 处)。
到目前为止,一切都很好。但是,文件可能在路径中的多个位置吗?- 当然。
获取所有这些文件会好吗?- 这取决于...
在本节中,我们将扩展该任务以支持返回所有文件的列表。Ant 本身不支持将列表作为属性值。因此,我们必须看看其他任务如何使用列表。使用列表的最著名任务是 Ant-Contrib 的 <foreach>。所有列表元素都连接在一起,并用可自定义的分隔符(默认值为 ,
)分隔。
<find ... delimiter=""/> ... </find>
因此,我们执行以下操作
如果设置了分隔符,我们将以该分隔符返回所有找到的文件作为列表。
返回该列表
因此,我们添加以下测试用例 <target name="test.init">
<mkdir dir="test1/dir11/dir111"/> *1
<mkdir dir="test1/dir11/dir112"/>
...
<touch file="test1/dir11/dir111/test"/>
<touch file="test1/dir11/dir111/not"/>
...
<touch file="test1/dir13/dir131/not2"/>
<touch file="test1/dir13/dir132/test"/>
<touch file="test1/dir13/dir132/not"/>
<touch file="test1/dir13/dir132/not2"/>
<mkdir dir="test2"/>
<copy todir="test2"> *2
<fileset dir="test1"/>
</copy>
</target>
<target name="testMultipleFiles" depends="use.init,test.init"> *3
<find file="test" location="location.test" delimiter=";">
<path>
<fileset dir="test1"/>
<fileset dir="test2"/>
</path>
</find>
<delete> *4
<fileset dir="test1"/>
<fileset dir="test2"/>
</delete>
</target>
在构建文件中
public void testMultipleFiles() {
executeTarget("testMultipleFiles");
String result = getProject().getProperty("location.test");
assertNotNull("Property not set.", result);
assertTrue("Only one file found.", result.indexOf(";") > -1);
}
在测试类中
现在,我们需要一个目录结构,我们可以在其中找到不同目录中具有相同名称的文件。因为我们不能确定是否有一个,所以我们在 *1 和 *2 上创建一个。当然,我们在 *4 上清理它。可以在我们的测试目标中或在单独的目标中创建它,这对于以后的重复使用会更好(*3)。
private List<String> foundFiles = new ArrayList<>();
...
private String delimiter = null;
...
public void setDelimiter(String delim) {
delimiter = delim;
}
...
public void execute() {
validate();
// find all files
for (Path path : paths) {
for (File includedFile : path.list()) {
String filename = includedFile.replace('\\','/');
filename = filename.substring(filename.lastIndexOf("/")+1);
if (file.equals(filename) && !foundFiles.contains(includedFile)) { // 1
foundFiles.add(includedFile);
}
}
}
// create the return value (list/single)
String rv = null;
if (!foundFiles.isEmpty()) { // 2
if (delimiter == null) {
// only the first
rv = foundFiles.get(0);
} else {
// create list
StringBuilder list = new StringBuilder();
for (String file : foundFiles) { // 3
list.append(it.next());
if (list.length() > 0) list.append(delimiter); // 4
}
rv = list.toString();
}
}
// create the property
if (rv != null)
getProject().setNewProperty(location, rv);
}
任务实现如下修改
该算法执行以下操作:查找所有文件,根据用户的意愿创建返回值,将值作为属性返回。在 //1 上,我们消除了重复项。//2 确保我们仅在找到一个文件时才创建返回值。在 //3 上,我们遍历所有找到的文件,并且 //4 确保最后一个条目没有尾随分隔符。
如果只有任务开发者能够编写构建文件(而且他只有在接下来的几周内才能做到 :-)),那么这个任务就毫无用处。因此,文档也很重要。你用什么形式来做取决于你的喜好。但在 Ant 内部有一个通用格式,使用它有很多好处:所有任务用户都知道这种格式,如果你决定贡献你的任务,就会要求使用这种格式。所以我们将以这种形式来记录我们的任务。
如果你看一下 Java 任务 [5] 的手册页,你会发现它
作为模板,我们有
<!DOCTYPE html>
<html lang="en">
<head>
<title>Taskname Task</title>
</head>
<body>
<h2 id="taskname">Taskname</h2>
<h3>Description</h3>
<p>Describe the task.</p>
<h3>Parameters</h3>
<table class="attr">
<tr>
<th scope="col">Attribute</th>
<th scope="col">Description</th>
<th scope="col">Required</th>
</tr>
do this html row for each attribute (including inherited attributes)
<tr>
<td>classname</td>
<td>the Java class to execute.</td>
<td>Either jar or classname</td>
</tr>
</table>
<h3>Parameters specified as nested elements</h3>
Describe each nested element (including inherited)
<h4>your nested element</h4>
<p>description</p>
<p><em>since Ant 1.6</em>.</p>
<h3>Examples</h3>
<pre>
A code sample; don't forget to escape the < of the tags with <
</pre>
What should that example do?
</body>
</html>
这是一个关于我们任务的示例文档页面
<!DOCTYPE html>
<html lang="en">
<head>
<title>Find Task</title>
</head>
<body>
<h2 id="find">Find</h2>
<h3>Description</h3>
<p>Searches in a given path for a file and returns the absolute to it as property.
If delimiter is set this task returns all found locations.</p>
<h3>Parameters</h3>
<table class="attr">
<tr>
<th scope="col">Attribute</th>
<th scope="col">Description</th>
<th scope="col">Required</th>
</tr>
<tr>
<td>file</td>
<td>The name of the file to search.</td>
<td>yes</td>
</tr>
<tr>
<td>location</td>
<td>The name of the property where to store the location</td>
<td>yes</td>
</tr>
<tr>
<td>delimiter</td>
<td>A delimiter to use when returning the list</td>
<td>only if the list is required</td>
</tr>
</table>
<h3>Parameters specified as nested elements</h3>
<h4>path</h4>
<p>The path where to search the file.</p>
<h3>Examples</h3>
<pre>
<find file="ant.jar" location="loc">
<path>
<fileset dir="${ant.home}"/>
<path>
</find></pre>
Searches in Ant's home directory for a file <samp>ant.jar</samp> and stores its location in
property <code>loc</code> (should be <samp>ANT_HOME/bin/ant.jar</samp>).
<pre>
<find file="ant.jar" location="loc" delimiter=";">
<path>
<fileset dir="C:/"/>
<path>
</find>
<echo>ant.jar found in: ${loc}</echo></pre>
Searches in Windows C: drive for all <samp>ant.jar</samp> and stores their locations in
property <code>loc</code> delimited with <q>;</q>. (should need a long time :-)
After that it prints out the result (e.g. <samp>C:/ant-1.5.4/bin/ant.jar;C:/ant-1.6/bin/ant.jar</samp>).
</body>
</html>
如果我们决定贡献我们的任务,我们应该做一些事情
有关此方面的更多信息,请参阅 Ant 任务指南 [6]。
现在我们将检查该指南中描述的“提交新任务前的清单”。
此任务不依赖于任何外部库。因此,我们可以将其用作核心任务。此任务只包含一个类。因此,我们可以使用核心任务的标准包:org.apache.tools.ant.taskdefs。实现位于 src/main 目录中,测试位于 src/testcases 目录中,测试的构建文件位于 src/etc/testcases 目录中。
现在我们将我们的工作集成到 Ant 发行版中。因此,首先我们要更新我们的 Git 树。如果还没有完成,你应该在 GitHub[7] 上克隆 Ant 仓库,然后创建一个本地克隆
git clone https://github.com/your-sig/ant.git
现在我们将构建我们的 Ant 发行版并进行测试。这样我们就可以看到我们的机器上是否有任何测试失败。(我们可以在后面的步骤中忽略这些失败的测试;这里使用的是 Windows 语法——如果需要,请将其转换为 UNIX 语法)
ANTREPO> build // 1 ANTREPO> set ANT_HOME=%CD%\dist // 2 ANTREPO> ant test -Dtest.haltonfailure=false // 3
首先,我们必须构建我们的 Ant 发行版(//1)。在 //2 上,我们将 ANT_HOME 环境变量设置为新创建的发行版所在的目录(%CD% 在 Windows 2000 及更高版本上扩展为当前目录)。在 //3 上,我们让 Ant 执行所有测试(这会强制编译所有测试),而不会在第一次失败时停止。
接下来,我们将我们的工作应用到 Ant 源代码上。因为我们没有修改任何内容,所以这是一个相对简单的步骤。(因为我有一个 Ant 的本地 Git 克隆,并且通常会贡献我的工作,所以我从一开始就在本地副本上工作。优点是:如果你修改了现有的源代码,则不需要此步骤,并且可以节省大量工作 :-))。
package org.apache.tools.ant.taskdefs;testFileNotPresent、
testFilePresent、
test.init和
testMultipleFiles
use.init的依赖项
configureProject("build.xml"); 更改为 configureProject("src/etc/testcases/taskdefs/find.xml");<a href="Tasks/find.html">Find</a><br>现在我们的修改已经完成,我们将重新测试它
ANTREPO> build
ANTREPO> ant run-single-test // 1
-Dtestcase=org.apache.tools.ant.taskdefs.FindTest // 2
-Dtest.haltonfailure=false
因为我们只想测试我们的新类,所以我们使用单个测试的目标,指定要使用的测试,并配置为不在第一次失败时停止——我们希望看到我们自己的测试的所有失败(//1 + 2)。
然后……哦,所有测试都失败了:Ant 找不到任务或此任务依赖的类。
好的:在前面的步骤中,我们告诉 Ant 使用 Find 类来执行 <find> 任务(请记住 use.init
目标中的 <taskdef> 语句)。但现在我们想将该任务作为核心任务引入。没有人想要 taskdef javac、echo……那么该怎么办?答案是 src/main/.../taskdefs/default.properties。这里完成了任务名称和实现类之间的映射。因此,我们将 find=org.apache.tools.ant.taskdefs.Find 添加为最后一个核心任务(就在 # optional tasks 行之前)。现在我们再试一次
ANTREPO> build // 1
ANTREPO> ant run-single-test
-Dtestcase=org.apache.tools.ant.taskdefs.FindTest
-Dtest.haltonfailure=false
我们必须重新构建(//1)Ant,因为测试会在 %ANT_HOME%\lib\ant.jar(更准确地说:在类路径上)中查找属性文件。而我们只在源路径中修改了它。因此,我们必须重新构建该 jar。但现在所有测试都通过了,我们将检查我们的类是否破坏了其他一些测试。
ANTREPO> ant test -Dtest.haltonfailure=false
因为有很多测试,所以这一步需要一些时间。因此,在开发过程中使用 run-single-test
,并在最后(可能在开发过程中也会偶尔使用)使用 test
。我们在这里使用 -Dtest.haltonfailure=false,因为可能会有其他测试失败,我们需要查看它们。
此测试运行应该向我们展示两件事:我们的测试将运行,并且失败的测试数量与直接在 git clone 之后(没有我们的修改)相同。
只需从 Ant 源代码树中的其他源代码中复制许可证文本。
Ant 1.10 使用 Java 8 进行开发,但 Ant 1.9 也在积极维护。这意味着 Ant 1.9 中存在的 Ant 代码更新必须能够在 JDK 5 上运行。(对于新的任务,只针对 Ant 1.10 及更高版本进行处理是可以的。)因此,我们必须对其进行测试。你可以从 Oracle [8] 下载旧版本的 JDK。
清理 ANT_HOME 变量,删除 build、bootstrap 和 dist 目录,并将 JAVA_HOME 指向 JDK 5 主目录。然后使用你的提交创建补丁,在 Git 中检出 1.9.x 分支,应用你的补丁并执行 build,设置 ANT_HOME 并运行 ant test(如上所述)。
我们的测试应该通过。
我们必须确保很多事情。缩进使用 4 个空格,这里和那里有空格……(所有这些都在 Ant 任务指南 [6] 中描述,其中包括 Sun 代码风格 [9])。因为有很多事情,所以我们很乐意有一个工具来执行检查。有一个工具:checkstyle。Checkstyle 可在 Sourceforge [10] 上获得,Ant 提供了 check.xml 构建文件,它可以为我们完成这项工作。
下载它并将 checkstyle-*-all.jar 放入你的 %USERPROFILE%\.ant\lib 目录中。存储在那里的所有 jar 都可供 Ant 使用,因此你无需将其添加到你的 %ANT_HOME%\lib 目录中(此功能自 Ant 1.6 起可用)。
因此,我们将使用以下命令运行测试
ANTREPO> ant -f check.xml checkstyle htmlreport
我更喜欢 HTML 报告,因为有很多消息,我们可以更快地浏览。打开 ANTREPO/build/reports/checkstyle/html/index.html 并导航到 Find.java。现在我们看到有一些错误:缺少空格、未使用的导入、缺少 javadoc。因此,我们必须这样做。
提示:从文件的底部开始,这样报告中的行号将保持最新,你将更容易找到下一个错误位置,而无需重新执行 checkstyle。
根据消息清理代码后,我们删除 reports 目录并再次运行 checkstyle。现在我们的任务没有列出。这很好 :-)
最后,我们发布该存档。如 Ant 任务指南 [7] 中所述,我们可以在开发者邮件列表中宣布它,创建一个 BugZilla 条目并打开一个 GitHub 拉取请求。对于两者,我们都需要一些信息
| 主题 | 简短描述 | 用于在路径中查找文件的任务 |
|---|---|---|
| 正文 | 有关路径的更多详细信息 | 此新任务在嵌套的 <path/> 中查找文件出现的位置,并将所有位置存储为属性。有关详细信息,请参阅附带的手册。 |
| 拉取请求引用 | GitHub 拉取请求 URL | https://github.com/apache/ant/pull/0 |
发送包含此信息的电子邮件非常容易,我认为我不需要描述它。BugZilla 稍微复杂一些。但优点是条目不会被遗忘(每周都会生成一次报告)。因此,我将描述该过程。
首先,您需要拥有一个 BugZilla 帐户。因此,请打开 BugZilla 主页 [11] 并按照链接 创建新的 Bugzilla 帐户 [12] 中的步骤操作,如果您还没有帐户的话。
现在新的任务已在错误数据库中注册。