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.
