Add a custom SvnInfo task to get branch@version information from svn info
and svnversion, replacing the old <propertyregex> tags that ant 1.6.5 
didn't like.

Review by: scottb


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@3669 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/build-tools/ant-gwt/src/com/google/gwt/ant/taskdefs/SvnInfo.java b/build-tools/ant-gwt/src/com/google/gwt/ant/taskdefs/SvnInfo.java
new file mode 100644
index 0000000..13530ed
--- /dev/null
+++ b/build-tools/ant-gwt/src/com/google/gwt/ant/taskdefs/SvnInfo.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * 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 com.google.gwt.ant.taskdefs;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.LineNumberReader;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+
+/**
+ * A Svn interface task, because the initial solution of <exec> and
+ * <propertyregex> is unhappy in ant 1.6.5, and while that's old, it's not
+ * quite "too old" for us to care.
+ */
+public class SvnInfo extends Task {
+
+  // URL line from svn info output, selecting the very last term of the URL as
+  // the branch specifier
+  private static final String URL_REGEX = "\\s*URL:\\s*https?://.*/([^/]*)\\s*";
+  
+  
+  public SvnInfo() {
+    super();
+  }
+
+  @Override
+  public void execute() throws BuildException {
+    String result;
+  
+    if (outprop == null) {
+      throw new BuildException(
+          "<svninfo> task requires an outputproperty attribute");
+    }
+    if (workdir == null) {
+      workdir = ".";
+    }
+    File workDirFile = new File(workdir);
+    if (!workDirFile.isDirectory()) {
+      throw new BuildException(workdir + " is not a directory");
+    }
+
+    String branch = getSvnBranch(workDirFile);
+    String revision = getSvnVersion(workDirFile);
+    getProject().setNewProperty(outprop, branch + "@" + revision);
+    if (fileprop != null) {
+      getProject().setNewProperty(fileprop, branch + "-" 
+          + revision.replaceAll(":", "-"));
+    }
+  }
+
+  /**
+   * Establishes the property containing the SVN output string, branch@rev.
+   * @param propname  Name of a property
+   */
+  public void setOutputProperty(String propname) {
+    outprop = propname;
+  }
+
+  /**
+   * Establishes the property containing the SVN output string, branch@rev.
+   * @param propname  Name of a property
+   */
+  public void setOutputFileProperty(String propname) {
+    fileprop = propname;
+  }
+
+  /**
+   * Establishes the directory used as the SVN workspace to fetch version
+   * information about
+   * @param srcdir  workspace directory name
+   */
+  public void setDirectory(String srcdir) {
+    workdir = srcdir;
+  }
+
+  private String getSvnBranch(File workdir) {
+    String branchName = null;
+
+    LineNumberReader svnout = runCommand(workdir, "svn", "info");
+    try {
+      String line = svnout.readLine();
+      
+      Pattern urlRegex = Pattern.compile(URL_REGEX);
+      while (line != null) {
+        Matcher m = urlRegex.matcher(line);
+        
+        if (m.matches()) {
+          branchName = m.group(1);
+          if (branchName == null || "".equals(branchName)) {
+            throw new BuildException("svn info didn't get branch from URL line " 
+                + line);
+          }
+          break;
+        }
+        line = svnout.readLine();
+      }
+    } catch (IOException e) {
+      throw new BuildException("<svninfo> cannot read svn info's output stream", 
+          e);
+    }
+    return branchName;
+  }
+
+  private String getSvnVersion(File workdir) {
+    String line = null;
+
+    LineNumberReader svnout = runCommand(workdir, "svnversion");
+    try {
+      line = svnout.readLine();
+    } catch (IOException e) {
+      throw new BuildException("<svninfo> cannot read svnversion's output stream",
+          e);
+    }
+    if (line == null || "".equals(line)) {
+      throw new BuildException("svnversion didn't give any answer");
+    }
+    return line;
+  }
+
+  private LineNumberReader runCommand (File workdir, String... cmd) {
+    String cmdString = "";
+    for (String arg : cmd) {
+      cmdString = cmdString + arg + " ";
+    }
+    cmdString = cmdString.substring(0, cmdString.length() - 1);
+    
+    ProcessBuilder svnPb = new ProcessBuilder(cmd);
+    svnPb.directory(workdir);
+    Process svnproc;
+    try {
+      svnproc = svnPb.start();
+    } catch (IOException e) {
+      throw new BuildException("cannot launch command " + cmdString, e);
+    }
+    
+    LineNumberReader svnerr = 
+      new LineNumberReader(new InputStreamReader(svnproc.getErrorStream()));
+    try {
+      String line = svnerr.readLine();
+      String errorText = "";
+      if (line != null) {
+        while (line != null) {
+          errorText = errorText + "  " + line + "\n";
+          line = svnerr.readLine();
+        }
+        throw new BuildException(cmdString + " returned error output:\n" 
+            + errorText);
+      }
+    } catch (IOException e) {
+      throw new BuildException("cannot read error stream from " + cmdString, 
+          e);
+    }
+    
+    LineNumberReader svnout = 
+      new LineNumberReader(new InputStreamReader(svnproc.getInputStream()));    
+    return svnout;
+  }
+  
+  private String fileprop;
+  private String outprop;
+  private String workdir;
+}
diff --git a/common.ant.xml b/common.ant.xml
index 160faa1..12b8a3b 100755
--- a/common.ant.xml
+++ b/common.ant.xml
@@ -201,36 +201,11 @@
 
   <macrodef name="gwt.getsvninfo" description="Identifies the SVN info of a workspace">
     <sequential>
-      <exec outputproperty="svn.info" executable="svn" 
-          failifexecutionfails="false" logError="true">
-        <arg value="info"/>
-        <arg value="${gwt.root}"/>
-      </exec>
-      <propertyregex property="gwt.svn.branch"
-        input="${svn.info}" 
-        regexp="URL: https?://google-web-toolkit.googlecode.com/svn/([^\r\n]+)"
-        select="\1"
-        defaultValue="unknown"
-        casesensitive="false" />
-      <exec outputproperty="svn.version" executable="svnversion" 
-          failifexecutionfails="false"  logError="true">
-        <arg value="${gwt.root}"/>
-      </exec>
-      <propertyregex property="gwt.svn.version"
-        input="${svn.version}" 
-        regexp="^(\d+(:\d+)?[MSP]*)$"
-        replace="\1"
-        defaultValue="unknown"
-        casesensitive="false" />
-      <property name="gwt.svnrev" 
-        value="${gwt.svn.branch}@${gwt.svn.version}"/>
-      <!-- Windows NTFS and FAT don't like ':' or '@', and we need a sentinel file to test
-           for out-of-dateness, so this is a filename-acceptable equivalent to svnrev -->
-      <propertyregex property="gwt.svnrev.filename"
-        input="${gwt.svnrev}"
-        regexp="[:@]"
-        replace="-"
-        defaultValue="${gwt.svnrev}"/>
+      <taskdef name="svninfo"
+         classname="com.google.gwt.ant.taskdefs.SvnInfo"
+         classpath="${gwt.build.lib}/ant-gwt.jar" />
+      <svninfo outputproperty="gwt.svnrev"
+          outputfileproperty="gwt.svnrev.filename" />
       <!-- Generally, filtering requires a sentinel file so that changes to svn rev will
          be noticed as invalidating the previously-generated filter output.  This property
          names where such a sentinel lives; it is tested with <available/> and created