[14982] | 1 | <!--
|
---|
| 2 | Licensed to the Apache Software Foundation (ASF) under one or more
|
---|
| 3 | contributor license agreements. See the NOTICE file distributed with
|
---|
| 4 | this work for additional information regarding copyright ownership.
|
---|
| 5 | The ASF licenses this file to You under the Apache License, Version 2.0
|
---|
| 6 | (the "License"); you may not use this file except in compliance with
|
---|
| 7 | the License. You may obtain a copy of the License at
|
---|
| 8 |
|
---|
| 9 | http://www.apache.org/licenses/LICENSE-2.0
|
---|
| 10 |
|
---|
| 11 | Unless required by applicable law or agreed to in writing, software
|
---|
| 12 | distributed under the License is distributed on an "AS IS" BASIS,
|
---|
| 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
---|
| 14 | See the License for the specific language governing permissions and
|
---|
| 15 | limitations under the License.
|
---|
| 16 | -->
|
---|
| 17 | <html>
|
---|
| 18 | <head>
|
---|
| 19 |
|
---|
| 20 | <title>Tutorial: Tasks using Properties, Filesets & Paths</title>
|
---|
| 21 | <meta name="author" content="Jan Matèrne">
|
---|
| 22 | <link rel="stylesheet" type="text/css" href="stylesheets/style.css">
|
---|
| 23 | <style type="text/css">
|
---|
| 24 | <!--
|
---|
| 25 | .code { background: #EFEFEF; margin-top: }
|
---|
| 26 | .output { color: #FFFFFF; background: #837A67; }
|
---|
| 27 | -->
|
---|
| 28 | </style>
|
---|
| 29 | </head>
|
---|
| 30 | <body>
|
---|
| 31 | <h1>Tutorial: Tasks using Properties, Filesets & Paths</h1>
|
---|
| 32 |
|
---|
| 33 | <p>After reading the tutorial about <a href="tutorial-writing-tasks.html">writing
|
---|
| 34 | tasks [1]</a> this tutorial explains how to get and set properties and how to use
|
---|
| 35 | nested filesets and paths. Finally it explains how to contribute tasks to Ant.</p>
|
---|
| 36 |
|
---|
| 37 | <h2>Content</h2>
|
---|
| 38 | <p><ul>
|
---|
| 39 | <li><a href="#goal">The goal</a></li>
|
---|
| 40 | <li><a href="#buildenvironment">Build environment</a></li>
|
---|
| 41 | <li><a href="#propertyaccess">Property access</a></li>
|
---|
| 42 | <li><a href="#filesets">Using filesets</a></li>
|
---|
| 43 | <li><a href="#path">Using nested paths</a></li>
|
---|
| 44 | <li><a href="#returning-list">Returning a list</a></li>
|
---|
| 45 | <li><a href="#documentation">Documentation</a></li>
|
---|
| 46 | <li><a href="#contribute">Contribute the new task</a></li>
|
---|
| 47 | <li><a href="#resources">Resources</a></li>
|
---|
| 48 | </ul></p>
|
---|
| 49 |
|
---|
| 50 |
|
---|
| 51 | <h2><a name="goal">The goal</a></h2>
|
---|
| 52 | <p>The goal is to write a task, which searchs in a path for a file and saves the
|
---|
| 53 | location of that file in a property.</p>
|
---|
| 54 |
|
---|
| 55 |
|
---|
| 56 | <h2><a name="buildenvironment">Build environment</a></h2>
|
---|
| 57 | <p>We can use the buildfile from the other tutorial and modify it a little bit.
|
---|
| 58 | That's the advantage of using properties - we can reuse nearly the whole script. :-)</p>
|
---|
| 59 | <pre class="code">
|
---|
| 60 | <?xml version="1.0" encoding="ISO-8859-1"?>
|
---|
| 61 | <project name="<b>FindTask</b>" basedir="." default="test">
|
---|
| 62 | ...
|
---|
| 63 | <target name="use.init" description="Taskdef's the <b>Find</b>-Task" depends="jar">
|
---|
| 64 | <taskdef name="<b>find</b>" classname="<b>Find</b>" classpath="${ant.project.name}.jar"/>
|
---|
| 65 | </target>
|
---|
| 66 |
|
---|
| 67 | <b><!-- the other use.* targets are deleted --></b>
|
---|
| 68 | ...
|
---|
| 69 | </project>
|
---|
| 70 | </pre>
|
---|
| 71 |
|
---|
| 72 | <p>The buildfile is in the archive <a href="tutorial-tasks-filesets-properties.zip">
|
---|
| 73 | tutorial-tasks-filesets-properties.zip [2]</a> in <tt>/build.xml.01-propertyaccess</tt>
|
---|
| 74 | (future version saved as *.02..., final version as build.xml; same for sources).</p>
|
---|
| 75 |
|
---|
| 76 |
|
---|
| 77 | <h2><a name="propertyaccess">Property access</a></h2>
|
---|
| 78 | <p>Our first step is to set a property to a value and print the value of that property.
|
---|
| 79 | So our scenario would be
|
---|
| 80 | <pre class="code">
|
---|
| 81 | <find property="test" value="test-value"/>
|
---|
| 82 | <find print="test"/>
|
---|
| 83 | </pre>
|
---|
| 84 | ok, can be rewritten with the core tasks
|
---|
| 85 | <pre class="code">
|
---|
| 86 | <property name="test" value="test-value"/>
|
---|
| 87 | <echo message="${test}"/>
|
---|
| 88 | </pre>
|
---|
| 89 | but I have to start on known ground :-)</p>
|
---|
| 90 | <p>So what to do? Handling three attributes (property, value, print) and an execute method.
|
---|
| 91 | Because this is only an introduction example I don't do much checking:
|
---|
| 92 |
|
---|
| 93 | <pre class="code">
|
---|
| 94 | import org.apache.tools.ant.BuildException;
|
---|
| 95 |
|
---|
| 96 | public class Find extends Task {
|
---|
| 97 |
|
---|
| 98 | private String property;
|
---|
| 99 | private String value;
|
---|
| 100 | private String print;
|
---|
| 101 |
|
---|
| 102 | public void setProperty(String property) {
|
---|
| 103 | this.property = property;
|
---|
| 104 | }
|
---|
| 105 |
|
---|
| 106 | // setter for value and print
|
---|
| 107 |
|
---|
| 108 | public void execute() {
|
---|
| 109 | if (print != null) {
|
---|
| 110 | String propValue = <b>getProject().getProperty(print)</b>;
|
---|
| 111 | log(propValue);
|
---|
| 112 | } else {
|
---|
| 113 | if (property == null) throw new BuildException("property not set");
|
---|
| 114 | if (value == null) throw new BuildException("value not set");
|
---|
| 115 | <b>getProject().setNewProperty(property, value)</b>;
|
---|
| 116 | }
|
---|
| 117 | }
|
---|
| 118 | }
|
---|
| 119 | </pre>
|
---|
| 120 |
|
---|
| 121 | As said in the other tutorial, the property access is done via Project instance.
|
---|
| 122 | We get this instance via the public <tt>getProject()</tt> method which we inherit from
|
---|
| 123 | <tt>Task</tt> (more precise from ProjectComponent). Reading a property is done via
|
---|
| 124 | <tt>getProperty(<i>propertyname</i>)</tt> (very simple, isn't it?). This property returns
|
---|
| 125 | the value as String or <i>null</i> if not set.<br>
|
---|
| 126 | Setting a property is ... not really difficult, but there is more than one setter. You can
|
---|
| 127 | use the <tt>setProperty()</tt> method which will do the job like expected. But there is
|
---|
| 128 | a golden rule in Ant: <i>properties are immutable</i>. And this method sets the property
|
---|
| 129 | to the specified value - whether it has a value before that or not. So we use another
|
---|
| 130 | way. <tt>setNewProperty()</tt> sets the property only if there is no property with that
|
---|
| 131 | name. Otherwise a message is logged.</p>
|
---|
| 132 |
|
---|
| 133 | <p><i>(by the way: a short word to ants "namespaces" (don't
|
---|
| 134 | be confused with xml namespaces:
|
---|
| 135 | an <code><antcall></code> creates a new space for property names. All properties from the caller
|
---|
| 136 | are passed to the callee, but the callee can set its own properties without notice by the
|
---|
| 137 | caller.)</i></p>
|
---|
| 138 |
|
---|
| 139 | <p>There are some other setter, too (but I haven't used them, so I can't say something
|
---|
| 140 | to them, sorry :-)</p>
|
---|
| 141 |
|
---|
| 142 | <p>After putting our two line example from above into a target names <tt>use.simple</tt>
|
---|
| 143 | we can call that from our testcase:
|
---|
| 144 |
|
---|
| 145 | <pre class="code">
|
---|
| 146 | import org.apache.tools.ant.BuildFileTest;
|
---|
| 147 |
|
---|
| 148 | public class FindTest extends BuildFileTest {
|
---|
| 149 |
|
---|
| 150 | public FindTest(String name) {
|
---|
| 151 | super(name);
|
---|
| 152 | }
|
---|
| 153 |
|
---|
| 154 | public void setUp() {
|
---|
| 155 | configureProject("build.xml");
|
---|
| 156 | }
|
---|
| 157 |
|
---|
| 158 | public void testSimple() {
|
---|
| 159 | <b>expectLog("use.simple", "test-value");</b>
|
---|
| 160 | }
|
---|
| 161 | }
|
---|
| 162 | </pre>
|
---|
| 163 |
|
---|
| 164 | and all works fine.</p>
|
---|
| 165 |
|
---|
| 166 |
|
---|
| 167 |
|
---|
| 168 | <h2><a name="filesets">Using filesets</a></h2>
|
---|
| 169 | <p>Ant provides a common way of bundling files: the fileset. Because you are reading
|
---|
| 170 | this tutorial I think you know them and I don't have to spend more explanations about
|
---|
| 171 | their usage in buildfiles. Our goal is to search a file in path. And on this step the
|
---|
| 172 | path is simply a fileset (or more precise: a collection of filesets). So our usage
|
---|
| 173 | would be
|
---|
| 174 | <pre class="code">
|
---|
| 175 | <find file="ant.jar" location="location.ant-jar">
|
---|
| 176 | <fileset dir="${ant.home}" includes="**/*.jar"/>
|
---|
| 177 | </find>
|
---|
| 178 | </pre>
|
---|
| 179 | </p>
|
---|
| 180 |
|
---|
| 181 | <p>What do we need? A task with two attributes (file, location) and nested
|
---|
| 182 | filesets. Because we had attribute handling already explained in the example above and the
|
---|
| 183 | handling of nested elements is described in the other tutorial the code should be very easy:
|
---|
| 184 | <pre class="code">
|
---|
| 185 | public class Find extends Task {
|
---|
| 186 |
|
---|
| 187 | private String file;
|
---|
| 188 | private String location;
|
---|
| 189 | private Vector filesets = new Vector();
|
---|
| 190 |
|
---|
| 191 | public void setFile(String file) {
|
---|
| 192 | this.file = file;
|
---|
| 193 | }
|
---|
| 194 |
|
---|
| 195 | public void setLocation(String location) {
|
---|
| 196 | this.location = location;
|
---|
| 197 | }
|
---|
| 198 |
|
---|
| 199 | public void addFileset(FileSet fileset) {
|
---|
| 200 | filesets.add(fileset);
|
---|
| 201 | }
|
---|
| 202 |
|
---|
| 203 | public void execute() {
|
---|
| 204 | }
|
---|
| 205 | }
|
---|
| 206 | </pre>
|
---|
| 207 | Ok - that task wouldn't do very much, but we can use it in the described manner without
|
---|
| 208 | failure. On next step we have to implement the execute method. And before that we will
|
---|
| 209 | implement the appropriate testcases (TDD - test driven development).</p>
|
---|
| 210 |
|
---|
| 211 | <p>In the other tutorial we have reused the already written targets of our buildfile.
|
---|
| 212 | Now we will configure most of the testcases via java code (sometimes it's much easier
|
---|
| 213 | to write a target than doing it via java coding). What can be tested?<ul>
|
---|
| 214 | <li>not valid configured task (missing file, missing location, missing fileset)</li>
|
---|
| 215 | <li>don't find a present file</li>
|
---|
| 216 | <li>behaviour if file can't be found</li>
|
---|
| 217 | </ul>
|
---|
| 218 | Maybe you find some more testcases. But this is enough for now.<br>
|
---|
| 219 | For each of these points we create a <tt>testXX</tt> method.</p>
|
---|
| 220 |
|
---|
| 221 | <pre class="code">
|
---|
| 222 | public class FindTest extends BuildFileTest {
|
---|
| 223 |
|
---|
| 224 | ... // constructor, setUp as above
|
---|
| 225 |
|
---|
| 226 | public void testMissingFile() {
|
---|
| 227 | <b>Find find = new Find();</b>
|
---|
| 228 | try {
|
---|
| 229 | <b>find.execute();</b>
|
---|
| 230 | fail("No 'no-file'-exception thrown.");
|
---|
| 231 | } catch (Exception e) {
|
---|
| 232 | // exception expected
|
---|
| 233 | String expected = "file not set";
|
---|
| 234 | assertEquals("Wrong exception message.", expected, e.getMessage());
|
---|
| 235 | }
|
---|
| 236 | }
|
---|
| 237 |
|
---|
| 238 | public void testMissingLocation() {
|
---|
| 239 | Find find = new Find();
|
---|
| 240 | <b>find.setFile("ant.jar");</b>
|
---|
| 241 | try {
|
---|
| 242 | find.execute();
|
---|
| 243 | fail("No 'no-location'-exception thrown.");
|
---|
| 244 | } catch (Exception e) {
|
---|
| 245 | ... // similar to testMissingFile()
|
---|
| 246 | }
|
---|
| 247 | }
|
---|
| 248 |
|
---|
| 249 | public void testMissingFileset() {
|
---|
| 250 | Find find = new Find();
|
---|
| 251 | find.setFile("ant.jar");
|
---|
| 252 | find.setLocation("location.ant-jar");
|
---|
| 253 | try {
|
---|
| 254 | find.execute();
|
---|
| 255 | fail("No 'no-fileset'-exception thrown.");
|
---|
| 256 | } catch (Exception e) {
|
---|
| 257 | ... // similar to testMissingFile()
|
---|
| 258 | }
|
---|
| 259 | }
|
---|
| 260 |
|
---|
| 261 | public void testFileNotPresent() {
|
---|
| 262 | executeTarget("testFileNotPresent");
|
---|
| 263 | String result = getProject().getProperty("location.ant-jar");
|
---|
| 264 | assertNull("Property set to wrong value.", result);
|
---|
| 265 | }
|
---|
| 266 |
|
---|
| 267 | public void testFilePresent() {
|
---|
| 268 | executeTarget("testFilePresent");
|
---|
| 269 | String result = getProject().getProperty("location.ant-jar");
|
---|
| 270 | assertNotNull("Property not set.", result);
|
---|
| 271 | assertTrue("Wrong file found.", result.endsWith("ant.jar"));
|
---|
| 272 | }
|
---|
| 273 | }
|
---|
| 274 | </pre>
|
---|
| 275 |
|
---|
| 276 | <p>If we run this test class all test cases (except <i>testFileNotPresent</i>) fail. Now we
|
---|
| 277 | can implement our task, so that these test cases will pass.</p>
|
---|
| 278 |
|
---|
| 279 | <pre class="code">
|
---|
| 280 | protected void validate() {
|
---|
| 281 | if (file==null) throw new BuildException("file not set");
|
---|
| 282 | if (location==null) throw new BuildException("location not set");
|
---|
| 283 | if (filesets.size()<1) throw new BuildException("fileset not set");
|
---|
| 284 | }
|
---|
| 285 |
|
---|
| 286 | public void execute() {
|
---|
| 287 | validate(); // 1
|
---|
| 288 | String foundLocation = null;
|
---|
| 289 | for(Iterator itFSets = filesets.iterator(); itFSets.hasNext(); ) { // 2
|
---|
| 290 | FileSet fs = (FileSet)itFSets.next();
|
---|
| 291 | DirectoryScanner ds = fs.getDirectoryScanner(getProject()); // 3
|
---|
| 292 | String[] includedFiles = ds.getIncludedFiles();
|
---|
| 293 | for(int i=0; i<includedFiles.length; i++) {
|
---|
| 294 | String filename = includedFiles[i].replace('\\','/'); // 4
|
---|
| 295 | filename = filename.substring(filename.lastIndexOf("/")+1);
|
---|
| 296 | if (foundLocation==null && file.equals(filename)) {
|
---|
| 297 | File base = ds.getBasedir(); // 5
|
---|
| 298 | File found = new File(base, includedFiles[i]);
|
---|
| 299 | foundLocation = found.getAbsolutePath();
|
---|
| 300 | }
|
---|
| 301 | }
|
---|
| 302 | }
|
---|
| 303 | if (foundLocation!=null) // 6
|
---|
| 304 | getProject().setNewProperty(location, foundLocation);
|
---|
| 305 | }
|
---|
| 306 | </pre>
|
---|
| 307 |
|
---|
| 308 | <p>On <b>//1</b> we check the prerequisites for our task. Doing that in a <tt>validate</tt>-method
|
---|
| 309 | is a common way, because we separate the prerequisites from the real work. On <b>//2</b> we iterate
|
---|
| 310 | over all nested filesets. If we don't want to handle multiple filesets, the <tt>addFileset()</tt>
|
---|
| 311 | method has to reject the further calls. We can get the result of a fileset via its DirectoryScanner
|
---|
| 312 | like done in <b>//3</b>. After that we create a plattform independend String representation of
|
---|
| 313 | the file path (<b>//4</b>, can be done in other ways of course). We have to do the <tt>replace()</tt>,
|
---|
| 314 | because we work with a simple string comparison. Ant itself is platform independant and can
|
---|
| 315 | therefore run on filesystems with slash (/, e.g. Linux) or backslash (\, e.g. Windows) as
|
---|
| 316 | path separator. Therefore we have to unify that. If we found our file we create an absolute
|
---|
| 317 | path representation on <b>//5</b>, so that we can use that information without knowing the basedir.
|
---|
| 318 | (This is very important on use with multiple filesets, because they can have different basedirs
|
---|
| 319 | and the return value of the directory scanner is relative to its basedir.) Finally we store the
|
---|
| 320 | location of the file as property, if we had found one (<b>//6</b>).</p>
|
---|
| 321 |
|
---|
| 322 | <p>Ok, much more easier in this simple case would be to add the <i>file</i> as additional
|
---|
| 323 | <i>include</i> element to all filesets. But I wanted to show how to handle complex situations
|
---|
| 324 | whithout being complex :-)</p>
|
---|
| 325 |
|
---|
| 326 | <p>The test case uses the ant property <i>ant.home</i> as reference. This property is set by the
|
---|
| 327 | <tt>Launcher</tt> class which starts ant. We can use that property in our buildfiles as a
|
---|
| 328 | <a href="using.html#built-in-props">build-in property [3]</a>. But if we create a new ant
|
---|
| 329 | environment we have to set that value for our own. And we use the <code><junit></code> task in fork-mode.
|
---|
| 330 | Therefore we have do modify our buildfile:
|
---|
| 331 | <pre class="code">
|
---|
| 332 | <target name="junit" description="Runs the unit tests" depends="jar">
|
---|
| 333 | <delete dir="${junit.out.dir.xml}"/>
|
---|
| 334 | <mkdir dir="${junit.out.dir.xml}"/>
|
---|
| 335 | <junit printsummary="yes" haltonfailure="no">
|
---|
| 336 | <classpath refid="classpath.test"/>
|
---|
| 337 | <b><sysproperty key="ant.home" value="${ant.home}"/></b>
|
---|
| 338 | <formatter type="xml"/>
|
---|
| 339 | <batchtest fork="yes" todir="${junit.out.dir.xml}">
|
---|
| 340 | <fileset dir="${src.dir}" includes="**/*Test.java"/>
|
---|
| 341 | </batchtest>
|
---|
| 342 | </junit>
|
---|
| 343 | </target>
|
---|
| 344 | </pre>
|
---|
| 345 |
|
---|
| 346 |
|
---|
| 347 | <h2><a name="path">Using nested paths</a></h2>
|
---|
| 348 | <p>A task providing support for filesets is a very comfortable one. But there is another
|
---|
| 349 | possibility of bundling files: the <code><path></code>. Fileset are easy if the files are all under
|
---|
| 350 | a common base directory. But if this is not the case you have a problem. Another disadvantage
|
---|
| 351 | is its speed: if you have only a few files in a huge directory structure, why not use a
|
---|
| 352 | <code><filelist></code> instead? <code><path></code>s combines these datatypes in that way that a path contains
|
---|
| 353 | other paths, filesets, dirsets and filelists. This is why <a href="http://ant-contrib.sourceforge.net/">
|
---|
| 354 | Ant-Contribs [4]</a> <code><foreach></code> task is modified to support paths instead of filesets. So we want that,
|
---|
| 355 | too.</p>
|
---|
| 356 |
|
---|
| 357 | <p>Changing from fileset to path support is very easy:</p>
|
---|
| 358 | <pre class="code">
|
---|
| 359 | <i><b>Change java code from:</b></i>
|
---|
| 360 | private Vector filesets = new Vector();
|
---|
| 361 | public void addFileset(FileSet fileset) {
|
---|
| 362 | filesets.add(fileset);
|
---|
| 363 | }
|
---|
| 364 | <i><b>to:</b></i>
|
---|
| 365 | private Vector paths = new Vector(); *1
|
---|
| 366 | public void add<b>Path</b>(<b>Path</b> path) { *2
|
---|
| 367 | paths.add(path);
|
---|
| 368 | }
|
---|
| 369 | <i><b>and build file from:</b></i>
|
---|
| 370 | <find file="ant.jar" location="location.ant-jar">
|
---|
| 371 | <fileset dir="${ant.home}" includes="**/*.jar"/>
|
---|
| 372 | </find>
|
---|
| 373 | <i><b>to:</b></i>
|
---|
| 374 | <find file="ant.jar" location="location.ant-jar">
|
---|
| 375 | <b><path></b> *3
|
---|
| 376 | <fileset dir="${ant.home}" includes="**/*.jar"/>
|
---|
| 377 | </path>
|
---|
| 378 | </find>
|
---|
| 379 | </pre>
|
---|
| 380 | <p>On <b>*1</b> we rename only the vector. Itï¿œs just for better reading the source. On <b>*2</b>
|
---|
| 381 | we have to provide the right method: an add<i>Name</i>(<i>Type</i> t). Therefore replace the
|
---|
| 382 | fileset with path here. Finally we have to modify our buildfile on <b>*3</b> because our task
|
---|
| 383 | doesnï¿œt support nested filesets any longer. So we wrap the fileset inside a path.</p>
|
---|
| 384 |
|
---|
| 385 | <p>And now we modify the testcase. Oh, not very much to do :-) Renaming the <tt>testMissingFileset()</tt>
|
---|
| 386 | (not really a <i>must-be</i> but better itï¿œs named like the think it does) and update the
|
---|
| 387 | <i>expected</i>-String in that method (now a <tt>path not set</tt> message is expected). The more complex
|
---|
| 388 | test cases base on the buildscript. So the targets <tt>testFileNotPresent</tt> and <tt>testFilePresent</tt> have to be
|
---|
| 389 | modified in the manner described above.</p>
|
---|
| 390 |
|
---|
| 391 | <p>The test are finished. Now we have to adapt the task implementation. The easiest modification is
|
---|
| 392 | in the <tt>validate()</tt> method where we change le last line to <tt>if (paths.size()<1) throw new
|
---|
| 393 | BuildException("path not set");</tt>. In the <tt>execute()</tt> method we have a little more work.
|
---|
| 394 | ... mmmh ... in reality it's lesser work, because the Path class does the whole DirectoryScanner-handling
|
---|
| 395 | and creating-absolute-paths stuff for us. So the execute method is just:</p>
|
---|
| 396 |
|
---|
| 397 | <pre class="code">
|
---|
| 398 | public void execute() {
|
---|
| 399 | validate();
|
---|
| 400 | String foundLocation = null;
|
---|
| 401 | for(Iterator itPaths = paths.iterator(); itPaths.hasNext(); ) {
|
---|
| 402 | Path path = (<b>Path</b>)itPaths.next(); // 1
|
---|
| 403 | String[] includedFiles = <b>path.list()</b>; // 2
|
---|
| 404 | for(int i=0; i<includedFiles.length; i++) {
|
---|
| 405 | String filename = includedFiles[i].replace('\\','/');
|
---|
| 406 | filename = filename.substring(filename.lastIndexOf("/")+1);
|
---|
| 407 | if (foundLocation==null && file.equals(filename)) {
|
---|
| 408 | <b>foundLocation = includedFiles[i];</b> // 3
|
---|
| 409 | }
|
---|
| 410 | }
|
---|
| 411 | }
|
---|
| 412 | if (foundLocation!=null)
|
---|
| 413 | getProject().setNewProperty(location, foundLocation);
|
---|
| 414 | }
|
---|
| 415 | </pre>
|
---|
| 416 |
|
---|
| 417 | <p>Of course we have to do the typecase to Path on <b>//1</b>. On <b>//2</b> and <b>//3</b>
|
---|
| 418 | we see that the Path class does the work for us: no DirectoryScanner (was at 2) and no
|
---|
| 419 | creating of the absolute path (was at 3).</p>
|
---|
| 420 |
|
---|
| 421 |
|
---|
| 422 |
|
---|
| 423 | <h2><a name="returning-list">Returning a list</a></h2>
|
---|
| 424 | <p>So far so good. But could a file be on more than one place in the path? - Of course.<br>
|
---|
| 425 | And would it be good to get all of them? - It depends on ...<p>
|
---|
| 426 |
|
---|
| 427 | <p>In this section we will extend that task to support returning a list of all files.
|
---|
| 428 | Lists as property values are not supported by Ant natively. So we have to see how other
|
---|
| 429 | tasks use lists. The most famous task using lists is Ant-Contribs <code><foreach></code>. All list
|
---|
| 430 | elements are concatenated and separated with a customizable separator (default ',').</p>
|
---|
| 431 |
|
---|
| 432 | <p>So we do the following:</p>
|
---|
| 433 |
|
---|
| 434 | <pre class="code">
|
---|
| 435 | <find ... <b>delimiter=""</b>/> ... </find>
|
---|
| 436 | </pre>
|
---|
| 437 |
|
---|
| 438 | <p>If the delimiter is set we will return all found files as list with that delimiter.</p>
|
---|
| 439 |
|
---|
| 440 | <p>Therefore we have to<ul>
|
---|
| 441 | <li>provide a new attribute</li>
|
---|
| 442 | <li>collect more than the first file</li>
|
---|
| 443 | <li>delete duplicates</li>
|
---|
| 444 | <li>create the list if necessary</li>
|
---|
| 445 | <li>return that list</li>
|
---|
| 446 | </ul></p>
|
---|
| 447 |
|
---|
| 448 | <p>So we add as testcase:</p>
|
---|
| 449 | <pre class="code">
|
---|
| 450 | <b><i>in the buildfile:</i></b>
|
---|
| 451 | <target name="test.init">
|
---|
| 452 | <mkdir dir="test1/dir11/dir111"/> *1
|
---|
| 453 | <mkdir dir="test1/dir11/dir112"/>
|
---|
| 454 | ...
|
---|
| 455 | <touch file="test1/dir11/dir111/test"/>
|
---|
| 456 | <touch file="test1/dir11/dir111/not"/>
|
---|
| 457 | ...
|
---|
| 458 | <touch file="test1/dir13/dir131/not2"/>
|
---|
| 459 | <touch file="test1/dir13/dir132/test"/>
|
---|
| 460 | <touch file="test1/dir13/dir132/not"/>
|
---|
| 461 | <touch file="test1/dir13/dir132/not2"/>
|
---|
| 462 | <mkdir dir="test2"/>
|
---|
| 463 | <copy todir="test2"> *2
|
---|
| 464 | <fileset dir="test1"/>
|
---|
| 465 | </copy>
|
---|
| 466 | </target>
|
---|
| 467 |
|
---|
| 468 | <target name="testMultipleFiles" depends="use.init,<b>test.init</b>"> *3
|
---|
| 469 | <find file="test" location="location.test" <b>delimiter=";"</b>>
|
---|
| 470 | <path>
|
---|
| 471 | <fileset dir="test1"/>
|
---|
| 472 | <fileset dir="test2"/>
|
---|
| 473 | </path>
|
---|
| 474 | </find>
|
---|
| 475 | <delete> *4
|
---|
| 476 | <fileset dir="test1"/>
|
---|
| 477 | <fileset dir="test2"/>
|
---|
| 478 | </delete>
|
---|
| 479 | </target>
|
---|
| 480 |
|
---|
| 481 | <b><i>in the test class:</i></b>
|
---|
| 482 | public void testMultipleFiles() {
|
---|
| 483 | executeTarget("testMultipleFiles");
|
---|
| 484 | String result = getProject().getProperty("location.test");
|
---|
| 485 | assertNotNull("Property not set.", result);
|
---|
| 486 | assertTrue("Only one file found.", result.indexOf(";") > -1);
|
---|
| 487 | }
|
---|
| 488 | </pre>
|
---|
| 489 |
|
---|
| 490 | <p>Now we need a directory structure where we CAN find files with the same
|
---|
| 491 | name in different directories. Because we can't sure to have one we create
|
---|
| 492 | one on <b>*1</b> and <b>*2</b>. And of course we clean up that on <b>*4</b>. The creation
|
---|
| 493 | can be done inside our test target or in a separate one, which will be better
|
---|
| 494 | for reuse later (<b>*3</b>).
|
---|
| 495 |
|
---|
| 496 | <p>The task implementation is modified as followed:</p>
|
---|
| 497 |
|
---|
| 498 | <pre class="code">
|
---|
| 499 | private Vector foundFiles = new Vector();
|
---|
| 500 | ...
|
---|
| 501 | private String delimiter = null;
|
---|
| 502 | ...
|
---|
| 503 | public void setDelimiter(String delim) {
|
---|
| 504 | delimiter = delim;
|
---|
| 505 | }
|
---|
| 506 | ...
|
---|
| 507 | public void execute() {
|
---|
| 508 | validate();
|
---|
| 509 | // find all files
|
---|
| 510 | for(Iterator itPaths = paths.iterator(); itPaths.hasNext(); ) {
|
---|
| 511 | Path path = (Path)itPaths.next();
|
---|
| 512 | String[] includedFiles = path.list();
|
---|
| 513 | for(int i=0; i<includedFiles.length; i++) {
|
---|
| 514 | String filename = includedFiles[i].replace('\\','/');
|
---|
| 515 | filename = filename.substring(filename.lastIndexOf("/")+1);
|
---|
| 516 | if (file.equals(filename) && <b>!foundFiles.contains(includedFiles[i]</b>)) { // 1
|
---|
| 517 | foundFiles.add(includedFiles[i]);
|
---|
| 518 | }
|
---|
| 519 | }
|
---|
| 520 | }
|
---|
| 521 |
|
---|
| 522 | // create the return value (list/single)
|
---|
| 523 | String rv = null;
|
---|
| 524 | if (foundFiles.size() > 0) { // 2
|
---|
| 525 | if (delimiter==null) {
|
---|
| 526 | // only the first
|
---|
| 527 | rv = (String)foundFiles.elementAt(0);
|
---|
| 528 | } else {
|
---|
| 529 | // create list
|
---|
| 530 | StringBuffer list = new StringBuffer();
|
---|
| 531 | for(Iterator it=foundFiles.iterator(); it.hasNext(); ) { // 3
|
---|
| 532 | list.append(it.next());
|
---|
| 533 | if (<b>it.hasNext()</b>) list.append(delimiter); // 4
|
---|
| 534 | }
|
---|
| 535 | rv = list.toString();
|
---|
| 536 | }
|
---|
| 537 | }
|
---|
| 538 |
|
---|
| 539 | // create the property
|
---|
| 540 | if (rv!=null)
|
---|
| 541 | getProject().setNewProperty(location, rv);
|
---|
| 542 | }
|
---|
| 543 | </pre>
|
---|
| 544 |
|
---|
| 545 | <p>The algorithm does: finding all files, creating the return value depending on the users
|
---|
| 546 | wish, returning the value as property. On <b>//1</b> we eliminates the duplicates. <b>//2</b>
|
---|
| 547 | ensures that we create the return value only if we have found one file. On <b>//3</b> we
|
---|
| 548 | iterate over all found files and <b>//4</b> ensures that the last entry has no trailing
|
---|
| 549 | delimiter.</p>
|
---|
| 550 |
|
---|
| 551 | <p>Ok, first searching for all files and then returning only the first one ... You can
|
---|
| 552 | tune the performance of your own :-)</p>
|
---|
| 553 |
|
---|
| 554 |
|
---|
| 555 | <h2><a name="documentation">Documentation</a></h2>
|
---|
| 556 | <p>A task is useless if the only who is able to code the buildfile is the task developer
|
---|
| 557 | (and he only the next few weeks :-). So documentation is also very important. In which
|
---|
| 558 | form you do that depends on your favourite. But inside Ant there is a common format and
|
---|
| 559 | it has advantages if you use that: all task users know that form, this form is requested if
|
---|
| 560 | you decide to contribute your task. So we will doc our task in that form.</p>
|
---|
| 561 |
|
---|
| 562 | <p>If you have a look at the manual page of the <a href="CoreTasks/java.html">java [5]</a>
|
---|
| 563 | task you will see<ul>
|
---|
| 564 | <li>it is plain html</li>
|
---|
| 565 | <li>starts with the name</li>
|
---|
| 566 | <li>has sections: description, parameters, nested elements, (maybe return codes) and (most
|
---|
| 567 | important :-) examples</li>
|
---|
| 568 | <li>parameters are listed in a table with columns for attribute name, its description and whether
|
---|
| 569 | it's required (if you add a feature after an Ant release, provide a <tt>since Ant xx</tt>
|
---|
| 570 | statement when it's introduced)</li>
|
---|
| 571 | <li>describe the nested elements (since-statement if necessary)</li>
|
---|
| 572 | <li>provide one or more useful examples; first code then description</li>
|
---|
| 573 | </ul>
|
---|
| 574 | As a template we have:
|
---|
| 575 |
|
---|
| 576 | <pre class="code">
|
---|
| 577 | <html>
|
---|
| 578 |
|
---|
| 579 | <head>
|
---|
| 580 | <meta http-equiv="Content-Language" content="en-us">
|
---|
| 581 | <title> <b>Taskname</b> Task</title>
|
---|
| 582 | </head>
|
---|
| 583 |
|
---|
| 584 | <body>
|
---|
| 585 |
|
---|
| 586 | <h2><a name="<i>taskname</i>"><b>Taskname</b></a></h2>
|
---|
| 587 | <h3>Description</h3>
|
---|
| 588 | <p> <b>Describe the task.</b></p>
|
---|
| 589 |
|
---|
| 590 | <h3>Parameters</h3>
|
---|
| 591 | <table border="1" cellpadding="2" cellspacing="0">
|
---|
| 592 | <tr>
|
---|
| 593 | <td valign="top"><b>Attribute</b></td>
|
---|
| 594 | <td valign="top"><b>Description</b></td>
|
---|
| 595 | <td align="center" valign="top"><b>Required</b></td>
|
---|
| 596 | </tr>
|
---|
| 597 |
|
---|
| 598 | <b>do this html row for each attribute (including inherited attributes)</b>
|
---|
| 599 | <tr>
|
---|
| 600 | <td valign="top">classname</td>
|
---|
| 601 | <td valign="top">the Java class to execute.</td>
|
---|
| 602 | <td align="center" valign="top">Either jar or classname</td>
|
---|
| 603 | </tr>
|
---|
| 604 |
|
---|
| 605 | </table>
|
---|
| 606 |
|
---|
| 607 | <h3>Parameters specified as nested elements</h3>
|
---|
| 608 |
|
---|
| 609 | <b>Describe each nested element (including inherited)</b>
|
---|
| 610 | <h4>your nested element</b></h4>
|
---|
| 611 | <p> <b>description</b> </p>
|
---|
| 612 | <p><em>since Ant 1.6</em>.</p>
|
---|
| 613 |
|
---|
| 614 | <h3>Examples</h3>
|
---|
| 615 | <pre>
|
---|
| 616 | <b>A code sample; don't forget to escape the < of the tags with &lt;</b>
|
---|
| 617 | </pre>
|
---|
| 618 | <b>what should that example do?</b>
|
---|
| 619 |
|
---|
| 620 | </body>
|
---|
| 621 | </html>
|
---|
| 622 | </pre>
|
---|
| 623 |
|
---|
| 624 | <p>For our task we have <a href="CoreTasks/find.html">that [6]</a>:</p>
|
---|
| 625 | <pre class="code">
|
---|
| 626 | <html>
|
---|
| 627 |
|
---|
| 628 | <head>
|
---|
| 629 | <meta http-equiv="Content-Language" content="en-us">
|
---|
| 630 | <title> Find Task</title>
|
---|
| 631 | </head>
|
---|
| 632 |
|
---|
| 633 | <body>
|
---|
| 634 |
|
---|
| 635 | <h2><a name="find">Find</a></h2>
|
---|
| 636 | <h3>Description</h3>
|
---|
| 637 | <p>Searchs in a given path for a file and returns the absolute to it as property.
|
---|
| 638 | If delimiter is set this task returns all found locations.</p>
|
---|
| 639 |
|
---|
| 640 | <h3>Parameters</h3>
|
---|
| 641 | <table border="1" cellpadding="2" cellspacing="0">
|
---|
| 642 | <tr>
|
---|
| 643 | <td valign="top"><b>Attribute</b></td>
|
---|
| 644 | <td valign="top"><b>Description</b></td>
|
---|
| 645 | <td align="center" valign="top"><b>Required</b></td>
|
---|
| 646 | </tr>
|
---|
| 647 | <tr>
|
---|
| 648 | <td valign="top">file</td>
|
---|
| 649 | <td valign="top">The name of the file to search.</td>
|
---|
| 650 | <td align="center" valign="top">yes</td>
|
---|
| 651 | </tr>
|
---|
| 652 | <tr>
|
---|
| 653 | <td valign="top">location</td>
|
---|
| 654 | <td valign="top">The name of the property where to store the location</td>
|
---|
| 655 | <td align="center" valign="top">yes</td>
|
---|
| 656 | </tr>
|
---|
| 657 | <tr>
|
---|
| 658 | <td valign="top">delimiter</td>
|
---|
| 659 | <td valign="top">A delimiter to use when returning the list</td>
|
---|
| 660 | <td align="center" valign="top">only if the list is required</td>
|
---|
| 661 | </tr>
|
---|
| 662 | </table>
|
---|
| 663 |
|
---|
| 664 | <h3>Parameters specified as nested elements</h3>
|
---|
| 665 |
|
---|
| 666 | <h4>path</h4>
|
---|
| 667 | <p>The path where to search the file.</p>
|
---|
| 668 |
|
---|
| 669 | <h3>Examples</h3>
|
---|
| 670 | <pre>
|
---|
| 671 | <find file="ant.jar" location="loc">
|
---|
| 672 | <path>
|
---|
| 673 | <fileset dir="${ant.home}"/>
|
---|
| 674 | <path>
|
---|
| 675 | </find>
|
---|
| 676 | </pre>
|
---|
| 677 | Searches in Ants home directory for a file <i>ant.jar</i> and stores its location in
|
---|
| 678 | property <i>loc</i> (should be ANT_HOME/bin/ant.jar).
|
---|
| 679 |
|
---|
| 680 | <pre>
|
---|
| 681 | <find file="ant.jar" location="loc" delimiter=";">
|
---|
| 682 | <path>
|
---|
| 683 | <fileset dir="C:/"/>
|
---|
| 684 | <path>
|
---|
| 685 | </find>
|
---|
| 686 | <echo>ant.jar found in: ${loc}</echo>
|
---|
| 687 | </pre>
|
---|
| 688 | Searches in Windows C: drive for all <i>ant.jar</i> and stores their locations in
|
---|
| 689 | property <i>loc</i> delimited with <i>';'</i>. (should need a long time :-)
|
---|
| 690 | After that it prints out the result (e.g. C:/ant-1.5.4/bin/ant.jar;C:/ant-1.6/bin/ant.jar).
|
---|
| 691 |
|
---|
| 692 | </body>
|
---|
| 693 | </html>
|
---|
| 694 | </pre>
|
---|
| 695 |
|
---|
| 696 |
|
---|
| 697 | <h2><a name="contribute">Contribute the new task</a></h2>
|
---|
| 698 | If we decide to contribute our task, we should do some things:<ul>
|
---|
| 699 | <li>is our task welcome? :-) Simply ask on the user list</li>
|
---|
| 700 | <li>is the right package used? </li>
|
---|
| 701 | <li>is the code conform to the styleguide?</li>
|
---|
| 702 | <li>do all tests pass? </li>
|
---|
| 703 | <li>does the code compile on JDK 1.2 (and passes all tests there)?</li>
|
---|
| 704 | <li>code under Apache license</li>
|
---|
| 705 | <li>create a patch file</li>
|
---|
| 706 | <li>publishing that patch file</li>
|
---|
| 707 | </ul>
|
---|
| 708 | The <a href="../ant_task_guidelines.html">Ant Task Guidelines [7]</a> support additional
|
---|
| 709 | information on that.</p>
|
---|
| 710 |
|
---|
| 711 | <p>Now we will check the "Checklist before submitting a new task" described in that guideline.
|
---|
| 712 | <ul>
|
---|
| 713 | <li>Java file begins with Apache license statement. <b><i>must do that</i></b></li>
|
---|
| 714 | <li>Task does not depend on GPL or LGPL code. <b><i>ok</i></b></li>
|
---|
| 715 | <li>Source code complies with style guidelines <b><i>have to check (checkstyle)</i></b></li>
|
---|
| 716 | <li>Code compiles and runs on Java1.2 <b><i>have to try</i></b></li>
|
---|
| 717 | <li>Member variables are private, and provide public accessor methods
|
---|
| 718 | if access is actually needed. <b><i>have to check (checkstyle)</i></b></li>
|
---|
| 719 | <li><i>Maybe</i> Task has failonerror attribute to control failure behaviour <b><i>hasn't</i></b></li>
|
---|
| 720 | <li>New test cases written and succeed <b><i>passed on JDK 1.4, have to try on JDK 1.2</i></b></li>
|
---|
| 721 | <li>Documentation page written <b><i>ok</i></b></li>
|
---|
| 722 | <li>Example task declarations in the documentation tested. <b><i>ok (used in tests)</i></b></li>
|
---|
| 723 | <li>Patch files generated using cvs diff -u <b><i>to do</i></b></li>
|
---|
| 724 | <li>patch files include a patch to defaults.properties to register the
|
---|
| 725 | tasks <b><i>to do</i></b></li>
|
---|
| 726 | <li>patch files include a patch to coretasklist.html or
|
---|
| 727 | optionaltasklist.html to link to the new task page <b><i>to do</i></b></li>
|
---|
| 728 | <li>Message to dev contains [SUBMIT] and task name in subject <b><i>to do</i></b></li>
|
---|
| 729 | <li>Message body contains a rationale for the task <b><i>to do</i></b></li>
|
---|
| 730 | <li>Message attachments contain the required files -source, documentation,
|
---|
| 731 | test and patches zipped up to escape the HTML filter. <b><i>to do</i></b></li>
|
---|
| 732 | </ul>
|
---|
| 733 |
|
---|
| 734 |
|
---|
| 735 | <h3>Package / Directories</h3>
|
---|
| 736 | <p>This task does not depend on any external library. Therefore we can use this as
|
---|
| 737 | a core task. This task contains only one class. So we can use the standard package
|
---|
| 738 | for core tasks: <tt>org.apache.tools.ant.taskdefs</tt>. Implementations are in the
|
---|
| 739 | directory <tt>src/main</tt>, tests in <tt>src/testcases</tt> and buildfiles for
|
---|
| 740 | tests in <tt>src/etc/testcases</tt>.</p>
|
---|
| 741 |
|
---|
| 742 | <p>Now we integrate our work into Ants distribution. So first we do an update of our
|
---|
| 743 | cvs tree. If not done yet, you have to checkout the ant module from Apaches cvs server
|
---|
| 744 | as described in <a href="http://ant.apache.org/cvs.html">Access the Source Tree (AnonCVS)
|
---|
| 745 | [8]</a> (password is <i>anoncvs</i>):<pre class="output">
|
---|
| 746 | cvs -d :pserver:[email protected]:/home/cvspublic login //1
|
---|
| 747 | cvs -d :pserver:[email protected]:/home/cvspublic checkout ant //2
|
---|
| 748 | </pre>
|
---|
| 749 | If you have a local copy of Ants sources just do an update
|
---|
| 750 | <pre class="output">
|
---|
| 751 | cvs -d :pserver:[email protected]:/home/cvspublic login
|
---|
| 752 | cd ant //3
|
---|
| 753 | cvs -d :pserver:[email protected]:/home/cvspublic update //4
|
---|
| 754 | </pre></p>
|
---|
| 755 |
|
---|
| 756 | <p>We use the <i>-d</i> flag on <b>//1</b> to specifiy the cvs directory. You can
|
---|
| 757 | specify the environment variable CVSROOT with that value and after that you havenï¿œt
|
---|
| 758 | to use that flag any more. On <b>//2</b> we get the whole cvs tree of ant. (Sorry,
|
---|
| 759 | but that uses a lot of time ... 10 up to 30 minutes are not unusual ... but this has
|
---|
| 760 | to be done only once :-). A cvs update doesn't use a modulename but you have to be
|
---|
| 761 | inside the directory. Therefore we go into that on <b>//3</b> and do the update
|
---|
| 762 | on <b>//4</b>.</p>
|
---|
| 763 |
|
---|
| 764 | <p>Now we will build our Ant distribution and do a test. So we can see if there
|
---|
| 765 | are any tests failing on our machine. (We can ignore these failing tests on later
|
---|
| 766 | steps; windows syntax used here- translate to xNIX if needed):
|
---|
| 767 | <pre class="output">
|
---|
| 768 | ANTHOME> build // 1
|
---|
| 769 | ANTHOME> set ANT_HOME=%CD%\dist // 2
|
---|
| 770 | ANTHOME> ant test -Dtest.haltonfailure=false // 3
|
---|
| 771 | </pre>
|
---|
| 772 |
|
---|
| 773 | First we have to build our Ant distribution (<b>//1</b>). On <b>//2</b> we set the ANT_HOME
|
---|
| 774 | environment variable to the directory where the new created distribution is stored
|
---|
| 775 | (%CD% is expanded to the current directory on Windows 2000 and XP, on 9x and NT
|
---|
| 776 | write it out). On <b>//3</b> we let Ant do all the tests (which enforced a compile
|
---|
| 777 | of all tests) without stopping on first failure.</p>
|
---|
| 778 |
|
---|
| 779 | <p>Next we apply our work onto Ants sources. Because we haven't modified any, this is
|
---|
| 780 | a relative simple step. <i>(Because I have a local copy of Ant and usually contribute my
|
---|
| 781 | work, I work on the local copy just from the beginning. The advantage: this step isn't
|
---|
| 782 | necessary and saves a lot of work if you modify existing source :-)</i>.
|
---|
| 783 |
|
---|
| 784 | <ul>
|
---|
| 785 | <li>move the Find.java to ANTHOME/src/main/org/apache/tools/ant/taskdefs/Find.java </li>
|
---|
| 786 | <li>move the FindTest.java to ANTHOME/src/testcases/org/apache/tools/ant/taskdefs/FindTest.java </li>
|
---|
| 787 | <li>move the build.xml to ANTHOME/src/etc/testcases/taskdefs/<b>find.xml</b> (!!! renamed !!!)</li>
|
---|
| 788 | <li>add a <tt>package org.apache.tools.ant.taskdefs;</tt> at the beginning of the two java files </li>
|
---|
| 789 | <li>delete all stuff from find.xml keeping the targets "testFileNotPresent", "testFilePresent",
|
---|
| 790 | "test.init" and "testMultipleFiles" </li>
|
---|
| 791 | <li>delete the dependency to "use.init" in the find.xml </li>
|
---|
| 792 | <li>in FindTest.java change the line <tt>configureProject("build.xml");</tt> to
|
---|
| 793 | <tt>configureProject("src/etc/testcases/taskdefs/find.xml");</tt> </li>
|
---|
| 794 | <li>move the find.html to ANTHOME/docs/manual/CoreTasks/find.html </li>
|
---|
| 795 | <li>add a <tt><a href="CoreTasks/find.html">Find</a><br></tt>
|
---|
| 796 | in the ANTHOME/docs/manual/coretasklist.html </li>
|
---|
| 797 | </ul>
|
---|
| 798 |
|
---|
| 799 | Now our modifications are done and we will retest it:
|
---|
| 800 | <pre class="output">
|
---|
| 801 | ANTHOME> build
|
---|
| 802 | ANTHOME> ant run-single-test // 1
|
---|
| 803 | -Dtestcase=org.apache.tools.ant.taskdefs.FindTest // 2
|
---|
| 804 | -Dtest.haltonfailure=false
|
---|
| 805 | </pre>
|
---|
| 806 | Because we only want to test our new class, we use the target for single tests, specify
|
---|
| 807 | the test to use and configure not to halt on the first failure - we want to see all
|
---|
| 808 | failures of our own test (<b>//1 + 2</b>).</p>
|
---|
| 809 |
|
---|
| 810 | <p>And ... oh, all tests fail: <i>Ant could not find the task or a class this task relies upon.</i></p>
|
---|
| 811 |
|
---|
| 812 | <p>Ok: in the earlier steps we told Ant to use the Find class for the <code><find></code> task (remember the
|
---|
| 813 | <code><taskdef></code> statement in the "use.init" target). But now we want to introduce that task as
|
---|
| 814 | a core task. And nobody wants to taskdef the javac, echo, ... So what to do? The answer is the
|
---|
| 815 | src/main/.../taskdefs/default.properties. Here is the mapping between taskname and implementing
|
---|
| 816 | class done. So we add a <tt>find=org.apache.tools.ant.taskdefs.Find</tt> as the last core
|
---|
| 817 | task (just before the <tt># optional tasks</tt> line). Now a second try:
|
---|
| 818 | <pre class="output">
|
---|
| 819 | ANTHOME> build // 1
|
---|
| 820 | ANTHOME> ant run-single-test
|
---|
| 821 | -Dtestcase=org.apache.tools.ant.taskdefs.FindTest
|
---|
| 822 | -Dtest.haltonfailure=false
|
---|
| 823 | </pre>
|
---|
| 824 | We have to rebuild (<b>//1</b>) Ant because the test look in the %ANT_HOME%\lib\ant.jar
|
---|
| 825 | (more precise: on the classpath) for the properties file. And we have only modified it in the
|
---|
| 826 | source path. So we have to rebuild that jar. But now all tests pass and we check whether our class
|
---|
| 827 | breaks some other tests.
|
---|
| 828 | <pre class="output">
|
---|
| 829 | ANTHOME> ant test -Dtest.haltonfailure=false
|
---|
| 830 | </pre>
|
---|
| 831 | Because there are a lot of tests this step requires a little bit of time. So use the <i>run-single-test</i>
|
---|
| 832 | during development and do the <i>test</i> only at the end (maybe sometimes during development too).
|
---|
| 833 | We use the <i>-Dtest.haltonfailure=false</i> here because there could be other tests fail and we have
|
---|
| 834 | to look into them.</p>
|
---|
| 835 |
|
---|
| 836 | <p>This test run should show us two things: our test will run and the number of failing tests
|
---|
| 837 | is the same as directly after the cvs update (without our modifications).</p>
|
---|
| 838 |
|
---|
| 839 |
|
---|
| 840 |
|
---|
| 841 | <h3>Apache license statement</h3>
|
---|
| 842 | <p>Simply copy the license text from one the other source from the Ant source tree.</p>
|
---|
| 843 |
|
---|
| 844 |
|
---|
| 845 | <h3>Test on JDK 1.2</h3>
|
---|
| 846 | <p>Until version 1.5 Ant must be able to run on a JDK 1.1. With version 1.6 this is not a
|
---|
| 847 | requisite any more. But JDK 1.2 is a must-to-work-with. So we have to test that. You can download older
|
---|
| 848 | JDKs from <a href="http://java.sun.com/products/archive/index.html">Sun [9]</a>.</p>
|
---|
| 849 |
|
---|
| 850 | <p>Clean the ANT_HOME variable, delete the <i>build, bootstrap</i> and <i>dist</i> directory
|
---|
| 851 | and point JAVA_HOME to the JDK 1.2 home directory. Then do the <tt>build</tt>, set ANT_HOME
|
---|
| 852 | and run <tt>ant test</tt> (like above).</p>
|
---|
| 853 |
|
---|
| 854 | <p>Our test should pass.</p>
|
---|
| 855 |
|
---|
| 856 |
|
---|
| 857 |
|
---|
| 858 | <h3>Checkstyle</h3>
|
---|
| 859 | <p>There are many things we have to ensure. Indentation with 4 spaces, blanks here and there, ...
|
---|
| 860 | (all described in the <a href="../ant_task_guidelines.html">Ant Task Guidelines [7]</a> which
|
---|
| 861 | includes the <a href="http://java.sun.com/docs/codeconv/html/CodeConvTOC.doc.html">Sun code style
|
---|
| 862 | [10]</a>). Because there are so many things we would be happy to have a tool for do the checks.
|
---|
| 863 | There is one: checkstyle. Checkstyle is available at <a href="http://checkstyle.sourceforge.net/">
|
---|
| 864 | Sourceforge [11]</a> and Ant provides with the <tt>check.xml</tt> a buildfile which will do the job
|
---|
| 865 | for us.</p>
|
---|
| 866 |
|
---|
| 867 | <p>Download it and put the checkstyle-*-all.jar into your %USERPROFILE%\.ant\lib directory.
|
---|
| 868 | All jar's stored there are available to Ant so you haven't to add it to you %ANT_HOME%\lib
|
---|
| 869 | directory (this feature was added with Ant 1.6).</p>
|
---|
| 870 |
|
---|
| 871 | <p>So we will run the tests with
|
---|
| 872 | <pre class="output">
|
---|
| 873 | ANTHOME> ant -f check.xml checkstyle htmlreport
|
---|
| 874 | </pre>
|
---|
| 875 | I prefer the HTML report because there are lots of messages and we can navigate faster.
|
---|
| 876 | Open the ANTHOME/build/reports/checkstyle/html/index.html and navigate to the Find.java. Now we
|
---|
| 877 | see that there are some errors: missing whitespaces, unused imports, missing javadocs. So we have
|
---|
| 878 | to do that.</p>
|
---|
| 879 |
|
---|
| 880 | <p>Hint: start at the <b>buttom</b> of the file so the line numbers in the report will keep
|
---|
| 881 | up to date and you will find the next error place much more easier without redoing the checkstyle.</p>
|
---|
| 882 |
|
---|
| 883 | <p>After cleaning up the code according to the messages we delete the reports directory and
|
---|
| 884 | do a second checkstyle run. Now our task isn't listed. That's fine :-)</p>
|
---|
| 885 |
|
---|
| 886 |
|
---|
| 887 |
|
---|
| 888 | <!--
|
---|
| 889 | Couldnt create the diff that way for myself, but that should be documented.
|
---|
| 890 | But on the other hand this tutorial should not be forgotten any longer so I
|
---|
| 891 | comment that out. JHM
|
---|
| 892 | <h3>Creating the diff</h3>
|
---|
| 893 | <p>Creating a diff for Ant is very easy: just start <tt>ant -f patch.xml</tt> and all is done
|
---|
| 894 | automatically. This step requires a cvs executable in your path and internet access (more precise:
|
---|
| 895 | cvs access). As a result we get a file <i> XXX </i>.</p>
|
---|
| 896 | -->
|
---|
| 897 |
|
---|
| 898 |
|
---|
| 899 | <h3>Publish the task</h3>
|
---|
| 900 | <p>Finally we publish that archive. As described in the <a href="../ant_task_guidelines.html">
|
---|
| 901 | Ant Task Guidelines [7]</a> we can post it on the developer mailinglist or we create a BugZilla
|
---|
| 902 | entry. For both we need some information:</p>
|
---|
| 903 |
|
---|
| 904 | <table border="1" cellpadding="2" cellspacing="0">
|
---|
| 905 | <tr>
|
---|
| 906 | <th>subject</th>
|
---|
| 907 | <td><i>short description</i></td>
|
---|
| 908 | <td>Task for finding files in a path</td>
|
---|
| 909 | </tr>
|
---|
| 910 | <tr>
|
---|
| 911 | <th>body</th>
|
---|
| 912 | <td><i>more details about the path</i></td>
|
---|
| 913 | <td>This new task looks inside a nested <code><path/></code> for occurrences of a file and stores
|
---|
| 914 | all locations as a property. See the included manual for details.</td>
|
---|
| 915 | </tr>
|
---|
| 916 | <tr>
|
---|
| 917 | <th>attachements</th>
|
---|
| 918 | <td><i>all files needed to apply the path</td>
|
---|
| 919 | <td>Archive containing a patch with the new and modified resources</td>
|
---|
| 920 | </tr>
|
---|
| 921 | </table>
|
---|
| 922 |
|
---|
| 923 | <p>Sending an email with these information is very easy and I think I haven't to show that.
|
---|
| 924 | The other way - BugZilla - is slightly more difficult. But it has the advantage that entries
|
---|
| 925 | will not be forgotten (once per week a report is generated). So I will show this way.</p>
|
---|
| 926 |
|
---|
| 927 | <p>You must have a BugZilla account for that. So open the <a href="http://issues.apache.org/bugzilla/">
|
---|
| 928 | BugZilla Main Page [12]</a> and follow the link
|
---|
| 929 | <a href="http://issues.apache.org/bugzilla/createaccount.cgi">Open a new Bugzilla account [13]</a>
|
---|
| 930 | and the steps described there if you haven't one.</p>
|
---|
| 931 |
|
---|
| 932 | <ol>
|
---|
| 933 | <li>From the BugZilla main page choose <a href="http://issues.apache.org/bugzilla/enter_bug.cgi">Enter
|
---|
| 934 | a new bug report [14]</a></li>
|
---|
| 935 | <li>Choose "Ant" as product </li>
|
---|
| 936 | <li>Version is the last "Alpha (nightly)" (at this time 1.7)</li>
|
---|
| 937 | <li>Component is "Core tasks"</li>
|
---|
| 938 | <li>Plattform and Severity are ok with "Other" and "Normal"</li>
|
---|
| 939 | <li>Initial State is ok with "New"</li>
|
---|
| 940 | <li>Same with the empy "Assigned to"</li>
|
---|
| 941 | <li>It is not required to add yourself as CC, because you are the reporter and therefore will be
|
---|
| 942 | informed on changes</li>
|
---|
| 943 | <li>URL: no url required</li>
|
---|
| 944 | <li>Summary: add the <i>subject</i> from the table</li>
|
---|
| 945 | <li>Description: add the <i>body</i> from the table</li>
|
---|
| 946 | <li>Then press "Commit"</li>
|
---|
| 947 | <li>After redirecting to the new created bug entry click "Create a New Attachment"</li>
|
---|
| 948 | <li>Enter the path to your local path file into "File" or choose it via the "File"'s
|
---|
| 949 | button.</li>
|
---|
| 950 | <li>Enter a short description into "Description", so that you could guess, what the
|
---|
| 951 | path file includes. Here we could add "Initial Patch".</li>
|
---|
| 952 | <li>The "Content Type" is "auto-detect". You could use the "patch" type, if you only
|
---|
| 953 | provide a single path file, but we want do upload more that one, included in our
|
---|
| 954 | patch.zip.</li>
|
---|
| 955 | <li>Then press "Commit"</li>
|
---|
| 956 | </ol>
|
---|
| 957 | Now the new task is uploaded into the bug database.
|
---|
| 958 |
|
---|
| 959 |
|
---|
| 960 | <h2><a name="resources">Resources</a></h2>
|
---|
| 961 | [1] <a href="tutorial-writing-tasks.html">tutorial-writing-tasks.html</a><br>
|
---|
| 962 | [2] <a href="tutorial-tasks-filesets-properties.zip">tutorial-tasks-filesets-properties.zip</a><br>
|
---|
| 963 | [3] <a href="using.html#built-in-props">using.html#built-in-props</a><br>
|
---|
| 964 | [4] <a href="http://ant-contrib.sourceforge.net/">http://ant-contrib.sourceforge.net/</a><br>
|
---|
| 965 | [5] <a href="CoreTasks/java.html">CoreTasks/java.html</a><br>
|
---|
| 966 | [6] <a href="CoreTasks/find.html">CoreTasks/find.html</a><br>
|
---|
| 967 | [7] <a href="../ant_task_guidelines.html">../ant_task_guidelines.html</a><br>
|
---|
| 968 | [8] <a href="http://ant.apache.org/cvs.html">http://ant.apache.org/cvs.html</a><br>
|
---|
| 969 | [9] <a href="http://java.sun.com/products/archive/index.html">http://java.sun.com/products/archive/index.html</a><br>
|
---|
| 970 | [10] <a href="http://java.sun.com/docs/codeconv/html/CodeConvTOC.doc.html">http://java.sun.com/docs/codeconv/html/CodeConvTOC.doc.html</a><br>
|
---|
| 971 | [11] <a href="http://checkstyle.sourceforge.net/">http://checkstyle.sourceforge.net/</a><br>
|
---|
| 972 | [12] <a href="http://issues.apache.org/bugzilla/">http://issues.apache.org/bugzilla/</a><br>
|
---|
| 973 | [13] <a href="http://issues.apache.org/bugzilla/createaccount.cgi">http://issues.apache.org/bugzilla/createaccount.cgi</a><br>
|
---|
| 974 | [14] <a href="http://issues.apache.org/bugzilla/enter_bug.cgi">http://issues.apache.org/bugzilla/enter_bug.cgi</a><br>
|
---|
| 975 |
|
---|
| 976 |
|
---|
| 977 | <!--
|
---|
| 978 | TODO:
|
---|
| 979 | - how to create a path (path.xml / command line)
|
---|
| 980 | -->
|
---|
| 981 |
|
---|
| 982 |
|
---|
| 983 |
|
---|
| 984 |
|
---|
| 985 |
|
---|
| 986 | </body>
|
---|
| 987 | </html>
|
---|