ant-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Phil Hanna" <aut...@philhanna.com>
Subject Fixes for org.apache.tools.ant.taskdefs.Javac and org.apache.tools.ant.Project
Date Sun, 14 May 2000 01:52:26 GMT
Appended to this note are fixes for two related Ant bugs:

BUG 1 - Project.translatePath()

1. org.apache.tools.ant.taskdefs.Javac uses Project.translatePath() to convert various paths
to native format (i.e., Unix to Win32, etc.).  But Project.translatePath() does not always
correctly handle Win32 paths with forward slashes, converting for example

   d:/jdk1.3/jre/lib/ext

to

   d;\jdk1.3\jre\lib\ext

which is interpreted as two directories instead of one.

Here is what happens (I added the DEBUG messages to the source code):

--- CUT HERE ---

D:\jspcr\docs>ant
Buildfile: build.xml
Project base dir set to: D:\jspcr\docs
DEBUG: Javac: setExtdirs: entry
DEBUG: Javac: setExtdirs: extdirs=d:/jdk1.3/jre/lib/ext
DEBUG: Javac: setExtdirs: after project.translatePath()
DEBUG: Javac: setExtdirs: this.extdirs=d;\jdk1.3\jre\lib\ext
DEBUG: Javac: setExtdirs: exit
Executing Target: init
Executing Target: compile
Compiling 1 source files to D:\jspcr\docs
DEBUG: addExtdirsToClasspath: entry
DEBUG: addExtdirsToClasspath: classpath=D:\jspcr\docs;D:\ant\lib\ant.jar;D:\jdk1.3\lib\tools.jar
DEBUG: addExtdirsToClasspath: extdirs=d;\jdk1.3\jre\lib\ext
DEBUG: addExtdirsToClasspath: dir=D:\jspcr\docs\d
Exception in thread "main" java.lang.NullPointerException
        at org.apache.tools.ant.taskdefs.Javac.addExtdirsToClasspath(Javac.java:605)
        at org.apache.tools.ant.taskdefs.Javac.doJikesCompile(Javac.java:466)
        at org.apache.tools.ant.taskdefs.Javac.execute(Javac.java:227)
        at org.apache.tools.ant.Target.execute(Target.java:122)
        at org.apache.tools.ant.Project.runTarget(Project.java:675)
        at org.apache.tools.ant.Project.executeTarget(Project.java:422)
        at org.apache.tools.ant.Main.runBuild(Main.java:248)
        at org.apache.tools.ant.Main.main(Main.java:191)

D:\jspcr\docs>

--- END CUT ---

Here is the build.xml file:

--- CUT HERE ---

<project default="compile" basedir="." >

   <target name="init">
      <tstamp/>
      <property name="java_home" value="d:/jdk1.3"/>
      <property name="extdirs" value="${java_home}/jre/lib/ext" />
      <property name="build.compiler" value="jikes" />
   </target>

   <target name="compile" depends="init">
      <javac srcdir="." destdir="." extdirs="${extdirs}" />
   </target>

</project>

--- END CUT ---

Unfortunately, the translatePath() problem is not always possible to solve because the Unix
and Win32 path formats do not map completely.  For example, what should we make of c:/test?
 It could be a single Win32 directory or two Unix directories.  (Forward slashes should be
treated as valid, even on Win32).

The best we can do is employ a heuristic to determine what platform the string is coded for,
and then apply the following decision table:

String      Runtime
Platform    Platform    Action
--------    --------    ------
Unix        Unix        No change required

Unix        Win32       Change ":" to ";", "/" to "\\"

Win32       Unix        Remove "<driveletter>:",
                        then change ";" to ":", "\\" to "/"

Win32       Win32       No change required

A string is considered to be a Win32 path if

1. It contains any ";" characters, OR
2. It contains any "\\" characters, OR
3. It begins with a letter followed by a ":"

I fiddled with Project.translatePath() but ended up writing a replacement.  Here is the patch:

--- CUT HERE ---
--- Project.java Wed May 10 16:20:34 2000
+++ Project.java.new Sat May 13 15:15:03 2000
@@ -482,43 +482,127 @@
     }
 
     /**
-        Translate a path into its native (platform specific)
-        path. This should be extremely fast, code is
-        borrowed from ECS project.
-        <p>
-        All it does is translate the : into ; and / into \
-        if needed. In other words, it isn't perfect.
-
-        @returns translated string or empty string if to_process is null or empty
-        @author Jon S. Stevens <a href="mailto:jon@clearink.com">jon@clearink.com</a>
+    * Translates a path into its native (platform specific)
+    * path.
+    * <P>
+    * This is not always possible because the
+    * Unix and Win32 path formats do not map completely.
+    * For example, what should we make of <CODE>c:/test</CODE>?
+    * It could be a single Win32 directory or two Unix
+    * directories.  (Forward slashes should be treated
+    * as valid, even on Win32).
+    * <P>
+    * The best we can do is employ a heuristic to determine
+    * what platform the string is coded for, and then apply
+    * the following decision table:
+    * <PRE>
+    * String      Runtime
+    * Platform    Platform    Action
+    * --------    --------    ------
+    * Unix        Unix        No change required
+    *
+    * Unix        Win32       Change ":" to ";", "/" to "\\"
+    *
+    * Win32       Unix        Remove "&lt;driveletter&gt;:",
+    *                         then change ";" to ":", "\\" to "/"
+    *
+    * Win32       Win32       No change required
+    * </PRE>
+    * A string is considered to be a Win32 path if
+    * <OL>
+    * <LI>It contains any ";" characters, <B>OR</B>
+    * <LI>It contains any "\\" characters, <B>OR</B>
+    * <LI>It begins with a letter followed by a ":"
+    * </OL>
+    *
+    * @param pathString a path string
+    * @return translated string
+    *      or empty string if pathString is null or empty
     */
-    public String translatePath(String to_process) {
-        if ( to_process == null || to_process.length() == 0 ) return "";
+    public String translatePath(String pathString) {
+
+        //  Simple cases first
+
+        if (pathString == null)
+            return "";
+        int length = pathString.length();
+        if (length == 0)
+            return null;
+
+        //  The string to process is considered a Win32 path if
+        //  it contains any semicolons, backslashes, or if it
+        //  begins with a drive letter + ":"
+
+        boolean string_is_win32 =
+            (pathString.indexOf(";") != -1) ||
+            (pathString.indexOf("\\") != -1) ||
+            ((length > 1)
+                && (Character.isLetter(pathString.charAt(0)))
+                && (pathString.charAt(1) == ':'));
+
+        //  The runtime platform is considered to be Win32
+        //  if the path separator is ";"
+
+        boolean platform_is_win32 =
+            System.getProperty("path.separator").equals(";");
+
+        //  No change is required if the platforms are the same
+
+        if (string_is_win32 == platform_is_win32)
+            return pathString;
+
+        //  Win32 to Unix translation:
 
-        StringBuffer bs = new StringBuffer(to_process.length() + 50);
-        StringCharacterIterator sci = new StringCharacterIterator(to_process);
-        String path = System.getProperty("path.separator");
-        String file = System.getProperty("file.separator");
-        String tmp = null;
-        for (char c = sci.first(); c != CharacterIterator.DONE; c = sci.next()) {
-            tmp = String.valueOf(c);
-
-            if (tmp.equals(":")) {
-                // could be a DOS drive or a Unix path separator...
-                // if followed by a backslash, assume it is a drive
-                c = sci.next();
-                tmp = String.valueOf(c);
-                bs.append( tmp.equals("\\") ? ":" : path );
-                if (c == CharacterIterator.DONE) break;
+        if (string_is_win32) {
+
+            //  Ugly problem: There is no completely correct
+            //  way to translate drive letters, since Unix
+            //  does not use them.  Moreover, if an absolute
+            //  path is intended, it probably does not make
+            //  sense to convert it to "/" - it is more likely
+            //  to be "~/" or something.
+            //
+            //  Solution: Remove drive letters and the colons
+            //  that follow them altogether.  If the user has
+            //  a mix of "C:" and "D:" designations in the
+            //  path and wants to run it on Unix, the user must
+            //  untangle it first.
+
+            int p = pathString.indexOf(":");
+            if (p != -1) {
+                StringBuffer sb = new StringBuffer();
+                int goodLength = 0;  // Last known good length
+                for (int i = 0; i < length; i++) {
+                    char c = pathString.charAt(i);
+                    if (c == ':') {
+                        sb.setLength(goodLength);
+                    }
+                    else {
+                        sb.append(c);
+                        if (c == ';')
+                            goodLength = sb.length();
+                    }
+                }
+                pathString = sb.toString();
             }
 
-            if (tmp.equals(":") || tmp.equals(";"))
-                tmp = path;
-            else if (tmp.equals("/") || tmp.equals ("\\"))
-                tmp = file;
-            bs.append(tmp);
+            //  Now translate ";" to ":" and "\\" to "/"
+
+            pathString = pathString.replace(';', ':');
+            pathString = pathString.replace('\\', '/');
         }
-        return(bs.toString());
+
+        //  Unix to Win32 translation:
+
+        else {
+
+            //  Translate ":" to ";" and "/" to "\\"
+
+            pathString = pathString.replace(':', ';');
+            pathString = pathString.replace('/', '\\');
+        }
+
+        return pathString;
     }
 
     /**

--- END CUT ---

BUG 2 - Javac.addExtdirsToClasspath()

2. org.apache.tools.ant.taskdefs.Javac gets a null pointer exception in addExtdirsToClasspath()
if any element of the path refers to a directory which does not exist.  The fix consists of
testing the file list for null before entering the loop.  Here is the patch:

--- CUT HERE ---
--- Javac.java Wed May 10 16:20:34 2000
+++ Javac.java.new Sat May 13 15:45:58 2000
@@ -593,6 +593,7 @@
            while (tok.hasMoreTokens()) {
                File dir = project.resolveFile(tok.nextToken());
                String[] files = dir.list(new JarFilenameFilter());
+               if (files == null) continue;
                for (int i=0 ; i < files.length ; i++) {
                    File f = new File(dir,files[i]);
                    if (f.exists() && f.isFile()) {

--- END CUT ---

Thanks,
Phil Hanna

Mime
View raw message