2018-12-21 11:20:43 +01:00
|
|
|
<!DOCTYPE html>
|
2006-09-11 04:19:00 +00:00
|
|
|
<!--
|
|
|
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
|
|
|
contributor license agreements. See the NOTICE file distributed with
|
|
|
|
this work for additional information regarding copyright ownership.
|
|
|
|
The ASF licenses this file to You under the Apache License, Version 2.0
|
|
|
|
(the "License"); you may not use this file except in compliance with
|
|
|
|
the License. You may obtain a copy of the License at
|
|
|
|
|
2019-05-25 13:41:47 +02:00
|
|
|
https://www.apache.org/licenses/LICENSE-2.0
|
2006-09-11 04:19:00 +00:00
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
-->
|
2018-12-21 11:20:43 +01:00
|
|
|
<html lang="en">
|
2003-09-25 17:39:53 +00:00
|
|
|
<head>
|
2005-04-29 18:58:16 +00:00
|
|
|
<link rel="stylesheet" type="text/css" href="stylesheets/style.css">
|
2007-05-02 06:56:16 +00:00
|
|
|
<title>Tutorial: Tasks using Properties, Filesets & Paths</title>
|
2003-09-25 17:39:53 +00:00
|
|
|
</head>
|
|
|
|
<body>
|
2005-03-18 09:57:28 +00:00
|
|
|
<h1>Tutorial: Tasks using Properties, Filesets & Paths</h1>
|
2003-09-25 17:39:53 +00:00
|
|
|
|
2018-02-28 07:58:59 +01:00
|
|
|
<p>After reading the tutorial about <a href="tutorial-writing-tasks.html">writing tasks [1]</a> this tutorial explains
|
|
|
|
how to get and set properties and how to use nested filesets and paths. Finally it explains how to contribute tasks to
|
|
|
|
Apache Ant.</p>
|
2003-09-25 17:39:53 +00:00
|
|
|
|
|
|
|
<h2>Content</h2>
|
2018-02-08 22:52:33 +01:00
|
|
|
<ul>
|
2003-09-29 14:30:17 +00:00
|
|
|
<li><a href="#goal">The goal</a></li>
|
|
|
|
<li><a href="#buildenvironment">Build environment</a></li>
|
|
|
|
<li><a href="#propertyaccess">Property access</a></li>
|
|
|
|
<li><a href="#filesets">Using filesets</a></li>
|
|
|
|
<li><a href="#path">Using nested paths</a></li>
|
|
|
|
<li><a href="#returning-list">Returning a list</a></li>
|
|
|
|
<li><a href="#documentation">Documentation</a></li>
|
|
|
|
<li><a href="#contribute">Contribute the new task</a></li>
|
|
|
|
<li><a href="#resources">Resources</a></li>
|
2018-02-08 22:52:33 +01:00
|
|
|
</ul>
|
2003-09-25 17:39:53 +00:00
|
|
|
|
2018-02-08 22:52:33 +01:00
|
|
|
<h2 id="goal">The goal</h2>
|
2018-02-28 07:58:59 +01:00
|
|
|
<p>The goal is to write a task, which searches in a path for a file and saves the location of that file in a
|
|
|
|
property.</p>
|
2003-09-25 17:39:53 +00:00
|
|
|
|
2018-02-08 22:52:33 +01:00
|
|
|
<h2 id="buildenvironment">Build environment</h2>
|
2018-02-28 07:58:59 +01:00
|
|
|
<p>We can use the buildfile from the other tutorial and modify it a little bit. That's the advantage of using
|
|
|
|
properties—we can reuse nearly the whole script. :-)</p>
|
2018-03-10 20:17:33 +01:00
|
|
|
<pre>
|
2018-02-28 07:58:59 +01:00
|
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
2004-11-16 08:12:35 +00:00
|
|
|
<project name="<b>FindTask</b>" basedir="." default="test">
|
2003-09-25 17:39:53 +00:00
|
|
|
...
|
2005-03-07 18:33:59 +00:00
|
|
|
<target name="use.init" description="Taskdef's the <b>Find</b>-Task" depends="jar">
|
2004-11-16 08:12:35 +00:00
|
|
|
<taskdef name="<b>find</b>" classname="<b>Find</b>" classpath="${ant.project.name}.jar"/>
|
|
|
|
</target>
|
2003-09-25 17:39:53 +00:00
|
|
|
|
|
|
|
<b><!-- the other use.* targets are deleted --></b>
|
|
|
|
...
|
2018-02-28 07:58:59 +01:00
|
|
|
</project></pre>
|
2003-09-25 17:39:53 +00:00
|
|
|
|
2018-02-28 07:58:59 +01:00
|
|
|
<p>The buildfile is in the
|
|
|
|
archive <a href="tutorial-tasks-filesets-properties.zip">tutorial-tasks-filesets-properties.zip [2]</a>
|
|
|
|
in <samp>/build.xml.01-propertyaccess</samp> (future version saved as *.02..., final version as <samp>build.xml</samp>;
|
|
|
|
same for sources).</p>
|
2003-09-29 14:30:17 +00:00
|
|
|
|
2018-02-08 22:52:33 +01:00
|
|
|
<h2 id="propertyaccess">Property access</h2>
|
2018-02-28 07:58:59 +01:00
|
|
|
<p>Our first step is to set a property to a value and print the value of that property. So
|
|
|
|
our scenario would be</p>
|
2018-03-10 20:17:33 +01:00
|
|
|
<pre>
|
2004-11-16 08:12:35 +00:00
|
|
|
<find property="test" value="test-value"/>
|
2018-02-28 07:58:59 +01:00
|
|
|
<find print="test"/></pre>
|
|
|
|
<p>Ok, it can be rewritten with the core tasks</p>
|
2018-03-10 20:17:33 +01:00
|
|
|
<pre>
|
2004-11-16 08:12:35 +00:00
|
|
|
<property name="test" value="test-value"/>
|
2018-02-28 07:58:59 +01:00
|
|
|
<echo message="${test}"/></pre>
|
2018-02-08 22:52:33 +01:00
|
|
|
<p>but I have to start on known ground :-)</p>
|
2018-02-28 07:58:59 +01:00
|
|
|
<p>So what to do? Handling three attributes (<var>property</var>, <var>value</var>, <var>print</var>) and an execute
|
|
|
|
method. Because this is only an introduction example I don't do much checking:</p>
|
2003-09-25 17:39:53 +00:00
|
|
|
|
2018-03-10 20:17:33 +01:00
|
|
|
<pre>
|
2003-09-25 17:39:53 +00:00
|
|
|
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 = <b>getProject().getProperty(print)</b>;
|
|
|
|
log(propValue);
|
|
|
|
} else {
|
|
|
|
if (property == null) throw new BuildException("property not set");
|
|
|
|
if (value == null) throw new BuildException("value not set");
|
|
|
|
<b>getProject().setNewProperty(property, value)</b>;
|
|
|
|
}
|
|
|
|
}
|
2018-02-28 07:58:59 +01:00
|
|
|
}</pre>
|
|
|
|
|
2018-03-10 20:17:33 +01:00
|
|
|
<p>As said in the other tutorial, the property access is done via <code class="code">Project</code> instance. We get
|
|
|
|
this instance via the public <code class="code">getProject()</code> method which we inherit
|
|
|
|
from <code class="code">Task</code> (more precisely from <code class="code">ProjectComponent</code>). Reading a property
|
|
|
|
is done via <code class="code">getProperty(<i>propertyname</i>)</code> (very simple, isn't it?). This property returns
|
|
|
|
the value as <code>String</code> or <code>null</code> if not set.<br/> Setting a property is ... not really difficult,
|
|
|
|
but there is more than one setter. You can use the <code class="code">setProperty()</code> method which will do the job
|
|
|
|
as expected. But there is a golden rule in Ant: <em>properties are immutable</em>. And this method sets the property to
|
|
|
|
the specified value—whether it has a value before that or not. So we use another
|
|
|
|
way. <code class="code">setNewProperty()</code> sets the property only if there is no property with that name. Otherwise
|
|
|
|
a message is logged.</p>
|
2003-09-25 17:39:53 +00:00
|
|
|
|
2018-02-28 20:15:10 +01:00
|
|
|
<p><em>(By the way, a short explanation of Ant's "namespaces"—not to be confused with XML namespaces:
|
2018-02-28 07:58:59 +01:00
|
|
|
an <code><antcall></code> creates a new space for property names. All properties from the caller are passed to the
|
|
|
|
callee, but the callee can set its own properties without notice by the caller.)</em></p>
|
|
|
|
|
|
|
|
<p>There are some other setters, too (but I haven't used them, so I can't say something to them, sorry :-)</p>
|
|
|
|
|
|
|
|
<p>After putting our two line example from above into a target names <code>use.simple</code> we can call that from our
|
|
|
|
test case:</p>
|
2003-09-25 17:39:53 +00:00
|
|
|
|
2018-03-10 20:17:33 +01:00
|
|
|
<pre>
|
2018-02-28 07:58:59 +01:00
|
|
|
import org.junit.Assert;
|
|
|
|
import org.junit.Before;
|
2014-04-18 21:00:38 +00:00
|
|
|
import org.junit.Rule;
|
|
|
|
import org.junit.Test;
|
|
|
|
import org.apache.tools.ant.BuildFileRule;
|
2003-09-25 17:39:53 +00:00
|
|
|
|
2014-04-18 21:00:38 +00:00
|
|
|
public class FindTest {
|
|
|
|
|
|
|
|
@Rule
|
|
|
|
public final BuildFileRule buildRule = new BuildFileRule();
|
2003-09-25 17:39:53 +00:00
|
|
|
|
2014-04-18 21:00:38 +00:00
|
|
|
@Before
|
2003-09-25 17:39:53 +00:00
|
|
|
public void setUp() {
|
|
|
|
configureProject("build.xml");
|
|
|
|
}
|
|
|
|
|
2014-04-18 21:00:38 +00:00
|
|
|
@Test
|
2003-09-25 17:39:53 +00:00
|
|
|
public void testSimple() {
|
2018-02-28 07:58:59 +01:00
|
|
|
buildRule.executeTarget("useSimple");
|
2014-04-18 21:00:38 +00:00
|
|
|
<b>Assert.assertEquals("test-value", buildRule.getLog());</b>
|
2003-09-25 17:39:53 +00:00
|
|
|
}
|
2018-02-28 07:58:59 +01:00
|
|
|
}</pre>
|
2003-09-25 17:39:53 +00:00
|
|
|
|
2018-02-08 22:52:33 +01:00
|
|
|
<p>and all works fine.</p>
|
2003-09-25 17:39:53 +00:00
|
|
|
|
2018-02-08 22:52:33 +01:00
|
|
|
<h2 id="filesets">Using filesets</h2>
|
2018-02-28 07:58:59 +01:00
|
|
|
<p>Ant provides a common way of bundling files: the fileset. Because you are reading this tutorial I think you know them
|
|
|
|
and I don't have to spend more explanations about their usage in buildfiles. Our goal is to search for a file in
|
|
|
|
path. And in this step the path is simply a fileset (or more precise: a collection of filesets). So our usage would
|
|
|
|
be</p>
|
2018-03-10 20:17:33 +01:00
|
|
|
<pre>
|
2004-11-16 08:12:35 +00:00
|
|
|
<find file="ant.jar" location="location.ant-jar">
|
|
|
|
<fileset dir="${ant.home}" includes="**/*.jar"/>
|
2018-02-28 07:58:59 +01:00
|
|
|
</find></pre>
|
2003-09-25 17:39:53 +00:00
|
|
|
|
2018-02-28 07:58:59 +01:00
|
|
|
<p>What do we need? A task with two attributes (<var>file</var>, <var>location</var>) and nested filesets. Because we
|
|
|
|
had attribute handling already explained in the example above and the handling of nested elements is described in the
|
|
|
|
other tutorial, the code should be very easy:</p>
|
2018-03-10 20:17:33 +01:00
|
|
|
<pre>
|
2003-09-25 17:39:53 +00:00
|
|
|
public class Find extends Task {
|
|
|
|
|
|
|
|
private String file;
|
|
|
|
private String location;
|
2018-02-28 07:58:59 +01:00
|
|
|
private List<FileSet> filesets = new ArrayList<>();
|
2003-09-25 17:39:53 +00:00
|
|
|
|
|
|
|
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() {
|
|
|
|
}
|
2018-02-28 07:58:59 +01:00
|
|
|
}</pre>
|
|
|
|
<p>Ok—that task wouldn't do very much, but we can use it in the described manner without failure. In the next step
|
|
|
|
we have to implement the execute method. And before that we will implement the appropriate test cases (TDD—test
|
|
|
|
driven development).</p>
|
|
|
|
|
|
|
|
<p>In the other tutorial we have reused the already written targets of our buildfile. Now we will configure most of the
|
|
|
|
test cases via Java code (sometimes it's much easier to write a target than doing it via Java coding). What can be
|
|
|
|
tested?</p>
|
2018-02-08 22:52:33 +01:00
|
|
|
<ul>
|
2018-02-28 07:58:59 +01:00
|
|
|
<li>invalid configuration of the task (missing file, missing location, missing fileset)</li>
|
2005-03-07 18:33:59 +00:00
|
|
|
<li>don't find a present file</li>
|
|
|
|
<li>behaviour if file can't be found</li>
|
2003-09-25 17:39:53 +00:00
|
|
|
</ul>
|
2018-03-10 20:17:33 +01:00
|
|
|
<p>Maybe you find some more test cases. But this is enough for now.<br/> For each of these points we create
|
|
|
|
a <code class="code">testXX</code> method.</p>
|
2003-09-25 17:39:53 +00:00
|
|
|
|
2018-03-10 20:17:33 +01:00
|
|
|
<pre>
|
2014-04-18 21:00:38 +00:00
|
|
|
public class FindTest {
|
|
|
|
|
|
|
|
@Rule
|
|
|
|
public final BuildFileRule buildRule = new BuildFileRule();
|
2003-09-25 17:39:53 +00:00
|
|
|
|
2018-02-28 07:58:59 +01:00
|
|
|
@Rule
|
|
|
|
public ExpectedException tried = ExpectedException.none();
|
|
|
|
|
2003-09-25 17:39:53 +00:00
|
|
|
... // constructor, setUp as above
|
|
|
|
|
2014-04-18 21:00:38 +00:00
|
|
|
@Test
|
2003-09-25 17:39:53 +00:00
|
|
|
public void testMissingFile() {
|
2018-02-28 07:58:59 +01:00
|
|
|
tried.expect(BuildException.class);
|
|
|
|
tried.expectMessage("file not set");
|
2003-09-25 17:39:53 +00:00
|
|
|
<b>Find find = new Find();</b>
|
2018-02-28 07:58:59 +01:00
|
|
|
<b>find.execute();</b>
|
2003-09-25 17:39:53 +00:00
|
|
|
}
|
|
|
|
|
2014-04-18 21:00:38 +00:00
|
|
|
@Test
|
2003-09-25 17:39:53 +00:00
|
|
|
public void testMissingLocation() {
|
2018-02-28 07:58:59 +01:00
|
|
|
tried.expect(BuildException.class);
|
|
|
|
tried.expectMessage("location not set");
|
2003-09-25 17:39:53 +00:00
|
|
|
Find find = new Find();
|
|
|
|
<b>find.setFile("ant.jar");</b>
|
2018-02-28 07:58:59 +01:00
|
|
|
find.execute();
|
2003-09-25 17:39:53 +00:00
|
|
|
}
|
|
|
|
|
2014-04-18 21:00:38 +00:00
|
|
|
@Test
|
2003-09-25 17:39:53 +00:00
|
|
|
public void testMissingFileset() {
|
2018-02-28 07:58:59 +01:00
|
|
|
tried.expect(BuildException.class);
|
|
|
|
tried.expectMessage("fileset not set");
|
2003-09-25 17:39:53 +00:00
|
|
|
Find find = new Find();
|
|
|
|
find.setFile("ant.jar");
|
|
|
|
find.setLocation("location.ant-jar");
|
|
|
|
}
|
|
|
|
|
2014-04-18 21:00:38 +00:00
|
|
|
@Test
|
2003-09-25 17:39:53 +00:00
|
|
|
public void testFileNotPresent() {
|
2014-04-18 21:00:38 +00:00
|
|
|
buildRule.executeTarget("testFileNotPresent");
|
|
|
|
String result = buildRule.getProject().getProperty("location.ant-jar");
|
2003-09-25 17:39:53 +00:00
|
|
|
assertNull("Property set to wrong value.", result);
|
|
|
|
}
|
|
|
|
|
2014-04-18 21:00:38 +00:00
|
|
|
@Test
|
2003-09-25 17:39:53 +00:00
|
|
|
public void testFilePresent() {
|
2014-04-18 21:00:38 +00:00
|
|
|
buildRule.executeTarget("testFilePresent");
|
|
|
|
String result = buildRule.getProject().getProperty("location.ant-jar");
|
2003-09-25 17:39:53 +00:00
|
|
|
assertNotNull("Property not set.", result);
|
|
|
|
assertTrue("Wrong file found.", result.endsWith("ant.jar"));
|
|
|
|
}
|
2018-02-28 07:58:59 +01:00
|
|
|
}</pre>
|
2003-09-25 17:39:53 +00:00
|
|
|
|
2018-03-10 20:17:33 +01:00
|
|
|
<p>If we run this test class all test cases (except <code class="code">testFileNotPresent</code>) fail. Now we can
|
|
|
|
implement our task, so that these test cases will pass.</p>
|
2003-09-25 17:39:53 +00:00
|
|
|
|
2018-03-10 20:17:33 +01:00
|
|
|
<pre>
|
2003-09-25 17:39:53 +00:00
|
|
|
protected void validate() {
|
2018-02-28 07:58:59 +01:00
|
|
|
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");
|
2003-09-25 17:39:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void execute() {
|
|
|
|
validate(); // 1
|
|
|
|
String foundLocation = null;
|
2018-02-28 07:58:59 +01:00
|
|
|
for (FileSet fs : filesets) { // 2
|
2003-09-25 17:39:53 +00:00
|
|
|
DirectoryScanner ds = fs.getDirectoryScanner(getProject()); // 3
|
2018-02-28 07:58:59 +01:00
|
|
|
for (String includedFile : ds.getIncludedFiles()) {
|
|
|
|
String filename = includedFile.replace('\\','/'); // 4
|
|
|
|
filename = filename.substring(filename.lastIndexOf("/") + 1);
|
|
|
|
if (foundLocation == null && file.equals(filename)) {
|
2003-09-25 17:39:53 +00:00
|
|
|
File base = ds.getBasedir(); // 5
|
2018-02-28 07:58:59 +01:00
|
|
|
File found = new File(base, includedFile);
|
2003-09-25 17:39:53 +00:00
|
|
|
foundLocation = found.getAbsolutePath();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-02-28 07:58:59 +01:00
|
|
|
if (foundLocation != null) // 6
|
2003-09-25 17:39:53 +00:00
|
|
|
getProject().setNewProperty(location, foundLocation);
|
2018-02-28 07:58:59 +01:00
|
|
|
}</pre>
|
|
|
|
|
2018-03-10 20:17:33 +01:00
|
|
|
<p>On <strong>//1</strong> we check the prerequisites for our task. Doing that in a <code class="code">validate()</code>
|
|
|
|
method is a common way, because we separate the prerequisites from the real work. On <strong>//2</strong> we iterate
|
|
|
|
over all nested filesets. If we don't want to handle multiple filesets, the <code class="code">addFileset()</code>
|
|
|
|
method has to reject the further calls. We can get the result of a fileset via
|
|
|
|
its <code class="code">DirectoryScanner</code> like done in <strong>//3</strong>. After that we create a platform
|
|
|
|
independent String representation of the file path (<strong>//4</strong>, can be done in other ways of course). We have
|
|
|
|
to do the <code class="code">replace()</code>, because we work with a simple string comparison. Ant itself is platform
|
|
|
|
independent and can therefore run on filesystems with slash (<q>/</q>, e.g. Linux) or backslash (<q>\</q>, e.g. Windows)
|
|
|
|
as path separator. Therefore we have to unify that. If we find our file, we create an absolute path representation
|
|
|
|
on <strong>//5</strong>, so that we can use that information without knowing the <var>basedir</var>. (This is very
|
|
|
|
important on use with multiple filesets, because they can have different <var>basedir</var>s and the return value of the
|
|
|
|
directory scanner is relative to its <var>basedir</var>.) Finally we store the location of the file as property, if we
|
|
|
|
had found one (<strong>//6</strong>).</p>
|
2018-02-28 07:58:59 +01:00
|
|
|
|
|
|
|
<p>Ok, much more easier in this simple case would be to add the <var>file</var> as additional <code>include</code>
|
|
|
|
element to all filesets. But I wanted to show how to handle complex situations without being complex :-)</p>
|
|
|
|
|
|
|
|
<p>The test case uses the Ant property <code>ant.home</code> as reference. This property is set by
|
2018-03-10 20:17:33 +01:00
|
|
|
the <code class="code">Launcher</code> class which starts ant. We can use that property in our buildfiles as
|
2018-02-28 07:58:59 +01:00
|
|
|
a <a href="properties.html#built-in-props">build-in property [3]</a>. But if we create a new Ant environment we have to
|
|
|
|
set that value for our own. And we use the <code><junit></code> task in <var>fork</var> mode. Therefore we have
|
|
|
|
do modify our buildfile:</p>
|
2018-03-10 20:17:33 +01:00
|
|
|
<pre>
|
2004-11-16 08:12:35 +00:00
|
|
|
<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"/>
|
|
|
|
<b><sysproperty key="ant.home" value="${ant.home}"/></b>
|
|
|
|
<formatter type="xml"/>
|
|
|
|
<batchtest fork="yes" todir="${junit.out.dir.xml}">
|
|
|
|
<fileset dir="${src.dir}" includes="**/*Test.java"/>
|
|
|
|
</batchtest>
|
|
|
|
</junit>
|
2018-02-28 07:58:59 +01:00
|
|
|
</target></pre>
|
2003-09-25 17:39:53 +00:00
|
|
|
|
2018-02-08 22:52:33 +01:00
|
|
|
<h2 id="path">Using nested paths</h2>
|
2018-02-28 07:58:59 +01:00
|
|
|
<p>A task providing support for filesets is a very comfortable one. But there is another possibility of bundling files:
|
|
|
|
the <code><path></code>. Filesets are easy if the files are all under a common base directory. But if this is not
|
|
|
|
the case, you have a problem. Another disadvantage is its speed: if you have only a few files in a huge directory
|
|
|
|
structure, why not use a <code><filelist></code> instead? <code><path></code>s combines these datatypes in
|
|
|
|
that way that a path contains other paths, filesets, dirsets and filelists. This is
|
2018-03-08 07:43:53 +01:00
|
|
|
why <a href="http://ant-contrib.sourceforge.net/" target="_top">Ant-Contrib [4]</a> <code><foreach></code> task is
|
|
|
|
modified to support paths instead of filesets. So we want that, too.</p>
|
2003-09-25 17:39:53 +00:00
|
|
|
|
|
|
|
<p>Changing from fileset to path support is very easy:</p>
|
2018-02-28 07:58:59 +01:00
|
|
|
<em><strong>Change Java code from:</strong></em>
|
2018-03-10 20:17:33 +01:00
|
|
|
<pre>
|
2018-02-28 07:58:59 +01:00
|
|
|
private List<FileSet> filesets = new ArrayList<>();
|
2003-09-25 17:39:53 +00:00
|
|
|
public void addFileset(FileSet fileset) {
|
|
|
|
filesets.add(fileset);
|
2018-02-28 07:58:59 +01:00
|
|
|
}</pre>
|
|
|
|
<em><strong>to:</strong></em>
|
2018-03-10 20:17:33 +01:00
|
|
|
<pre>
|
2018-02-28 07:58:59 +01:00
|
|
|
private List<Path> paths = new ArrayList<>(); *1
|
2003-09-25 17:39:53 +00:00
|
|
|
public void add<b>Path</b>(<b>Path</b> path) { *2
|
|
|
|
paths.add(path);
|
2018-02-28 07:58:59 +01:00
|
|
|
}</pre>
|
|
|
|
<em><strong>and build file from:</strong></em>
|
2018-03-10 20:17:33 +01:00
|
|
|
<pre>
|
2004-11-16 08:12:35 +00:00
|
|
|
<find file="ant.jar" location="location.ant-jar">
|
|
|
|
<fileset dir="${ant.home}" includes="**/*.jar"/>
|
2018-02-28 07:58:59 +01:00
|
|
|
</find></pre>
|
|
|
|
<em><strong>to:</strong></em>
|
2018-03-10 20:17:33 +01:00
|
|
|
<pre>
|
2004-11-16 08:12:35 +00:00
|
|
|
<find file="ant.jar" location="location.ant-jar">
|
|
|
|
<b><path></b> *3
|
|
|
|
<fileset dir="${ant.home}" includes="**/*.jar"/>
|
|
|
|
</path>
|
2018-02-28 07:58:59 +01:00
|
|
|
</find></pre>
|
|
|
|
<p>On <strong>*1</strong> we rename only the list. It's just for better reading the source. On <strong>*2</strong> we
|
|
|
|
have to provide the right method: an <code>add<i>Name</i>(<i>Type</i> t)</code>. Therefore replace the fileset with path
|
|
|
|
here. Finally we have to modify our buildfile on <strong>*3</strong> because our task doesn't support nested filesets
|
|
|
|
any longer. So we wrap the fileset inside a path.</p>
|
|
|
|
|
2018-03-10 20:17:33 +01:00
|
|
|
<p>And now we modify the test case. Oh, not very much to do :-) Renaming
|
|
|
|
the <code class="code">testMissingFileset()</code> (not really a <em>must-be</em> but better it's named like the thing
|
|
|
|
it does) and update the <var>expected</var>-String in that method (now a <samp>path not set</samp> message is
|
|
|
|
expected). The more complex test cases base on the build script. So the targets <var>testFileNotPresent</var>
|
|
|
|
and <var>testFilePresent</var> have to be modified in the manner described above.</p>
|
2018-02-28 07:58:59 +01:00
|
|
|
|
|
|
|
<p>The test are finished. Now we have to adapt the task implementation. The easiest modification is in
|
2018-03-10 20:17:33 +01:00
|
|
|
the <code class="code">validate()</code> method where we change the last line to <code class="code">if
|
|
|
|
(paths.size()<1) throw new BuildException("path not set");</code>. In the <code class="code">execute()</code> method
|
|
|
|
we have a little more work. ... mmmh ... in reality it's less work, because the <code class="code">Path</code> class
|
|
|
|
does the whole <code class="code">DirectoryScanner</code>-handling and creating-absolute-paths stuff for us. So the
|
|
|
|
execute method becomes just:</p>
|
2003-09-25 17:39:53 +00:00
|
|
|
|
2018-03-10 20:17:33 +01:00
|
|
|
<pre>
|
2003-09-25 17:39:53 +00:00
|
|
|
public void execute() {
|
|
|
|
validate();
|
|
|
|
String foundLocation = null;
|
2018-02-28 07:58:59 +01:00
|
|
|
for (Path path : paths) { // 1
|
|
|
|
for (String includedFile : <b>path.list()</b>) { // 2
|
|
|
|
String filename = includedFile.replace('\\','/');
|
|
|
|
filename = filename.substring(filename.lastIndexOf("/") + 1);
|
|
|
|
if (foundLocation == null && file.equals(filename)) {
|
|
|
|
<b>foundLocation = includedFile;</b> // 3
|
2003-09-25 17:39:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-02-28 07:58:59 +01:00
|
|
|
if (foundLocation != null)
|
2003-09-25 17:39:53 +00:00
|
|
|
getProject().setNewProperty(location, foundLocation);
|
|
|
|
}
|
|
|
|
</pre>
|
|
|
|
|
2018-02-28 07:58:59 +01:00
|
|
|
<p>Of course we have to iterate through paths on <strong>//1</strong>. On <strong>//2</strong> and <strong>//3</strong>
|
|
|
|
we see that the Path class does the work for us: no DirectoryScanner (was at 2) and no creating of the absolute path
|
|
|
|
(was at 3).</p>
|
2003-09-25 17:39:53 +00:00
|
|
|
|
2018-02-08 22:52:33 +01:00
|
|
|
<h2 id="returning-list">Returning a list</h2>
|
2018-02-28 07:58:59 +01:00
|
|
|
<p>So far so good. But could a file be on more than one place in the path?—Of course.<br/>
|
|
|
|
And would it be good to get all of them?—It depends ...<p>
|
2003-09-25 17:39:53 +00:00
|
|
|
|
2018-02-28 07:58:59 +01:00
|
|
|
<p>In this section we will extend that task to support returning a list of all files. Lists as property values are not
|
|
|
|
supported by Ant natively. So we have to see how other tasks use lists. The most famous task using lists is
|
2018-03-10 20:17:33 +01:00
|
|
|
Ant-Contrib's <code><foreach></code>. All list elements are concatenated and separated with a customizable
|
2018-02-28 07:58:59 +01:00
|
|
|
separator (default <q>,</q>).</p>
|
2003-09-25 17:39:53 +00:00
|
|
|
|
|
|
|
<p>So we do the following:</p>
|
|
|
|
|
2018-03-10 20:17:33 +01:00
|
|
|
<pre><find ... <b>delimiter=""</b>/> ... </find></pre>
|
2003-09-25 17:39:53 +00:00
|
|
|
|
2018-02-28 07:58:59 +01:00
|
|
|
<p>if the delimiter is set, we will return all found files as list with that delimiter.</p>
|
2003-09-25 17:39:53 +00:00
|
|
|
|
2018-02-08 22:52:33 +01:00
|
|
|
<p>Therefore we have to</p>
|
|
|
|
<ul>
|
2003-09-25 17:39:53 +00:00
|
|
|
<li>provide a new attribute</li>
|
|
|
|
<li>collect more than the first file</li>
|
|
|
|
<li>delete duplicates</li>
|
|
|
|
<li>create the list if necessary</li>
|
|
|
|
<li>return that list</li>
|
2018-02-08 22:52:33 +01:00
|
|
|
</ul>
|
2003-09-25 17:39:53 +00:00
|
|
|
|
2018-02-28 07:58:59 +01:00
|
|
|
<p>So we add as test case:</p>
|
|
|
|
<strong><em>in the buildfile:</em></strong>
|
2018-03-10 20:17:33 +01:00
|
|
|
<pre>
|
2004-11-16 08:12:35 +00:00
|
|
|
<target name="test.init">
|
|
|
|
<mkdir dir="test1/dir11/dir111"/> *1
|
|
|
|
<mkdir dir="test1/dir11/dir112"/>
|
2003-09-25 17:39:53 +00:00
|
|
|
...
|
2004-11-16 08:12:35 +00:00
|
|
|
<touch file="test1/dir11/dir111/test"/>
|
|
|
|
<touch file="test1/dir11/dir111/not"/>
|
2003-09-25 17:39:53 +00:00
|
|
|
...
|
2004-11-16 08:12:35 +00:00
|
|
|
<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,<b>test.init</b>"> *3
|
|
|
|
<find file="test" location="location.test" <b>delimiter=";"</b>>
|
|
|
|
<path>
|
|
|
|
<fileset dir="test1"/>
|
|
|
|
<fileset dir="test2"/>
|
|
|
|
</path>
|
|
|
|
</find>
|
|
|
|
<delete> *4
|
|
|
|
<fileset dir="test1"/>
|
|
|
|
<fileset dir="test2"/>
|
|
|
|
</delete>
|
2018-02-28 07:58:59 +01:00
|
|
|
</target></pre>
|
|
|
|
<strong><em>in the test class:</em></strong>
|
2018-03-10 20:17:33 +01:00
|
|
|
<pre>
|
2003-09-25 17:39:53 +00:00
|
|
|
public void testMultipleFiles() {
|
|
|
|
executeTarget("testMultipleFiles");
|
|
|
|
String result = getProject().getProperty("location.test");
|
|
|
|
assertNotNull("Property not set.", result);
|
|
|
|
assertTrue("Only one file found.", result.indexOf(";") > -1);
|
2018-02-28 07:58:59 +01:00
|
|
|
}</pre>
|
2003-09-25 17:39:53 +00:00
|
|
|
|
2018-02-28 07:58:59 +01:00
|
|
|
<p>Now we need a directory structure where we CAN find files with the same name in different directories. Because we
|
|
|
|
can't sure to have one we create one on <strong>*1</strong> and <strong>*2</strong>. And of course we clean up that
|
|
|
|
on <strong>*4</strong>. The creation can be done inside our test target or in a separate one, which will be better for
|
|
|
|
reuse later (<strong>*3</strong>).
|
2003-09-25 17:39:53 +00:00
|
|
|
|
|
|
|
<p>The task implementation is modified as followed:</p>
|
|
|
|
|
2018-03-10 20:17:33 +01:00
|
|
|
<pre>
|
2018-02-28 07:58:59 +01:00
|
|
|
private List<String> foundFiles = new ArrayList<>();
|
2003-09-25 17:39:53 +00:00
|
|
|
...
|
|
|
|
private String delimiter = null;
|
|
|
|
...
|
|
|
|
public void setDelimiter(String delim) {
|
|
|
|
delimiter = delim;
|
|
|
|
}
|
|
|
|
...
|
|
|
|
public void execute() {
|
|
|
|
validate();
|
|
|
|
// find all files
|
2018-02-28 07:58:59 +01:00
|
|
|
for (Path path : paths) {
|
|
|
|
for (File includedFile : path.list()) {
|
|
|
|
String filename = includedFile.replace('\\','/');
|
2003-09-25 17:39:53 +00:00
|
|
|
filename = filename.substring(filename.lastIndexOf("/")+1);
|
2018-02-28 20:26:50 +01:00
|
|
|
if (file.equals(filename) && <b>!foundFiles.contains(includedFile)</b>) { // 1
|
2018-02-28 07:58:59 +01:00
|
|
|
foundFiles.add(includedFile);
|
2003-09-25 17:39:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// create the return value (list/single)
|
|
|
|
String rv = null;
|
2018-02-28 20:26:50 +01:00
|
|
|
if (!foundFiles.isEmpty()) { // 2
|
2018-02-28 07:58:59 +01:00
|
|
|
if (delimiter == null) {
|
2003-09-25 17:39:53 +00:00
|
|
|
// only the first
|
2018-02-28 07:58:59 +01:00
|
|
|
rv = foundFiles.get(0);
|
2003-09-25 17:39:53 +00:00
|
|
|
} else {
|
|
|
|
// create list
|
2018-02-28 07:58:59 +01:00
|
|
|
StringBuilder list = new StringBuilder();
|
2018-02-28 20:26:50 +01:00
|
|
|
for (String file : foundFiles) { // 3
|
2003-09-25 17:39:53 +00:00
|
|
|
list.append(it.next());
|
2018-02-28 20:26:50 +01:00
|
|
|
if (<b>list.length() > 0</b>) list.append(delimiter); // 4
|
2018-02-28 07:58:59 +01:00
|
|
|
}
|
2003-09-25 17:39:53 +00:00
|
|
|
rv = list.toString();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// create the property
|
2018-02-28 07:58:59 +01:00
|
|
|
if (rv != null)
|
2003-09-25 17:39:53 +00:00
|
|
|
getProject().setNewProperty(location, rv);
|
2018-02-28 07:58:59 +01:00
|
|
|
}</pre>
|
2003-09-25 17:39:53 +00:00
|
|
|
|
2018-02-28 07:58:59 +01:00
|
|
|
<p>The algorithm does: finding all files, creating the return value depending on the users wish, returning the value as
|
|
|
|
property. On <strong>//1</strong> we eliminates the duplicates. <strong>//2</strong> ensures that we create the return
|
|
|
|
value only if we have found one file. On <strong>//3</strong> we iterate over all found files and <strong>//4</strong>
|
|
|
|
ensures that the last entry has no trailing delimiter.</p>
|
2003-09-25 17:39:53 +00:00
|
|
|
|
2018-02-28 07:58:59 +01:00
|
|
|
<p>Ok, first searching for all files and then returning only the first one ... You can tune the performance of your own
|
|
|
|
:-)</p>
|
2003-09-25 17:39:53 +00:00
|
|
|
|
2018-02-08 22:52:33 +01:00
|
|
|
<h2 id="documentation">Documentation</h2>
|
2018-02-28 07:58:59 +01:00
|
|
|
<p>A task is useless if the only who is able to code the buildfile is the task developer (and he only the next few weeks
|
|
|
|
:-). So documentation is also very important. In which form you do that depends on your favourite. But inside Ant there
|
|
|
|
is a common format and it has advantages if you use that: all task users know that form, this form is requested if you
|
|
|
|
decide to contribute your task. So we will doc our task in that form.</p>
|
|
|
|
|
|
|
|
<p>If you have a look at the manual page of the <a href="Tasks/java.html">Java task [5]</a> you will see that it:</p>
|
|
|
|
<ul>
|
2009-05-07 09:20:14 +00:00
|
|
|
<li>is plain html</li>
|
2003-09-25 17:39:53 +00:00
|
|
|
<li>starts with the name</li>
|
2018-02-28 07:58:59 +01:00
|
|
|
<li>has sections: description, parameters, nested elements, (maybe return codes) and (most important :-) examples</li>
|
|
|
|
<li>parameters are listed in a table with columns for attribute name, its description and whether it's required (if you
|
|
|
|
add a feature after an Ant release, provide a <em>since Ant xx</em> statement when it's introduced)</li>
|
2003-09-25 17:39:53 +00:00
|
|
|
<li>describe the nested elements (since-statement if necessary)</li>
|
2009-05-07 09:20:14 +00:00
|
|
|
<li>provide one or more useful examples; first code, then description.</li>
|
2003-09-25 17:39:53 +00:00
|
|
|
</ul>
|
2018-02-28 07:58:59 +01:00
|
|
|
<p>As a template we have:</p>
|
2003-09-25 17:39:53 +00:00
|
|
|
|
2018-03-10 20:17:33 +01:00
|
|
|
<pre>
|
2018-12-21 11:20:43 +01:00
|
|
|
<!DOCTYPE html>
|
|
|
|
<html lang="en">
|
2003-09-25 17:39:53 +00:00
|
|
|
|
2004-11-16 08:12:35 +00:00
|
|
|
<head>
|
2009-05-07 09:20:14 +00:00
|
|
|
<title><b>Taskname</b> Task</title>
|
2004-11-16 08:12:35 +00:00
|
|
|
</head>
|
2003-09-25 17:39:53 +00:00
|
|
|
|
2004-11-16 08:12:35 +00:00
|
|
|
<body>
|
2003-09-25 17:39:53 +00:00
|
|
|
|
2018-02-28 07:58:59 +01:00
|
|
|
<h2 id="<b>taskname</b>"><b>Taskname</b></h2>
|
2004-11-16 08:12:35 +00:00
|
|
|
<h3>Description</h3>
|
2018-02-28 07:58:59 +01:00
|
|
|
<p><b>Describe the task.</b></p>
|
2003-09-25 17:39:53 +00:00
|
|
|
|
2004-11-16 08:12:35 +00:00
|
|
|
<h3>Parameters</h3>
|
2018-02-28 07:58:59 +01:00
|
|
|
<table class="attr">
|
2004-11-16 08:12:35 +00:00
|
|
|
<tr>
|
2018-12-21 11:20:43 +01:00
|
|
|
<th scope="col">Attribute</th>
|
|
|
|
<th scope="col">Description</th>
|
|
|
|
<th scope="col">Required</th>
|
2004-11-16 08:12:35 +00:00
|
|
|
</tr>
|
2003-09-25 17:39:53 +00:00
|
|
|
|
|
|
|
<b>do this html row for each attribute (including inherited attributes)</b>
|
2004-11-16 08:12:35 +00:00
|
|
|
<tr>
|
2018-02-28 07:58:59 +01:00
|
|
|
<td>classname</td>
|
|
|
|
<td>the Java class to execute.</td>
|
|
|
|
<td>Either jar or classname</td>
|
2004-11-16 08:12:35 +00:00
|
|
|
</tr>
|
2003-09-25 17:39:53 +00:00
|
|
|
|
2004-11-16 08:12:35 +00:00
|
|
|
</table>
|
2003-09-25 17:39:53 +00:00
|
|
|
|
2004-11-16 08:12:35 +00:00
|
|
|
<h3>Parameters specified as nested elements</h3>
|
2003-09-25 17:39:53 +00:00
|
|
|
|
|
|
|
<b>Describe each nested element (including inherited)</b>
|
2009-05-07 09:20:14 +00:00
|
|
|
<h4><b>your nested element</b></h4>
|
|
|
|
<p><b>description</b></p>
|
2004-11-16 08:12:35 +00:00
|
|
|
<p><em>since Ant 1.6</em>.</p>
|
2003-09-25 17:39:53 +00:00
|
|
|
|
2004-11-16 08:12:35 +00:00
|
|
|
<h3>Examples</h3>
|
|
|
|
<pre>
|
2005-03-07 18:33:59 +00:00
|
|
|
<b>A code sample; don't forget to escape the < of the tags with &lt;</b>
|
2004-11-16 08:12:35 +00:00
|
|
|
</pre>
|
2009-05-07 09:20:14 +00:00
|
|
|
<b>What should that example do?</b>
|
2003-09-25 17:39:53 +00:00
|
|
|
|
2004-11-16 08:12:35 +00:00
|
|
|
</body>
|
2018-02-28 07:58:59 +01:00
|
|
|
</html></pre>
|
2003-09-25 17:39:53 +00:00
|
|
|
|
2009-05-07 09:20:14 +00:00
|
|
|
<p>Here is an example documentation page for our task:</p>
|
2018-03-10 20:17:33 +01:00
|
|
|
<pre>
|
2018-12-21 11:20:43 +01:00
|
|
|
<!DOCTYPE html>
|
|
|
|
<html lang="en">
|
2004-11-16 08:12:35 +00:00
|
|
|
|
|
|
|
<head>
|
2009-05-07 09:20:14 +00:00
|
|
|
<title>Find Task</title>
|
2004-11-16 08:12:35 +00:00
|
|
|
</head>
|
|
|
|
|
|
|
|
<body>
|
|
|
|
|
2018-02-08 22:52:33 +01:00
|
|
|
<h2 id="find">Find</h2>
|
2004-11-16 08:12:35 +00:00
|
|
|
<h3>Description</h3>
|
2018-02-28 07:58:59 +01:00
|
|
|
<p>Searches in a given path for a file and returns the absolute to it as property.
|
2004-11-16 08:12:35 +00:00
|
|
|
If delimiter is set this task returns all found locations.</p>
|
|
|
|
|
|
|
|
<h3>Parameters</h3>
|
2018-02-28 07:58:59 +01:00
|
|
|
<table class="attr">
|
2004-11-16 08:12:35 +00:00
|
|
|
<tr>
|
2018-12-21 11:20:43 +01:00
|
|
|
<th scope="col">Attribute</th>
|
|
|
|
<th scope="col">Description</th>
|
|
|
|
<th scope="col">Required</th>
|
2004-11-16 08:12:35 +00:00
|
|
|
</tr>
|
|
|
|
<tr>
|
2018-02-28 07:58:59 +01:00
|
|
|
<td>file</td>
|
|
|
|
<td>The name of the file to search.</td>
|
|
|
|
<td>yes</td>
|
2004-11-16 08:12:35 +00:00
|
|
|
</tr>
|
|
|
|
<tr>
|
2018-02-28 07:58:59 +01:00
|
|
|
<td>location</td>
|
|
|
|
<td>The name of the property where to store the location</td>
|
|
|
|
<td>yes</td>
|
2004-11-16 08:12:35 +00:00
|
|
|
</tr>
|
|
|
|
<tr>
|
2018-02-28 07:58:59 +01:00
|
|
|
<td>delimiter</td>
|
|
|
|
<td>A delimiter to use when returning the list</td>
|
|
|
|
<td>only if the list is required</td>
|
2004-11-16 08:12:35 +00:00
|
|
|
</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>
|
2018-02-28 07:58:59 +01:00
|
|
|
<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>).
|
2004-11-16 08:12:35 +00:00
|
|
|
|
|
|
|
<pre>
|
2018-02-28 07:58:59 +01:00
|
|
|
<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>).
|
2003-09-25 17:39:53 +00:00
|
|
|
|
2004-11-16 08:12:35 +00:00
|
|
|
</body>
|
2018-02-28 07:58:59 +01:00
|
|
|
</html></pre>
|
2003-09-25 17:39:53 +00:00
|
|
|
|
2018-02-08 22:52:33 +01:00
|
|
|
<h2 id="contribute">Contribute the new task</h2>
|
2018-02-28 07:58:59 +01:00
|
|
|
<p>If we decide to contribute our task, we should do some things:</p>
|
2018-02-08 22:52:33 +01:00
|
|
|
<ul>
|
2003-09-25 17:39:53 +00:00
|
|
|
<li>is our task welcome? :-) Simply ask on the user list</li>
|
2018-02-28 07:58:59 +01:00
|
|
|
<li>is the right package used?</li>
|
2007-06-14 07:05:09 +00:00
|
|
|
<li>does the code conform to the styleguide?</li>
|
2018-02-28 07:58:59 +01:00
|
|
|
<li>do all tests pass?</li>
|
|
|
|
<li>does the code compile on JDK 5 (and passes all tests there)?</li>
|
2003-09-25 17:39:53 +00:00
|
|
|
<li>code under Apache license</li>
|
|
|
|
<li>create a patch file</li>
|
|
|
|
<li>publishing that patch file</li>
|
|
|
|
</ul>
|
2018-03-08 07:43:53 +01:00
|
|
|
<p>The <a href="https://ant.apache.org/ant_task_guidelines.html" target="_top">Ant Task Guidelines [6]</a> support
|
|
|
|
additional information on that.</p>
|
2003-09-25 17:39:53 +00:00
|
|
|
|
2018-02-28 07:58:59 +01:00
|
|
|
<p>Now we will check the "Checklist before submitting a new task" described in that guideline.</p>
|
2003-09-25 17:39:53 +00:00
|
|
|
<ul>
|
2018-02-28 07:58:59 +01:00
|
|
|
<li>Java file begins with Apache license statement. <strong><em>must do that</em></strong></li>
|
|
|
|
<li>Task does not depend on GPL or LGPL code. <strong><em>ok</em></strong></li>
|
|
|
|
<li>Source code complies with style guidelines <strong><em>have to check (checkstyle)</em></strong></li>
|
|
|
|
<li>Code compiles and runs on Java 5 <strong><em>have to try</em></strong></li>
|
|
|
|
<li>Member variables are private, and provide public accessor methods if access is actually needed. <strong><em>have to
|
|
|
|
check (checkstyle)</em></strong></li>
|
|
|
|
<li><em>Maybe</em> Task has <var>failonerror</var> attribute to control failure
|
|
|
|
behaviour <strong><em>hasn't</em></strong></li>
|
|
|
|
<li>New test cases written and succeed <strong><em>passed on JDK 8, have to try on JDK 5</em></strong></li>
|
|
|
|
<li>Documentation page written <strong><em>ok</em></strong></li>
|
|
|
|
<li>Example task declarations in the documentation tested. <strong><em>ok (used in tests)</em></strong></li>
|
|
|
|
<li>Message to dev contains [SUBMIT] and task name in subject <strong><em>to do</em></strong></li>
|
|
|
|
<li>Message body contains a rationale for the task <strong><em>to do</em></strong></li>
|
|
|
|
<li>Message body contains the URL to GitHub pull request. <strong><em>to do</em></strong></li>
|
2003-09-25 17:39:53 +00:00
|
|
|
</ul>
|
|
|
|
|
|
|
|
<h3>Package / Directories</h3>
|
2018-02-28 07:58:59 +01:00
|
|
|
<p>This task does not depend on any external library. Therefore we can use this as a core task. This task contains only
|
|
|
|
one class. So we can use the standard package for core
|
2018-03-10 20:17:33 +01:00
|
|
|
tasks: <code class="code">org.apache.tools.ant.taskdefs</code>. Implementations are in the
|
|
|
|
directory <samp>src/main</samp>, tests in <samp>src/testcases</samp> and buildfiles for tests
|
|
|
|
in <samp>src/etc/testcases</samp>.</p>
|
2018-02-28 07:58:59 +01:00
|
|
|
|
|
|
|
<p>Now we integrate our work into Ant distribution. So first we do an update of our Git tree. If not done yet, you
|
|
|
|
should clone the Ant repository on GitHub[7], then create a local clone:</p>
|
2018-03-10 20:17:33 +01:00
|
|
|
<pre class="input">git clone https://github.com/<em>your-sig</em>/ant.git</pre>
|
2018-02-28 07:58:59 +01:00
|
|
|
|
|
|
|
<p>Now we will build our Ant distribution and do a test. So we can see if there are any tests failing on our
|
|
|
|
machine. (We can ignore these failing tests on later steps; Windows syntax used here—translate to UNIX if
|
|
|
|
needed):</p>
|
2018-03-10 20:17:33 +01:00
|
|
|
<pre class="input">
|
2018-02-28 07:58:59 +01:00
|
|
|
ANTREPO> build // 1
|
|
|
|
ANTREPO> set ANT_HOME=%CD%\dist // 2
|
|
|
|
ANTREPO> ant test -Dtest.haltonfailure=false // 3</pre>
|
2003-09-29 14:30:17 +00:00
|
|
|
|
2018-02-28 07:58:59 +01:00
|
|
|
<p>First we have to build our Ant distribution (<strong>//1</strong>). On <strong>//2</strong> we set
|
|
|
|
the <code>ANT_HOME</code> environment variable to the directory where the new created distribution is stored
|
|
|
|
(<code>%CD%</code> is expanded to the current directory on Windows 2000 and later). On <strong>//3</strong> we let Ant
|
|
|
|
do all the tests (which enforced a compile of all tests) without stopping on first failure.</p>
|
2003-09-29 14:30:17 +00:00
|
|
|
|
2018-02-28 07:58:59 +01:00
|
|
|
<p>Next we apply our work onto Ant sources. Because we haven't modified any, this is a relatively simple
|
|
|
|
step. <em>(Because I have a local Git clone of Ant and usually contribute my work, I work on the local copy just from
|
|
|
|
the beginning. The advantage: this step isn't necessary and saves a lot of work if you modify existing sources :-)</em>.
|
2003-09-29 14:30:17 +00:00
|
|
|
|
|
|
|
<ul>
|
2018-02-28 07:58:59 +01:00
|
|
|
<li>move the <samp>Find.java</samp> to <samp>ANTREPO/src/main/org/apache/tools/ant/taskdefs/Find.java</samp></li>
|
|
|
|
<li>move the <samp>FindTest.java</samp> to <samp>ANTREPO/src/testcases/org/apache/tools/ant/taskdefs/FindTest.java</samp></li>
|
|
|
|
<li>move the <samp>build.xml</samp> to <samp>ANTREPO/src/etc/testcases/taskdefs/<strong>find.xml</strong></samp> (!!! renamed !!!)</li>
|
|
|
|
<li>add a <code>package org.apache.tools.ant.taskdefs;</code> at the beginning of the two java files</li>
|
|
|
|
<li>delete all stuff from <samp>find.xml</samp> keeping the
|
|
|
|
targets <q>testFileNotPresent</q>, <q>testFilePresent</q>, <q>test.init</q> and <q>testMultipleFiles</q></li>
|
|
|
|
<li>delete the dependency to <q>use.init</q> in the <samp>find.xml</samp></li>
|
|
|
|
<li>in <samp>FindTest.java</samp> change the line <code>configureProject("build.xml");</code>
|
|
|
|
to <code>configureProject("src/etc/testcases/taskdefs/find.xml");</code></li>
|
|
|
|
<li>move the <samp>find.html</samp> to <samp>ANTREPO/docs/manual/Tasks/find.html</samp></li>
|
|
|
|
<li>add a <code><a href="Tasks/find.html">Find</a><br></code> in
|
|
|
|
the <samp>ANTREPO/docs/manual/tasklist.html</samp></li>
|
2003-09-29 14:30:17 +00:00
|
|
|
</ul>
|
|
|
|
|
2018-02-28 07:58:59 +01:00
|
|
|
<p>Now our modifications are done and we will retest it:</p>
|
2018-03-10 20:17:33 +01:00
|
|
|
<pre class="input">
|
2018-02-28 07:58:59 +01:00
|
|
|
ANTREPO> build
|
|
|
|
ANTREPO> ant run-single-test // 1
|
2003-09-29 14:30:17 +00:00
|
|
|
-Dtestcase=org.apache.tools.ant.taskdefs.FindTest // 2
|
2018-02-28 07:58:59 +01:00
|
|
|
-Dtest.haltonfailure=false</pre>
|
|
|
|
<p>Because we only want to test our new class, we use the target for single tests, specify the test to use and configure
|
|
|
|
not to halt on the first failure—we want to see all failures of our own test (<strong>//1 + 2</strong>).</p>
|
|
|
|
|
|
|
|
<p>And ... oh, all tests fail: <em>Ant could not find the task or a class this task relies upon.</em></p>
|
|
|
|
|
|
|
|
<p>Ok: in the earlier steps we told Ant to use the Find class for the <code><find></code> task (remember
|
2018-03-10 20:17:33 +01:00
|
|
|
the <code><taskdef></code> statement in the <q>use.init</q> target). But now we want to introduce that task as a
|
|
|
|
core task. And nobody wants to <code>taskdef</code> the <code>javac</code>, <code>echo</code>, ... So what to do? The
|
|
|
|
answer is the <samp>src/main/.../taskdefs/default.properties</samp>. Here is the mapping between taskname and
|
|
|
|
implementing class done. So we add a <code>find=org.apache.tools.ant.taskdefs.Find</code> as the last core task (just
|
|
|
|
before the <code># optional tasks</code> line). Now a second try:</p>
|
|
|
|
<pre class="input">
|
2018-02-28 07:58:59 +01:00
|
|
|
ANTREPO> build // 1
|
|
|
|
ANTREPO> ant run-single-test
|
2003-09-29 14:30:17 +00:00
|
|
|
-Dtestcase=org.apache.tools.ant.taskdefs.FindTest
|
2018-02-28 07:58:59 +01:00
|
|
|
-Dtest.haltonfailure=false</pre>
|
|
|
|
<p>We have to rebuild (<strong>//1</strong>) Ant because the test look in the <samp>%ANT_HOME%\lib\ant.jar</samp> (more
|
|
|
|
precise: on the classpath) for the properties file. And we have only modified it in the source path. So we have to
|
|
|
|
rebuild that jar. But now all tests pass and we check whether our class breaks some other tests.</p>
|
2018-03-10 20:17:33 +01:00
|
|
|
<pre class="input">ANTREPO> ant test -Dtest.haltonfailure=false</pre>
|
2018-02-28 07:58:59 +01:00
|
|
|
<p>Because there are a lot of tests this step requires a little bit of time. So use the <q>run-single-test</q> during
|
|
|
|
development and do the <q>test</q> only at the end (maybe sometimes during development too). We use
|
2018-03-10 20:17:33 +01:00
|
|
|
the <kbd>-Dtest.haltonfailure=false</kbd> here because there could be other tests fail and we have to look into
|
2018-02-28 07:58:59 +01:00
|
|
|
them.</p>
|
|
|
|
|
|
|
|
<p>This test run should show us two things: our test will run and the number of failing tests is the same as directly
|
|
|
|
after <code>git clone</code> (without our modifications).</p>
|
2003-09-29 14:30:17 +00:00
|
|
|
|
2006-09-11 20:24:18 +00:00
|
|
|
<h3>Apache license statement</h3>
|
|
|
|
<p>Simply copy the license text from one the other source from the Ant source tree.</p>
|
2003-09-29 14:30:17 +00:00
|
|
|
|
2018-02-28 07:58:59 +01:00
|
|
|
<h3>Test on JDK 5</h3>
|
|
|
|
<p>Ant 1.10 uses Java 8 for development, but Ant 1.9 is actively maintained, too. That means that Ant code must be able
|
|
|
|
to run on a JDK 5. So we have to test that. You can download older JDKs
|
2018-03-08 07:43:53 +01:00
|
|
|
from <a href="https://www.oracle.com/technetwork/java/archive-139210.html" target="_top">Oracle [8]</a>.</p>
|
2003-09-29 14:30:17 +00:00
|
|
|
|
2018-02-28 07:58:59 +01:00
|
|
|
<p>Clean the <code>ANT_HOME</code> variable, delete the <samp>build</samp>, <samp>bootstrap</samp> and <samp>dist</samp>
|
|
|
|
directories, and point <code>JAVA_HOME</code> to the JDK 5 home directory. Then create the patch with your commit,
|
|
|
|
checkout 1.9.x branch in Git, apply your patch and do the <code>build</code>, set <code>ANT_HOME</code> and
|
2018-03-10 20:17:33 +01:00
|
|
|
run <kbd>ant test</kbd> (like above).</p>
|
2003-09-29 14:30:17 +00:00
|
|
|
|
|
|
|
<p>Our test should pass.</p>
|
|
|
|
|
2003-09-25 17:39:53 +00:00
|
|
|
<h3>Checkstyle</h3>
|
2018-02-28 07:58:59 +01:00
|
|
|
<p>There are many things we have to ensure. Indentation with 4 spaces, blanks here and there, ... (all described in
|
2018-03-08 07:43:53 +01:00
|
|
|
the <a href="https://ant.apache.org/ant_task_guidelines.html" target="_top">Ant Task Guidelines [6]</a> which includes
|
|
|
|
the <a href="https://www.oracle.com/technetwork/java/codeconvtoc-136057.html" target="_top">Sun code style
|
|
|
|
[9]</a>). Because there are so many things we would be happy to have a tool for do the checks. There is one:
|
|
|
|
checkstyle. Checkstyle is available at <a href="http://checkstyle.sourceforge.net/" target="_top">Sourceforge [10]</a>
|
|
|
|
and Ant provides with the <samp>check.xml</samp> a buildfile which will do the job for us.</p>
|
2018-02-28 07:58:59 +01:00
|
|
|
|
|
|
|
<p>Download it and put the <samp>checkstyle-*-all.jar</samp> into your <samp>%USERPROFILE%\.ant\lib</samp> directory.
|
|
|
|
All jar's stored there are available to Ant so you haven't to add it to you <samp>%ANT_HOME%\lib</samp> directory (this
|
|
|
|
feature is available <em>since Ant 1.6</em>).</p>
|
2003-09-29 14:30:17 +00:00
|
|
|
|
2018-02-08 22:52:33 +01:00
|
|
|
<p>So we will run the tests with</p>
|
2018-03-10 20:17:33 +01:00
|
|
|
<pre class="input">ANTREPO> ant -f check.xml checkstyle htmlreport</pre>
|
2018-02-28 07:58:59 +01:00
|
|
|
<p>I prefer the HTML report because there are lots of messages and we can navigate faster. Open
|
|
|
|
the <samp>ANTREPO/build/reports/checkstyle/html/index.html</samp> and navigate to the <samp>Find.java</samp>. Now we see
|
|
|
|
that there are some errors: missing whitespaces, unused imports, missing javadocs. So we have to do that.</p>
|
2003-09-29 14:30:17 +00:00
|
|
|
|
2018-02-28 07:58:59 +01:00
|
|
|
<p>Hint: start at the <strong>bottom</strong> of the file so the line numbers in the report will keep up to date and you
|
|
|
|
will find the next error place much more easier without redoing the checkstyle.</p>
|
2003-09-29 14:30:17 +00:00
|
|
|
|
2018-02-28 07:58:59 +01:00
|
|
|
<p>After cleaning up the code according to the messages we delete the reports directory and do a second checkstyle
|
|
|
|
run. Now our task isn't listed. That's fine :-)</p>
|
2003-09-25 17:39:53 +00:00
|
|
|
|
2003-09-29 14:30:17 +00:00
|
|
|
<h3>Publish the task</h3>
|
2018-03-08 07:43:53 +01:00
|
|
|
<p>Finally we publish that archive. As described in the <a href="https://ant.apache.org/ant_task_guidelines.html"
|
|
|
|
target="_top">Ant Task Guidelines [7]</a> we can announce it on the developer mailing list, create a BugZilla entry and
|
|
|
|
open a GitHub pull request. For both we need some information:</p>
|
2003-09-29 14:30:17 +00:00
|
|
|
|
2018-02-08 22:52:33 +01:00
|
|
|
<table>
|
2018-02-28 07:58:59 +01:00
|
|
|
<tr>
|
2019-03-01 22:34:05 +01:00
|
|
|
<th scope="row">subject</th>
|
2018-02-28 07:58:59 +01:00
|
|
|
<td><em>short description</em></td>
|
2003-09-29 14:30:17 +00:00
|
|
|
<td>Task for finding files in a path</td>
|
|
|
|
</tr>
|
|
|
|
<tr>
|
2019-03-01 22:34:05 +01:00
|
|
|
<th scope="row">body</th>
|
2018-02-28 07:58:59 +01:00
|
|
|
<td><em>more details about the path</em></td>
|
|
|
|
<td>This new task looks inside a nested <code><path/></code> for occurrences of a file and stores all locations
|
|
|
|
as a property. See the included manual for details.</td>
|
2003-09-29 14:30:17 +00:00
|
|
|
</tr>
|
|
|
|
<tr>
|
2019-03-01 22:34:05 +01:00
|
|
|
<th scope="row">pull request reference</th>
|
2018-02-28 07:58:59 +01:00
|
|
|
<td><em>GitHub pull request URL</em></td>
|
|
|
|
<td>https://github.com/apache/ant/pull/0</td>
|
2003-09-29 14:30:17 +00:00
|
|
|
</tr>
|
|
|
|
</table>
|
|
|
|
|
2018-02-28 07:58:59 +01:00
|
|
|
<p>Sending an email with this information is very easy and I think I haven't to describe that. BugZilla is slightly
|
|
|
|
more difficult. But the advantage is that entries will not be forgotten (a report is generated once every weekend). So
|
|
|
|
I will describe the process.</p>
|
2003-09-29 14:30:17 +00:00
|
|
|
|
2018-03-08 07:43:53 +01:00
|
|
|
<p>First, you must have a BugZilla account. So open the <a href="https://issues.apache.org/bugzilla/"
|
|
|
|
target="_top">BugZilla Main Page [11]</a> and follow the
|
|
|
|
link <a href="https://issues.apache.org/bugzilla/createaccount.cgi" target="_top">Open a new Bugzilla account [12]</a>
|
|
|
|
and the steps described there if you haven't one.</p>
|
2003-09-29 14:30:17 +00:00
|
|
|
|
|
|
|
<ol>
|
2018-03-08 07:43:53 +01:00
|
|
|
<li>From the BugZilla main page choose <a href="https://issues.apache.org/bugzilla/enter_bug.cgi" target="_top">Enter a
|
|
|
|
new bug report [13]</a></li>
|
2018-02-28 07:58:59 +01:00
|
|
|
<li>Choose "Ant" as product</li>
|
|
|
|
<li>Version is the last "Alpha (nightly)" (at this time 1.10)</li>
|
2003-09-29 14:30:17 +00:00
|
|
|
<li>Component is "Core tasks"</li>
|
2012-05-26 12:14:49 +00:00
|
|
|
<li>Platform and Severity are ok with "Other" and "Normal"</li>
|
2003-09-29 14:30:17 +00:00
|
|
|
<li>Initial State is ok with "New"</li>
|
2012-05-26 12:14:49 +00:00
|
|
|
<li>Same with the empty "Assigned to"</li>
|
2018-02-28 07:58:59 +01:00
|
|
|
<li>It is not required to add yourself as CC, because you are the reporter and therefore will be informed on
|
|
|
|
changes</li>
|
|
|
|
<li>URL: GitHub pull request URL</li>
|
|
|
|
<li>Summary: add the <var>subject</var> from the table</li>
|
|
|
|
<li>Description: add the <var>body</var> from the table</li>
|
2005-03-05 08:50:27 +00:00
|
|
|
<li>Then press "Commit"</li>
|
2003-09-29 14:30:17 +00:00
|
|
|
</ol>
|
2018-02-28 07:58:59 +01:00
|
|
|
|
|
|
|
<p>Now the new task is registered in the bug database.</p>
|
2003-09-25 17:39:53 +00:00
|
|
|
|
2018-02-08 22:52:33 +01:00
|
|
|
<h2 id="resources">Resources</h2>
|
|
|
|
<ol class="refs">
|
|
|
|
<li><a href="tutorial-writing-tasks.html">tutorial-writing-tasks.html</a></li>
|
|
|
|
<li><a href="tutorial-tasks-filesets-properties.zip">tutorial-tasks-filesets-properties.zip</a></li>
|
|
|
|
<li><a href="properties.html#built-in-props">properties.html#built-in-props</a></li>
|
2018-03-08 07:43:53 +01:00
|
|
|
<li><a href="http://ant-contrib.sourceforge.net/" target="_top">http://ant-contrib.sourceforge.net/</a></li>
|
2018-02-08 22:52:33 +01:00
|
|
|
<li><a href="Tasks/java.html">Tasks/java.html</a></li>
|
2018-03-08 07:43:53 +01:00
|
|
|
<li><a href="https://ant.apache.org/ant_task_guidelines.html"
|
|
|
|
target="_top">https://ant.apache.org/ant_task_guidelines.html</a></li>
|
|
|
|
<li><a href="https://github.com/apache/ant" target="_top">https://github.com/apache/ant</a></li>
|
|
|
|
<li><a href="https://www.oracle.com/technetwork/java/archive-139210.html"
|
|
|
|
target="_top">https://www.oracle.com/technetwork/java/archive-139210.html</a></li>
|
|
|
|
<li><a href="https://www.oracle.com/technetwork/java/codeconvtoc-136057.html"
|
|
|
|
target="_top">https://www.oracle.com/technetwork/java/codeconvtoc-136057.html</a></li>
|
|
|
|
<li><a href="http://checkstyle.sourceforge.net/" target="_top">http://checkstyle.sourceforge.net/</a></li>
|
|
|
|
<li><a href="https://issues.apache.org/bugzilla/" target="_top">https://issues.apache.org/bugzilla/</a></li>
|
|
|
|
<li><a href="https://issues.apache.org/bugzilla/createaccount.cgi"
|
|
|
|
target="_top">https://issues.apache.org/bugzilla/createaccount.cgi</a></li>
|
|
|
|
<li><a href="https://issues.apache.org/bugzilla/enter_bug.cgi"
|
|
|
|
target="_top">https://issues.apache.org/bugzilla/enter_bug.cgi</a></li>
|
2018-02-08 22:52:33 +01:00
|
|
|
</ol>
|
2003-09-29 14:30:17 +00:00
|
|
|
|
2003-09-25 17:39:53 +00:00
|
|
|
</body>
|
2005-03-07 18:33:59 +00:00
|
|
|
</html>
|