Hi,

A few changes:

- The JavaCC task writes the output files to the directory containing the
grammar file, if the outputdirectory attribute is not used.  It used to
write them to the current working directory.

- Some rough documentation for the javacc task

- A fix to the Execute class.  Under JDK 1.1 on Windows Runtime.exec() does
not properly quote arguments that contain spaces.  The other JDK's do.  So,
the Execute class now quotes arguments that need it, when running in JDK 1.1
on Windows.  This also required a small fix to antRun.bat so that it can
handle quoted arguments.  I've run these changes on Win 2000 and 98 with
JDKs 1.3, 1.2.2, 1.1.8 and the MS JVM.  Seems fine.  I've also run with
Sun's JDK1.3 and Blackdown JDK 1.1.8 on Redhat 6.2.

There remains a problem with quotes and trailing '\' in quoted arguments -
these need to be escaped.  I'll get round to fixing this soon.

Not sure how to 'cvs diff' a new file, so I've attached it separately.

Adam
Index: docs/index.html
===================================================================
RCS file: /home/cvspublic/jakarta-ant/docs/index.html,v
retrieving revision 1.118
diff -u -r1.118 index.html
--- docs/index.html     2000/09/27 15:58:34     1.118
+++ docs/index.html     2000/09/28 11:29:47
@@ -4209,6 +4209,7 @@
 <ul>
   <li><a href="#cab">Cab</a></li>
   <li><a href="#ftp">FTP</a></li>
+  <li><a href="javacc.html">JavaCC</a></li>
   <li><a href="jlink.html">Jlink</a></li>
   <li><a href="junit.html">JUnit</a></li>
   <li><a href="native2ascii.html">Native2Ascii</a></li>
Index: src/bin/antRun.bat
===================================================================
RCS file: /home/cvspublic/jakarta-ant/src/bin/antRun.bat,v
retrieving revision 1.5
diff -u -r1.5 antRun.bat
--- src/bin/antRun.bat  2000/08/02 10:28:28     1.5
+++ src/bin/antRun.bat  2000/09/28 11:35:00
@@ -7,12 +7,12 @@
 
 set PARAMS=
 :loop
-if "%1" == "" goto runCommand
+if ""%1 == "" goto runCommand
 set PARAMS=%PARAMS% %1
 shift
 goto loop
 
 :runCommand
-echo %ANT_RUN_CMD% %PARAMS%
+rem echo %ANT_RUN_CMD% %PARAMS%
 %ANT_RUN_CMD% %PARAMS%
 
Index: src/main/org/apache/tools/ant/taskdefs/Execute.java
===================================================================
RCS file: 
/home/cvspublic/jakarta-ant/src/main/org/apache/tools/ant/taskdefs/Execute.java,v
retrieving revision 1.7
diff -u -r1.7 Execute.java
--- src/main/org/apache/tools/ant/taskdefs/Execute.java 2000/09/18 09:01:53     
1.7
+++ src/main/org/apache/tools/ant/taskdefs/Execute.java 2000/09/28 11:35:59
@@ -56,6 +56,8 @@
 
 import org.apache.tools.ant.BuildException;
 import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.Commandline;
 
 import java.io.File;
 import java.io.IOException;
@@ -81,20 +83,59 @@
     private ExecuteStreamHandler streamHandler;
     private ExecuteWatchdog watchdog;
     private File workingDirectory = null;
-    private String antRun;
+    private Project project = null;
 
     private static String antWorkingDirectory = System.getProperty("user.dir");
-    private static String myos = System.getProperty("os.name");
+    private static CommandLauncher launcher = createCommandLauncher();
 
-    private static Method execWithCWD = null;
-    static {
-       try {
-           // JDK 1.3 API extension:
-           // Runtime.exec(String[] cmdarray, String[] envp, File dir)
-           execWithCWD = Runtime.class.getMethod("exec", new Class[] 
{String[].class, String[].class, File.class});
-       } catch (NoSuchMethodException nsme) {
-           // OK.
-       }
+    /** 
+     * Builds a command launcher for the OS and JVM we are running under
+     */
+    private static CommandLauncher createCommandLauncher()
+    {
+        // Try using a JDK 1.3 launcher
+        try {
+            return new Java13CommandLauncher();
+        }
+        catch ( NoSuchMethodException exc ) {
+            // Ignore and keep try
+        }
+
+        String osname = System.getProperty("os.name").toLowerCase();
+        if ( osname.indexOf("mac os") >= 0 ) {
+            // Mac
+            return new MacCommandLauncher(new CommandLauncher());
+        }
+        else if ( osname.indexOf("os/2") >= 0 ) {
+            // OS/2 - use same mechanism as Windows 2000
+            return new WinNTCommandLauncher(new CommandLauncher());
+        }
+        else if ( osname.indexOf("windows") >= 0 ) {
+            // Windows.  Need to determine which JDK we're running in
+            CommandLauncher baseLauncher;
+            if ( System.getProperty("java.version").startsWith("1.1") ) {
+                // JDK 1.1
+                baseLauncher = new Java11CommandLauncher();
+            }
+            else {
+                // JDK 1.2
+                baseLauncher = new CommandLauncher();
+            }
+
+            // Determine if we're running under 2000/NT or 98/95
+            if ( osname.indexOf("nt") >= 0 || osname.indexOf("2000") >= 0 ) {
+                // Windows 2000/NT
+                return new WinNTCommandLauncher(baseLauncher);
+            }
+            else {
+                // Windows 98/95 - need to use an auxiliary script
+                return new ScriptCommandLauncher("bin/antRun.bat", 
baseLauncher);
+            }
+        }
+        else {
+            // Generic
+            return new ScriptCommandLauncher("bin/antRun", new 
CommandLauncher());
+        }
     }
 
     /**
@@ -192,19 +233,7 @@
      * @param project the current project.
      */
     public void setAntRun(Project project) throws BuildException {
-       if (myos.equals("Mac OS") || execWithCWD != null)
-            return;
-
-        String ant = project.getProperty("ant.home");
-        if (ant == null) {
-            throw new BuildException("Property 'ant.home' not found");
-        }
-
-        if (myos.toLowerCase().indexOf("windows") >= 0) {
-            antRun = project.resolveFile(ant + "/bin/antRun.bat").toString();
-        } else {
-            antRun = project.resolveFile(ant + "/bin/antRun").toString();
-        }
+        this.project = project;
     }
 
     /**
@@ -215,7 +244,7 @@
      *            of the subprocess failed
      */
     public int execute() throws IOException {
-        final Process process = exec();
+        final Process process = launcher.exec(project, getCommandline(), 
getEnvironment(), workingDirectory);
         try {
             streamHandler.setProcessInputStream(process.getOutputStream());
             streamHandler.setProcessOutputStream(process.getInputStream());
@@ -233,63 +262,6 @@
         return getExitValue();
     }
 
-
-    protected Process exec() throws IOException {
-       if (workingDirectory == null) {
-           // Easy.
-           return Runtime.getRuntime().exec(cmdl, getEnvironment());
-       } else if (execWithCWD != null) {
-           // The best way to set cwd, if you have JDK 1.3.
-           try {
-               Object[] arguments = new Object[] {getCommandline(), 
getEnvironment(), workingDirectory};
-               return (Process)execWithCWD.invoke(Runtime.getRuntime(), 
arguments);
-            } catch (InvocationTargetException ite) {
-                Throwable t = ite.getTargetException();
-                if (t instanceof ThreadDeath) {
-                    throw (ThreadDeath)t;
-                } else if (t instanceof IOException) {
-                    throw (IOException)t;
-                } else {
-                    throw new IOException(t.toString());
-                }
-           } catch (Exception e) {
-               // IllegalAccess, IllegalArgument, ClassCast
-               throw new IOException(e.toString());
-           }
-       } else if (myos.equals("Mac OS")) {
-           // Dubious Mac hack.
-           System.getProperties().put("user.dir", 
-                                      workingDirectory.getAbsolutePath());
-           try {
-               return Runtime.getRuntime().exec(cmdl, getEnvironment());
-           } finally {
-                System.getProperties().put("user.dir", antWorkingDirectory);
-           }
-       } else if ((myos.toLowerCase().indexOf("windows") >= 0 &&
-                       (myos.toLowerCase().indexOf("nt") >= 0 ||
-                        myos.indexOf("2000") >= 0))
-                      // cmd /c cd works OK on Windows NT & friends.
-                   || myos.toLowerCase().indexOf("os/2") >= 0
-                      // as well as on OS/2
-                   ) {
-           String[] commandLine = new String[cmdl.length+5];
-           commandLine[0] = "cmd";
-           commandLine[1] = "/c";
-           commandLine[2] = "cd";
-           commandLine[3] = workingDirectory.getAbsolutePath();
-           commandLine[4] = "&&";
-           System.arraycopy(cmdl, 0, commandLine, 5, cmdl.length);
-           return Runtime.getRuntime().exec(commandLine, getEnvironment());
-       } else {
-           // Fallback to the antRun wrapper script (POSIX, Win95/98, etc.):
-           String[] commandLine = new String[cmdl.length+2];
-           commandLine[0] = antRun;
-           commandLine[1] = workingDirectory.getAbsolutePath();
-           System.arraycopy(cmdl, 0, commandLine, 2, cmdl.length);
-           return Runtime.getRuntime().exec(commandLine, getEnvironment());
-       }
-    }
-
     protected void waitFor(Process process) {
         try {
             process.waitFor();
@@ -303,5 +275,277 @@
 
     protected int getExitValue() {
         return exitValue;
+    }
+
+    /**
+     * A utility method that runs an external command.  Writes the output and
+     * error streams of the command to the project log.
+     *
+     * @param task      The task that the command is part of.  Used for logging
+     * @param cmdline   The command to execute.
+     *
+     * @throws BuildException if the command does not return 0.
+     */
+    public static void runCommand(Task task, String[] cmdline) throws 
BuildException
+    {
+        try {
+            task.log(Commandline.toString(cmdline), Project.MSG_VERBOSE);
+            Execute exe = new Execute(new LogStreamHandler(task, 
+                                                           Project.MSG_INFO,
+                                                           Project.MSG_ERR));
+            exe.setAntRun(task.getProject());
+            exe.setCommandline(cmdline);
+            int retval = exe.execute();
+            if ( retval != 0 ) {
+                throw new BuildException(cmdline[0] + " failed with return 
code " + retval, task.getLocation());
+            }
+        } 
+        catch (java.io.IOException exc) {
+            throw new BuildException("Could not launch " + cmdline[0] + ": " + 
exc, task.getLocation());
+        }
+    }
+
+    /**
+     * A command launcher for a particular JVM/OS platform.  This class is
+     * a general purpose command launcher which can only launch commands in
+     * the current working directory.
+     */
+    private static class CommandLauncher
+    {
+        /** 
+         * Launches the given command in a new process.
+         *
+         * @param project       The project that the command is part of
+         * @param cmd           The command to execute
+         * @param env           The environment for the new process.  If null,
+         *                      the environment of the current proccess is 
used.
+         */
+        public Process exec(Project project, String[] cmd, String[] env) 
throws IOException
+        {
+            return Runtime.getRuntime().exec(cmd, env);
+        }
+
+        /** 
+         * Launches the given command in a new process, in the given working
+         * directory.
+         *
+         * @param project       The project that the command is part of
+         * @param cmd           The command to execute
+         * @param env           The environment for the new process.  If null,
+         *                      the environment of the current proccess is 
used.
+         * @param workingDir    The directory to start the command in.  If 
null,
+         *                      the current directory is used
+         */
+        public Process exec(Project project, String[] cmd, String[] env, File 
workingDir) throws IOException
+        {
+            if ( workingDir == null ) {
+                return exec(project, cmd, env);
+            }
+            throw new IOException("Cannot execute a process in different 
directory under this JVM");
+        }
+    }
+
+    /**
+     * A command launcher for JDK/JRE 1.1 under Windows.  Fixes quoting 
problems
+     * in Runtime.exec().  Can only launch commands in the current working
+     * directory
+     */
+    private static class Java11CommandLauncher extends CommandLauncher
+    {
+        /**
+         * Launches the given command in a new process.  Needs to quote
+         * arguments
+         */
+        public Process exec(Project project, String[] cmd, String[] env) 
throws IOException 
+        {
+            // Need to quote arguments with spaces, and to escape quote 
characters
+            String[] newcmd = new String[cmd.length];
+            for ( int i = 0; i < cmd.length; i++ ) {
+                if ( cmd[i].indexOf(' ') >= 0 ) {
+                    newcmd[i] = '\"' + cmd[i] + '\"';
+                }
+                else {
+                    newcmd[i] = cmd[i];
+                }
+            }
+            return Runtime.getRuntime().exec(newcmd, env);
+        }
+    }
+
+    /**
+     * A command launcher for JDK/JRE 1.3 (and higher).  Uses the built-in
+     * Runtime.exec() command
+     */
+    private static class Java13CommandLauncher extends CommandLauncher
+    {
+        public Java13CommandLauncher() throws NoSuchMethodException
+        {
+            // Locate method Runtime.exec(String[] cmdarray, String[] envp, 
File dir)
+            _execWithCWD = Runtime.class.getMethod("exec", new Class[] 
{String[].class, String[].class, File.class});
+        }
+
+        /** 
+         * Launches the given command in a new process, in the given working
+         * directory
+         */
+        public Process exec(Project project, String[] cmd, String[] env, File 
workingDir) throws IOException
+        {
+           try {
+               Object[] arguments = { cmd, env, workingDir };
+               return (Process)_execWithCWD.invoke(Runtime.getRuntime(), 
arguments);
+            } 
+            catch ( InvocationTargetException exc ) {
+                Throwable realexc = exc.getTargetException();
+                if ( realexc instanceof ThreadDeath ) {
+                    throw (ThreadDeath)realexc;
+                } 
+                else if ( realexc instanceof IOException ) {
+                    throw (IOException)realexc;
+                } 
+                else {
+                    throw new IOException(realexc.getMessage());
+                }
+           } 
+            catch ( Exception exc ) {
+               // IllegalAccess, IllegalArgument, ClassCast
+               throw new IOException(exc.getMessage());
+           }
+        }
+
+        private Method _execWithCWD;
+    }
+
+    /**
+     * A command launcher that proxies another command launcher.  
+     *
+     * Sub-classes override exec(args, env, workdir)
+     */
+    private static class CommandLauncherProxy extends CommandLauncher
+    {
+        CommandLauncherProxy(CommandLauncher launcher)
+        {
+            _launcher = launcher;
+        }
+
+        /** 
+         * Launches the given command in a new process.  Delegates this
+         * method to the proxied launcher
+         */
+        public Process exec(Project project, String[] cmd, String[] env) 
throws IOException
+        {
+            return _launcher.exec(project, cmd, env);
+        }
+
+        private CommandLauncher _launcher;
+    }
+
+    /**
+     * A command launcher for Windows 2000/NT that uses 'cmd.exe' when
+     * launching commands in directories other than the current working
+     * directory.
+     */
+    private static class WinNTCommandLauncher extends CommandLauncherProxy
+    {
+        WinNTCommandLauncher(CommandLauncher launcher)
+        {
+            super(launcher);
+        }
+
+        /** 
+         * Launches the given command in a new process, in the given working
+         * directory.
+         */
+        public Process exec(Project project, String[] cmd, String[] env, File 
workingDir) throws IOException
+        {
+            if ( workingDir == null ) {
+                return exec(project, cmd, env);
+            }
+
+            // Use cmd.exe to change to the specified directory before running
+            // the command
+           String[] newcmd = new String[cmd.length+5];
+           newcmd[0] = "cmd";
+           newcmd[1] = "/c";
+           newcmd[2] = "cd";
+           newcmd[3] = workingDir.getAbsolutePath();
+           newcmd[4] = "&&";
+           System.arraycopy(cmd, 0, newcmd, 5, cmd.length);
+            return exec(project, newcmd, env);
+        }
+    }
+
+    /**
+     * A command launcher for Mac that uses a dodgy mechanism to change
+     * working directory before launching commands.
+     */
+    private static class MacCommandLauncher extends CommandLauncherProxy
+    {
+        MacCommandLauncher(CommandLauncher launcher)
+        {
+            super(launcher);
+        }
+
+        /** 
+         * Launches the given command in a new process, in the given working
+         * directory
+         */
+        public Process exec(Project project, String[] cmd, String[] env, File 
workingDir) throws IOException
+        {
+            if ( workingDir == null ) {
+                return exec(project, cmd, env);
+            }
+
+           System.getProperties().put("user.dir", 
workingDir.getAbsolutePath());
+           try {
+               return exec(project, cmd, env);
+           } 
+            finally {
+                System.getProperties().put("user.dir", antWorkingDirectory);
+           }
+        }
+    }
+
+    /**
+     * A command launcher that uses an auxiliary script to launch commands
+     * in directories other than the current working directory.
+     */
+    private static class ScriptCommandLauncher extends CommandLauncherProxy
+    {
+        ScriptCommandLauncher(String script, CommandLauncher launcher)
+        {
+            super(launcher);
+            _script = script;
+        }
+
+        /** 
+         * Launches the given command in a new process, in the given working
+         * directory
+         */
+        public Process exec(Project project, String[] cmd, String[] env, File 
workingDir) throws IOException
+        {
+            if ( workingDir == null ) {
+                return exec(project, cmd, env);
+            }
+
+            // Locate the auxiliary script
+            if ( project == null ) {
+                throw new IOException("Cannot locate antRun script: No project 
provided");
+            }
+            String antHome = project.getProperty("ant.home");
+            if ( antHome == null ) {
+                throw new IOException("Cannot locate antRun script: Property 
'ant.home' not found");
+            }
+            String antRun = project.resolveFile(antHome + File.separator + 
_script).toString();
+
+            // Build the command
+           String[] newcmd = new String[cmd.length + 2];
+           newcmd[0] = antRun;
+           newcmd[1] = workingDir.getAbsolutePath();
+           System.arraycopy(cmd, 0, newcmd, 2, cmd.length);
+
+            return exec(project, newcmd, env);
+        }
+
+        private String _script;
     }
 }
Index: src/main/org/apache/tools/ant/taskdefs/optional/javacc/JavaCC.java
===================================================================
RCS file: 
/home/cvspublic/jakarta-ant/src/main/org/apache/tools/ant/taskdefs/optional/javacc/JavaCC.java,v
retrieving revision 1.6
diff -u -r1.6 JavaCC.java
--- src/main/org/apache/tools/ant/taskdefs/optional/javacc/JavaCC.java  
2000/09/25 14:39:29     1.6
+++ src/main/org/apache/tools/ant/taskdefs/optional/javacc/JavaCC.java  
2000/09/28 11:36:32
@@ -216,23 +216,25 @@
             cmdl.createArgument().setValue("-"+name+":"+value.toString());
         }
 
-        // load command line with required attributes
-        if (outputDirectory != null) {
-            if (!outputDirectory.isDirectory()) {
-                throw new BuildException("Outputdir not a directory.");
-            }
-            cmdl.createArgument().setValue(
-                "-OUTPUT_DIRECTORY:"+outputDirectory.getAbsolutePath());
-        }
-
+        // check the target is a file
         if (target == null || !target.isFile()) {
             throw new BuildException("Invalid target: " + target);
         }
 
+        // use the directory containing the target as the output directory
+        if (outputDirectory == null) {
+            outputDirectory = new File(target.getParent());
+        }
+        else if (!outputDirectory.isDirectory()) {
+            throw new BuildException("Outputdir not a directory.");
+        }
+        cmdl.createArgument().setValue(
+            "-OUTPUT_DIRECTORY:"+outputDirectory.getAbsolutePath());
+
         // determine if the generated java file is up-to-date
         final File javaFile = getOutputJavaFile(outputDirectory, target);
         if (javaFile.exists() && target.lastModified() < 
javaFile.lastModified()) {
-            project.log("Target is already built - skipping (" + target + ")");
+            log("Target is already built - skipping (" + target + ")", 
Project.MSG_VERBOSE);
             return;
         }
         cmdl.createArgument().setValue(target.getAbsolutePath());
@@ -247,23 +249,8 @@
         final Commandline.Argument arg = cmdl.createVmArgument();
         arg.setValue("-mx140M");
         arg.setValue("-Dinstall.root="+javaccHome.getAbsolutePath());
-
-        final Execute process =
-            new Execute(new LogStreamHandler(this,
-                                             Project.MSG_INFO,
-                                             Project.MSG_INFO),
-                        null);
-        log(cmdl.toString(), Project.MSG_VERBOSE);
-        process.setCommandline(cmdl.getCommandline());
 
-        try {
-            if (process.execute() != 0) {
-                throw new BuildException("JavaCC failed.");
-            }
-        }
-        catch (IOException e) {
-            throw new BuildException("Failed to launch JavaCC: " + e);
-        }
+        Execute.runCommand(this, cmdl.getCommandline());
     }
 
     /**
Title: JavaCC Task

JavaCC

Description

Invokes the JavaCC compiler compiler on a grammar file.

To use the javacc task, set the target attribute to the name of the grammar file to process. You also need to specify the directory containing the JavaCC installation using the javacchome attribute, so that ant can find the JavaCC classes. Optionally, you can also set the outputdirectory to write the generated file to a specific directory. Otherwise javacc writes the generated files to the directory containing the grammar file.

This task only invokes JavaCC if the grammar file is newer than the generated Java files. javacc assumes that the Java class name of the generated parser is the same as the name of the grammar file, ignoring the .jj. If this is not the case, the javacc task will still work, but it will always generate the output files.

Parameters

Attribute Description Required
target The grammar file to process. Yes
javacchome The directory containing the JavaCC distribution. Yes
outputdirectory The directory to write the generated files to. If not set, the files are written to the directory containing the grammar file. No
buildparser Sets the BUILD_PARSER grammar option. This is a boolean option. No
buildtokenmanager Sets the BUILD_TOKEN_MANAGER grammar option. This is a boolean option. No
cachetokens Sets the CACHE_TOKENS grammar option. This is a boolean option. No
choiceambiguitycheck Sets the CHOICE_AMBIGUITY_CHECK grammar option. This is an integer option. No
commontokenaction Sets the COMMON_TOKEN_ACTION grammar option. This is a boolean option. No
debuglookahead Sets the DEBUG_LOOKAHEAD grammar option. This is a boolean option. No
debugparser Sets the DEBUG_PARSER grammar option. This is a boolean option. No
debugtokenmanager Sets the DEBUG_TOKEN_MANAGER grammar option. This is a boolean option. No
errorreporting Sets the ERROR_REPORTING grammar option. This is a boolean option. No
forcelacheck Sets the FORCE_LA_CHECK grammar option. This is a boolean option. No
ignorecase Sets the IGNORE_CASE grammar option. This is a boolean option. No
javaunicodeescape Sets the JAVA_UNICODE_ESCAPE grammar option. This is a boolean option. No
lookahead Sets the LOOKAHEAD grammar option. This is an integer option. No
optimizetokenmanager Sets the OPTIMIZE_TOKEN_MANAGER grammar option. This is a boolean option. No
otherambiguitycheck Sets the OTHER_AMBIGUITY_CHECK grammar option. This is an integer option. No
sanitycheck Sets the SANITY_CHECK grammar option. This is a boolean option. No
static Sets the STATIC grammar option. This is a boolean option. No
unicodeinput Sets the UNICODE_INPUT grammar option. This is a boolean option. No
usercharstream Sets the USER_CHAR_STREAM grammar option. This is a boolean option. No
usertokenmanager Sets the USER_TOKEN_MANAGER grammar option. This is a boolean option. No

Example

<javacc 
    target="src/Parser.jj" 
    outputdirectory"build/src"
    javacchome="c:/program files/JavaCC" 
    static="true"
/>

This invokes JavaCC on grammar file src/Parser.jj, writing the generated files to build/src. The grammar option STATIC is set to true when invoking JavaCC.


Reply via email to