You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@ant.apache.org by bo...@apache.org on 2005/02/15 14:30:51 UTC

cvs commit: ant/proposal/sandbox/svn/src/main/org/apache/tools/ant/taskdefs/svn SvnRevisionDiff.java SvnEntry.java antlib.xml

bodewig     2005/02/15 05:30:51

  Modified:    proposal/sandbox/svn/src/main/org/apache/tools/ant/taskdefs/svn
                        SvnEntry.java antlib.xml
  Added:       proposal/sandbox/svn/docs revisiondiff.html
               proposal/sandbox/svn/src/etc revisiondiff.xsl
               proposal/sandbox/svn/src/etc/testcases revisiondiff.xml
               proposal/sandbox/svn/src/main/org/apache/tools/ant/taskdefs/svn
                        SvnRevisionDiff.java
  Log:
  Add revisiondiff to match cvstagdiff, well, sort-of
  
  Revision  Changes    Path
  1.1                  ant/proposal/sandbox/svn/docs/revisiondiff.html
  
  Index: revisiondiff.html
  ===================================================================
  <html>
  <head>
  <meta http-equiv="Content-Language" content="en-us">
  <title>RevisionDiff Task</title>
  </head>
  <body>
  <h2><a name="revisiondiff">RevisionDiff</a></h2>
  <h3>Description</h3>
  
  <p>Generates an XML-formatted report file of the changes between two
  revisions recorded in a <a href="http://subversion.tigris.org/"
  target="_top">Subversion</a> repository. </p>
  
  <p><b>Important:</b> This task needs "svn" on the path. If it isn't,
  you will get an error (such as error 2 on windows). If
  <code>&lt;svn&gt;</code> doesn't work, try to execute
  <code>svn.exe</code> from the command line in the target directory in
  which you are working.</p>
  
  <h3>Parameters</h3>
  <table border="1" cellpadding="2" cellspacing="0">
    <tr>
      <td valign="top"><b>Attribute</b></td>
      <td valign="top"><b>Description</b></td>
      <td align="center" valign="top"><b>Required</b></td>
    </tr>
    <tr>
      <td valign="top">start</td>
      <td valign="top">The earliest revision from which diffs are to be
       included in the report.</td>
      <td align="center" valign="top">Yes.</td>
    </tr>
    <tr>
      <td valign="top">end</td>
      <td valign="top">The latest revision from which diffs are to be
       included in the report.</td>
      <td align="center" valign="top">Yes.</td>
    </tr>
    <tr>
      <td valign="top">destfile</td>
      <td valign="top">The file in which to write the diff report.</td>
      <td align="center" valign="top">Yes</td>
    </tr>
  </table>
  
  <h3>Parameters inherited from the <code>svn</code> task</h3>
  <table border="1" cellpadding="2" cellspacing="0">
    <tr>
      <td valign="top"><b>Attribute</b></td>
      <td valign="top"><b>Description</b></td>
      <td align="center" valign="top"><b>Required</b></td>
    </tr>
    <tr>
      <td valign="top">svnURL</td>
      <td valign="top">the svn URL to diff.</td>
      <td align="center" valign="top">No</td>
    </tr>
    <tr>
      <td valign="top">failonerror</td>
      <td valign="top">Stop the buildprocess if the command exits with a
        returncode other than 0. Defaults to false</td>
      <td align="center" valign="top">No</td>
    </tr>
  </table>
  
  <h3>Examples</h3>
  <pre>  &lt;revisiondiff
                  svnURL=&quot;http://svn.apache.org/repos/asf/jakarta/bcel/trunk&quot;
                  destfile=&quot;diff.xml&quot;
                  start=&quot;152904&quot;
                  end=&quot;153682&quot;
    /&gt;</pre>
  
  <p>Generates a revisiondiff report for all the changes that have been
  made in the <code>Apache BCEL</code> module between the revisions
  <code>152904</code> and <code>153682</code>.  It writes these changes
  into the file <code>diff.xml</code>.</p>
  
  <pre>  &lt;revisiondiff
                  destfile=&quot;diff.xml&quot;
                  package=&quot;ant&quot;
                  start=&quot;{2002-01-01}&quot;
                  end=&quot;{2002-02-01}&quot;
                  dest=&quot;my-working-copy-of-BCEL&quot;
    /&gt;</pre>
  
  <p>Generates a diff report for all the changes that have been made in
  the <code>Apache BCEL</code> module in january 2002. In this example
  <code>svnURL</code> has not been set, it is assumed that
  <code>my-working-copy-of-BCEL</code> contains a checked out copy of
  the BCEL module.  It writes these changes into the file
  <code>diff.xml</code>.</p>
  
  <h4>Generate Report</h4>
  
  <p>This antlib includes a basic XSLT stylesheet that you can use to
  generate a HTML report based on the xml output. The following example
  illustrates how to generate a HTML report from the XML report.</p>
  
  <pre>
          &lt;style in="diff.xml" 
                 out="diff.html" 
                 style="your-path-to/etc/revisiondiff.xsl"&gt;
            &lt;param name="title" expression="Jakarta BCEL diff"/&gt;
            &lt;param name="repo" expression="http://svn.apache.org/repos/asf/jakarta/bcel/trunk"/&gt;
          &lt;/style&gt;
  </pre>
  
  <h4>(Shortened) Example Output</h4>
  <pre>
  &lt;?xml version="1.0" encoding="UTF-8"?&gt;
  &lt;revisiondiff start="153872" end="152873" svnurl="http://svn.apache.org/repos/asf/jakarta/bcel/trunk" &gt;
    &lt;path&gt;
      &lt;name&gt;&lt;![CDATA[LICENSE.txt]]&gt;&lt;/name&gt;
      &lt;action&gt;modified&lt;/action&gt;
    &lt;/path&gt;
    &lt;path&gt;
      &lt;name&gt;&lt;![CDATA[NOTICE.txt]]&gt;&lt;/name&gt;
      &lt;action&gt;deleted&lt;/action&gt;
    &lt;/path&gt;
  &lt;/revisiondiff&gt;
  </pre>
  
  <hr><p align="center">Copyright &copy; 2005 The Apache Software Foundation. All rights
  Reserved.</p>
  
  </body>
  </html>
  
  
  
  
  1.1                  ant/proposal/sandbox/svn/src/etc/revisiondiff.xsl
  
  Index: revisiondiff.xsl
  ===================================================================
  <!--
      Copyright 2005 The Apache Software Foundation
     
       Licensed 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
     
           http://www.apache.org/licenses/LICENSE-2.0
     
       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.
     
  -->
  <xsl:stylesheet
      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
      version="1.0">
    <xsl:param name="title"/>
    <xsl:param name="repo"/>
  
    <xsl:output method="html" indent="yes"/>
  
    <!-- Copy standard document elements.  Elements that
         should be ignored must be filtered by apply-templates
         tags. -->
    <xsl:template match="*">
      <xsl:copy>
        <xsl:copy-of select="attribute::*[. != '']"/>
        <xsl:apply-templates/>
      </xsl:copy>
    </xsl:template>
  
    <xsl:template match="revisiondiff">
      <HTML>
        <HEAD>
          <TITLE><xsl:value-of select="$title"/></TITLE>
        </HEAD>
        <BODY link="#000000" alink="#000000" vlink="#000000" text="#000000">
          <style type="text/css">
            body, p {
            font-family: verdana,arial,helvetica;
            font-size: 80%;
            color:#000000;
            }
  	  .dateAndAuthor {
            font-family: verdana,arial,helvetica;
            font-size: 80%;
            font-weight: bold;
            text-align:left;
            background:#a6caf0;
  	  }
            tr, td{
            font-family: verdana,arial,helvetica;
            font-size: 80%;
            background:#eeeee0;
            }	  
  	  </style>        
            <h1>
              <a name="top"><xsl:value-of select="$title"/></a>
            </h1>
            diff between <xsl:value-of select="@start"/> and <xsl:value-of select="@end"/>
            <p align="right">Designed for use with <a href="http://ant.apache.org/">Apache Ant</a>.</p>
            <hr size="2"/>
  	<a name="TOP"/>
  	<table width="100%">
  		<tr>
  			<td align="right">
  				<a href="#New">New Files</a> |
  				<a href="#Modified">Modified Files</a> |
  				<a href="#Removed">Removed Files</a>
  			</td>
  		</tr>
  	</table>
          <TABLE BORDER="0" WIDTH="100%" CELLPADDING="3" CELLSPACING="1">
  		<xsl:call-template name="show-paths">
  			<xsl:with-param name="title">New Files</xsl:with-param>
  			<xsl:with-param name="anchor">New</xsl:with-param>
  			<xsl:with-param name="paths" select=".//path[action='added']"/>
  		</xsl:call-template>
  
  		<xsl:call-template name="show-paths">
  			<xsl:with-param name="title">Modified Files</xsl:with-param>
  			<xsl:with-param name="anchor">Modified</xsl:with-param>
  			<xsl:with-param name="paths" select=".//path[action='modified']"/>
  		</xsl:call-template>
  
  		<xsl:call-template name="show-paths">
  			<xsl:with-param name="title">Removed Files</xsl:with-param>
  			<xsl:with-param name="anchor">Removed</xsl:with-param>
  			<xsl:with-param name="paths" select=".//path[action='deleted']"/>
  		</xsl:call-template>
          </TABLE>
          
        </BODY>
      </HTML>
    </xsl:template>
  
    <xsl:template name="show-paths">
  	<xsl:param name="title"/>
  	<xsl:param name="anchor"/>
  	<xsl:param name="paths"/>
  	<TR>
  		<TD colspan="2" class="dateAndAuthor">
  			<a>
  				<xsl:attribute name="name"><xsl:value-of select="$anchor"/></xsl:attribute>
  				<xsl:value-of select="$title"/> - <xsl:value-of select="count($paths)"/> entries
  			</a>
  			<a href="#TOP">(back to top)</a>
  		</TD>
  	</TR>
  	<TR>
  		<TD width="20">
  			<xsl:text>    </xsl:text>
  		</TD>
  		<TD>
  		        <ul>
  				<xsl:apply-templates select="$paths"/>
  			</ul>
  		</TD>
  	</TR>
    </xsl:template>  
  
    <xsl:template match="path">
      <li>
        <a target="_new">
          <xsl:attribute name="href"><xsl:value-of select="$repo"/>/<xsl:value-of select="name" /></xsl:attribute>
          <xsl:value-of select="name" />
        </a>
      </li>
    </xsl:template>
  
    <!-- Any elements within a msg are processed,
         so that we can preserve HTML tags. -->
    <xsl:template match="msg">
      <b><xsl:apply-templates/></b>
    </xsl:template>
    
  </xsl:stylesheet>
  
  
  
  1.1                  ant/proposal/sandbox/svn/src/etc/testcases/revisiondiff.xml
  
  Index: revisiondiff.xml
  ===================================================================
  <?xml version="1.0"?>
  
  <!--
   Copyright  2005 The Apache Software Foundation
   
    Licensed 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
   
        http://www.apache.org/licenses/LICENSE-2.0
   
    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.
  -->
  
  <project name="revisiondiff-test" basedir="../../../" 
           default="diff" xmlns:svn="antlib:org.apache.tools.ant.taskdefs.svn">
  
    <property name="tmpdir" value="tmpdir"/>
    <property name="trunkdir" value="${tmpdir}/trunk"/>
    <property name="file" value="ebcdic.h"/>
  
    <target name="dir-prep">
      <mkdir dir="${tmpdir}"/>
    </target>
  
    <target name="setup" depends="dir-prep">
      <svn:svn
        svnURL="http://svn.apache.org/repos/asf/jakarta/bcel/trunk"
        dest="${tmpdir}"/>
    </target>
  
    <target name="diff" depends="setup">
      <svn:revisiondiff failonerror="true" dest="${trunkdir}"
        destfile="${tmpdir}/diff.xml" start="152904" end="153682"/>
    </target>
  
    <target name="diff-using-url" depends="dir-prep">
      <svn:revisiondiff failonerror="true"
        svnURL="http://svn.apache.org/repos/asf/jakarta/bcel/trunk"
        destfile="${tmpdir}/diff.xml" start="152904" end="153682"/>
    </target>
  
    <target name="report">
    <!--target name="report" depends="diff-using-url"-->
      <style in="${tmpdir}/diff.xml" 
        out="${tmpdir}/diff.html" 
        style="src/etc/revisiondiff.xsl">
        <param name="title" expression="Jakarta BCEL diff"/>
        <param name="repo" expression="http://svn.apache.org/repos/asf/jakarta/bcel/trunk"/>
      </style>
    </target>
  
    <target name="cleanup">
      <delete dir="${tmpdir}" />
    </target>
  </project>
  
  
  
  1.3       +8 -0      ant/proposal/sandbox/svn/src/main/org/apache/tools/ant/taskdefs/svn/SvnEntry.java
  
  Index: SvnEntry.java
  ===================================================================
  RCS file: /home/cvs/ant/proposal/sandbox/svn/src/main/org/apache/tools/ant/taskdefs/svn/SvnEntry.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- SvnEntry.java	15 Feb 2005 11:12:15 -0000	1.2
  +++ SvnEntry.java	15 Feb 2005 13:30:49 -0000	1.3
  @@ -147,6 +147,14 @@
               }
           }
   
  +        public Path(final String name, final int action) {
  +            this.name = name;
  +            if (action != ADDED && action != DELETED && action != MODIFIED) {
  +                throw new IllegalArgumentException("Unkown action; " + action);
  +            }
  +            this.action = action;
  +        }
  +
           public String getName() {
               return name;
           }
  
  
  
  1.3       +4 -0      ant/proposal/sandbox/svn/src/main/org/apache/tools/ant/taskdefs/svn/antlib.xml
  
  Index: antlib.xml
  ===================================================================
  RCS file: /home/cvs/ant/proposal/sandbox/svn/src/main/org/apache/tools/ant/taskdefs/svn/antlib.xml,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- antlib.xml	14 Feb 2005 13:28:33 -0000	1.2
  +++ antlib.xml	15 Feb 2005 13:30:49 -0000	1.3
  @@ -23,4 +23,8 @@
       name="changelog"
       classname="org.apache.tools.ant.taskdefs.svn.SvnChangeLogTask"
       />
  +  <taskdef
  +    name="revisiondiff"
  +    classname="org.apache.tools.ant.taskdefs.svn.SvnRevisionDiff"
  +    />
   </antlib>
  
  
  
  1.1                  ant/proposal/sandbox/svn/src/main/org/apache/tools/ant/taskdefs/svn/SvnRevisionDiff.java
  
  Index: SvnRevisionDiff.java
  ===================================================================
  /*
   * Copyright 2005 The Apache Software Foundation
   *
   *  Licensed 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
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   *  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.
   *
   */
  package org.apache.tools.ant.taskdefs.svn;
  
  import java.io.BufferedReader;
  import java.io.File;
  import java.io.FileOutputStream;
  import java.io.FileReader;
  import java.io.IOException;
  import java.io.OutputStreamWriter;
  import java.io.PrintWriter;
  import java.io.UnsupportedEncodingException;
  import java.util.ArrayList;
  import java.util.StringTokenizer;
  
  import org.apache.tools.ant.BuildException;
  import org.apache.tools.ant.Project;
  import org.apache.tools.ant.util.FileUtils;
  
  /**
   * Examines the output of svn diff between two revisions.
   *
   * It produces an XML output representing the list of changes.
   * <PRE>
   * &lt;!-- Root element --&gt;
   * &lt;!ELEMENT revisiondiff ( paths? ) &gt;
   * &lt;!-- Start revision of the report --&gt;
   * &lt;!ATTLIST revisiondiff startRevision NMTOKEN #IMPLIED &gt;
   * &lt;!-- End revision of the report --&gt;
   * &lt;!ATTLIST revisiondiff endRevision NMTOKEN #IMPLIED &gt;
   * &lt;!-- Start date of the report --&gt;
   *
   * &lt;!-- Path added, changed or removed --&gt;
   * &lt;!ELEMENT path ( name,action ) &gt;
   * &lt;!-- Name of the file --&gt;
   * &lt;!ELEMENT name ( #PCDATA ) &gt;
   * &lt;!ELEMENT action (added|modified|deleted)&gt;
   * </PRE>
   *
   * @ant.task name="svnrevisiondiff"
   */
  public class SvnRevisionDiff extends AbstractSvnTask {
  
      /**
       * Used to create the temp file for svn log
       */
      private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
  
      /**
       * Token to identify the word file in the rdiff log
       */
      static final String INDEX = "Index: ";
      /**
       * Token to identify a deleted file based on the Index line.
       */
      static final String DELETED = " (deleted)";
  
      /**
       * Token to identify added files based on the diff line.
       */
      static final String IS_NEW = "\t(revision 0)";
  
      /**
       * Token that starts diff line of old revision.
       */
      static final String DASHES = "--- ";
  
      /**
       * The earliest revision from which diffs are to be included in the report.
       */
      private String mystartRevision;
  
      /**
       * The latest revision from which diffs are to be included in the report.
       */
      private String myendRevision;
  
      /**
       * The file in which to write the diff report.
       */
      private File mydestfile;
  
      /**
       * Set the start revision.
       *
       * @param s the start revision.
       */
      public void setStart(String s) {
          mystartRevision = s;
      }
  
      /**
       * Set the end revision.
       *
       * @param s the end revision.
       */
      public void setEnd(String s) {
          myendRevision = s;
      }
  
      /**
       * Set the output file for the diff.
       *
       * @param f the output file for the diff.
       */
      public void setDestFile(File f) {
          mydestfile = f;
      }
  
      /**
       * Execute task.
       *
       * @exception BuildException if an error occurs
       */
      public void execute() throws BuildException {
          // validate the input parameters
          validate();
  
          // build the rdiff command
          setSubCommand("diff");
          setRevision(mystartRevision + ":" + myendRevision);
          addSubCommandArgument("--no-diff-deleted");
  
          File tmpFile = null;
          try {
              tmpFile = 
                  FILE_UTILS.createTempFile("svnrevisiondiff", ".log", null);
              tmpFile.deleteOnExit();
              setOutput(tmpFile);
  
              // run the svn command
              super.execute();
  
              // parse the rdiff
              SvnEntry.Path[] entries = parseDiff(tmpFile);
  
              // write the revision diff
              writeRevisionDiff(entries);
  
          } finally {
              if (tmpFile != null) {
                  tmpFile.delete();
              }
          }
      }
  
      /**
       * Parse the tmpFile and return and array of SvnRevisionEntry to be
       * written in the output.
       *
       * @param tmpFile the File containing the output of the svn rdiff command
       * @return the entries in the output
       * @exception BuildException if an error occurs
       */
      private SvnEntry.Path[] parseDiff(File tmpFile) throws BuildException {
          // parse the output of the command
          BufferedReader reader = null;
  
          try {
              reader = new BufferedReader(new FileReader(tmpFile));
              ArrayList entries = new ArrayList();
  
              String line = reader.readLine();
              String name = null;
              String currDiffLine = null;
              boolean deleted = false;
              boolean added = false;
  
              while (null != line) {
                  if (line.length() > INDEX.length()) {
                      if (line.startsWith(INDEX)) {
                          if (name != null) {
                              SvnEntry.Path p =
                                  new SvnEntry.Path(name, 
                                                    deleted 
                                                    ? SvnEntry.Path.DELETED 
                                                    : (added 
                                                       ? SvnEntry.Path.ADDED 
                                                       : SvnEntry.Path.MODIFIED)
                                                    );
                              entries.add(p);
                              deleted = added = false;
                          }
  
                          name = line.substring(INDEX.length());
                          if (line.endsWith(DELETED)) {
                              name = name.substring(0, name.length() 
                                                    - DELETED.length());
                              deleted = true;
                          }
  
                          currDiffLine = DASHES + name;
                      } else if (currDiffLine != null 
                                 && line.startsWith(currDiffLine)
                                 && line.endsWith(IS_NEW)) {
                          added = true;
                      }
                  }
                  line = reader.readLine();
              }
              if (name != null) {
                  SvnEntry.Path p = new SvnEntry.Path(name, 
                                                      deleted 
                                                      ? SvnEntry.Path.DELETED 
                                                      : (added 
                                                         ? SvnEntry.Path.ADDED 
                                                         : SvnEntry.Path.MODIFIED)
                                                      );
                  entries.add(p);
              }
  
              SvnEntry.Path[] array = (SvnEntry.Path[])
                  entries.toArray(new SvnEntry.Path[entries.size()]);
              return array;
          } catch (IOException e) {
              throw new BuildException("Error in parsing", e);
          } finally {
              if (reader != null) {
                  try {
                      reader.close();
                  } catch (IOException e) {
                      log(e.toString(), Project.MSG_ERR);
                  }
              }
          }
      }
  
      /**
       * Write the rdiff log.
       *
       * @param entries a <code>SvnRevisionEntry[]</code> value
       * @exception BuildException if an error occurs
       */
      private void writeRevisionDiff(SvnEntry.Path[] entries) throws BuildException {
          FileOutputStream output = null;
          try {
              output = new FileOutputStream(mydestfile);
              PrintWriter writer = new PrintWriter(
                                       new OutputStreamWriter(output, "UTF-8"));
              writer.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
              writer.print("<revisiondiff ");
              if (mystartRevision != null) {
                  writer.print("start=\"" + mystartRevision + "\" ");
              }
              if (myendRevision != null) {
                  writer.print("end=\"" + myendRevision + "\" ");
              }
  
              if (getSvnURL() != null) {
                  writer.print("svnurl=\"" + getSvnURL() + "\" ");
              }
  
              writer.println(">");
              for (int i = 0, c = entries.length; i < c; i++) {
                  writeRevisionEntry(writer, entries[i]);
              }
              writer.println("</revisiondiff>");
              writer.flush();
              writer.close();
          } catch (UnsupportedEncodingException uee) {
              log(uee.toString(), Project.MSG_ERR);
          } catch (IOException ioe) {
              throw new BuildException(ioe.toString(), ioe);
          } finally {
              if (null != output) {
                  try {
                      output.close();
                  } catch (IOException ioe) {
                      log(ioe.toString(), Project.MSG_ERR);
                  }
              }
          }
      }
  
      /**
       * Write a single entry to the given writer.
       *
       * @param writer a <code>PrintWriter</code> value
       * @param entry a <code>SvnRevisionEntry</code> value
       */
      private void writeRevisionEntry(PrintWriter writer, SvnEntry.Path entry) {
          writer.println("\t<path>");
          writer.println("\t\t<name><![CDATA[" + entry.getName() + "]]></name>");
          writer.println("\t\t<action>" + entry.getActionDescription() 
                         + "</action>");
          writer.println("\t</path>");
      }
  
      /**
       * Validate the parameters specified for task.
       *
       * @exception BuildException if a parameter is not correctly set
       */
      private void validate() throws BuildException {
          if (null == mydestfile) {
              throw new BuildException("Destfile must be set.");
          }
  
          if (null == mystartRevision) {
              throw new BuildException("Start revision or start date must be set.");
          }
  
          if (null == myendRevision) {
              throw new BuildException("End revision or end date must be set.");
          }
      }
  }
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@ant.apache.org
For additional commands, e-mail: dev-help@ant.apache.org