1 | /*
|
---|
2 | * Copyright 2000-2004 The Apache Software Foundation
|
---|
3 | *
|
---|
4 | * Licensed under the Apache License, Version 2.0 (the "License");
|
---|
5 | * you may not use this file except in compliance with the License.
|
---|
6 | * You may obtain a copy of the License at
|
---|
7 | *
|
---|
8 | * http://www.apache.org/licenses/LICENSE-2.0
|
---|
9 | *
|
---|
10 | * Unless required by applicable law or agreed to in writing, software
|
---|
11 | * distributed under the License is distributed on an "AS IS" BASIS,
|
---|
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
---|
13 | * See the License for the specific language governing permissions and
|
---|
14 | * limitations under the License.
|
---|
15 | *
|
---|
16 | */
|
---|
17 |
|
---|
18 | package org.apache.tools.ant.taskdefs.optional;
|
---|
19 |
|
---|
20 | import java.io.BufferedReader;
|
---|
21 | import java.io.ByteArrayOutputStream;
|
---|
22 | import java.io.File;
|
---|
23 | import java.io.FileReader;
|
---|
24 | import java.io.IOException;
|
---|
25 | import org.apache.tools.ant.AntClassLoader;
|
---|
26 | import org.apache.tools.ant.BuildException;
|
---|
27 | import org.apache.tools.ant.Project;
|
---|
28 | import org.apache.tools.ant.Task;
|
---|
29 | import org.apache.tools.ant.taskdefs.Execute;
|
---|
30 | import org.apache.tools.ant.taskdefs.LogOutputStream;
|
---|
31 | import org.apache.tools.ant.taskdefs.PumpStreamHandler;
|
---|
32 | import org.apache.tools.ant.taskdefs.condition.Os;
|
---|
33 | import org.apache.tools.ant.types.Commandline;
|
---|
34 | import org.apache.tools.ant.types.CommandlineJava;
|
---|
35 | import org.apache.tools.ant.types.Path;
|
---|
36 | import org.apache.tools.ant.util.JavaEnvUtils;
|
---|
37 | import org.apache.tools.ant.util.LoaderUtils;
|
---|
38 | import org.apache.tools.ant.util.TeeOutputStream;
|
---|
39 | import org.apache.tools.ant.util.FileUtils;
|
---|
40 |
|
---|
41 | /**
|
---|
42 | * Invokes the ANTLR Translator generator on a grammar file.
|
---|
43 | *
|
---|
44 | */
|
---|
45 | public class ANTLR extends Task {
|
---|
46 |
|
---|
47 | private CommandlineJava commandline = new CommandlineJava();
|
---|
48 |
|
---|
49 | /** the file to process */
|
---|
50 | private File target;
|
---|
51 |
|
---|
52 | /** where to output the result */
|
---|
53 | private File outputDirectory;
|
---|
54 |
|
---|
55 | /** an optional super grammar file */
|
---|
56 | private File superGrammar;
|
---|
57 |
|
---|
58 | /** optional flag to enable html output */
|
---|
59 | private boolean html;
|
---|
60 |
|
---|
61 | /** optional flag to print out a diagnostic file */
|
---|
62 | private boolean diagnostic;
|
---|
63 |
|
---|
64 | /** optional flag to add trace methods */
|
---|
65 | private boolean trace;
|
---|
66 |
|
---|
67 | /** optional flag to add trace methods to the parser only */
|
---|
68 | private boolean traceParser;
|
---|
69 |
|
---|
70 | /** optional flag to add trace methods to the lexer only */
|
---|
71 | private boolean traceLexer;
|
---|
72 |
|
---|
73 | /** optional flag to add trace methods to the tree walker only */
|
---|
74 | private boolean traceTreeWalker;
|
---|
75 |
|
---|
76 | /** working directory */
|
---|
77 | private File workingdir = null;
|
---|
78 |
|
---|
79 | /** captures ANTLR's output */
|
---|
80 | private ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
---|
81 |
|
---|
82 | /** The debug attribute */
|
---|
83 | private boolean debug;
|
---|
84 |
|
---|
85 |
|
---|
86 | /** Instance of a utility class to use for file operations. */
|
---|
87 | private FileUtils fileUtils;
|
---|
88 |
|
---|
89 | public ANTLR() {
|
---|
90 | commandline.setVm(JavaEnvUtils.getJreExecutable("java"));
|
---|
91 | commandline.setClassname("antlr.Tool");
|
---|
92 | fileUtils = FileUtils.newFileUtils();
|
---|
93 | }
|
---|
94 |
|
---|
95 | /**
|
---|
96 | * The grammar file to process.
|
---|
97 | */
|
---|
98 | public void setTarget(File target) {
|
---|
99 | log("Setting target to: " + target.toString(), Project.MSG_VERBOSE);
|
---|
100 | this.target = target;
|
---|
101 | }
|
---|
102 |
|
---|
103 | /**
|
---|
104 | * The directory to write the generated files to.
|
---|
105 | */
|
---|
106 | public void setOutputdirectory(File outputDirectory) {
|
---|
107 | log("Setting output directory to: " + outputDirectory.toString(), Project.MSG_VERBOSE);
|
---|
108 | this.outputDirectory = outputDirectory;
|
---|
109 | }
|
---|
110 |
|
---|
111 | /**
|
---|
112 | * Sets an optional super grammar file.
|
---|
113 | * Use setGlib(File superGrammar) instead.
|
---|
114 | * @deprecated since ant 1.6
|
---|
115 | */
|
---|
116 | public void setGlib(String superGrammar) {
|
---|
117 | String sg = null;
|
---|
118 | if (Os.isFamily("dos")) {
|
---|
119 | sg = superGrammar.replace('\\', '/');
|
---|
120 | } else {
|
---|
121 | sg = superGrammar;
|
---|
122 | }
|
---|
123 | setGlib(fileUtils.resolveFile(getProject().getBaseDir(), sg));
|
---|
124 | }
|
---|
125 | /**
|
---|
126 | * Sets an optional super grammar file
|
---|
127 | * @since ant 1.6
|
---|
128 | */
|
---|
129 | public void setGlib(File superGrammar) {
|
---|
130 | this.superGrammar = superGrammar;
|
---|
131 | }
|
---|
132 | /**
|
---|
133 | * Sets a flag to enable ParseView debugging
|
---|
134 | */
|
---|
135 | public void setDebug(boolean enable) {
|
---|
136 | this.debug = enable;
|
---|
137 | }
|
---|
138 |
|
---|
139 | /**
|
---|
140 | * If true, emit html
|
---|
141 | */
|
---|
142 | public void setHtml(boolean enable) {
|
---|
143 | html = enable;
|
---|
144 | }
|
---|
145 |
|
---|
146 | /**
|
---|
147 | * Sets a flag to emit diagnostic text
|
---|
148 | */
|
---|
149 | public void setDiagnostic(boolean enable) {
|
---|
150 | diagnostic = enable;
|
---|
151 | }
|
---|
152 |
|
---|
153 | /**
|
---|
154 | * If true, enables all tracing.
|
---|
155 | */
|
---|
156 | public void setTrace(boolean enable) {
|
---|
157 | trace = enable;
|
---|
158 | }
|
---|
159 |
|
---|
160 | /**
|
---|
161 | * If true, enables parser tracing.
|
---|
162 | */
|
---|
163 | public void setTraceParser(boolean enable) {
|
---|
164 | traceParser = enable;
|
---|
165 | }
|
---|
166 |
|
---|
167 | /**
|
---|
168 | * If true, enables lexer tracing.
|
---|
169 | */
|
---|
170 | public void setTraceLexer(boolean enable) {
|
---|
171 | traceLexer = enable;
|
---|
172 | }
|
---|
173 |
|
---|
174 | /**
|
---|
175 | * Sets a flag to allow the user to enable tree walker tracing
|
---|
176 | */
|
---|
177 | public void setTraceTreeWalker(boolean enable) {
|
---|
178 | traceTreeWalker = enable;
|
---|
179 | }
|
---|
180 |
|
---|
181 | // we are forced to fork ANTLR since there is a call
|
---|
182 | // to System.exit() and there is nothing we can do
|
---|
183 | // right now to avoid this. :-( (SBa)
|
---|
184 | // I'm not removing this method to keep backward compatibility
|
---|
185 | /**
|
---|
186 | * @ant.attribute ignore="true"
|
---|
187 | */
|
---|
188 | public void setFork(boolean s) {
|
---|
189 | //this.fork = s;
|
---|
190 | }
|
---|
191 |
|
---|
192 | /**
|
---|
193 | * The working directory of the process
|
---|
194 | */
|
---|
195 | public void setDir(File d) {
|
---|
196 | this.workingdir = d;
|
---|
197 | }
|
---|
198 |
|
---|
199 | /**
|
---|
200 | * Adds a classpath to be set
|
---|
201 | * because a directory might be given for Antlr debug.
|
---|
202 | */
|
---|
203 | public Path createClasspath() {
|
---|
204 | return commandline.createClasspath(getProject()).createPath();
|
---|
205 | }
|
---|
206 |
|
---|
207 | /**
|
---|
208 | * Adds a new JVM argument.
|
---|
209 | * @return create a new JVM argument so that any argument can be passed to the JVM.
|
---|
210 | * @see #setFork(boolean)
|
---|
211 | */
|
---|
212 | public Commandline.Argument createJvmarg() {
|
---|
213 | return commandline.createVmArgument();
|
---|
214 | }
|
---|
215 |
|
---|
216 | /**
|
---|
217 | * Adds the jars or directories containing Antlr
|
---|
218 | * this should make the forked JVM work without having to
|
---|
219 | * specify it directly.
|
---|
220 | */
|
---|
221 | public void init() throws BuildException {
|
---|
222 | addClasspathEntry("/antlr/ANTLRGrammarParseBehavior.class");
|
---|
223 | }
|
---|
224 |
|
---|
225 | /**
|
---|
226 | * Search for the given resource and add the directory or archive
|
---|
227 | * that contains it to the classpath.
|
---|
228 | *
|
---|
229 | * <p>Doesn't work for archives in JDK 1.1 as the URL returned by
|
---|
230 | * getResource doesn't contain the name of the archive.</p>
|
---|
231 | */
|
---|
232 | protected void addClasspathEntry(String resource) {
|
---|
233 | /*
|
---|
234 | * pre Ant 1.6 this method used to call getClass().getResource
|
---|
235 | * while Ant 1.6 will call ClassLoader.getResource().
|
---|
236 | *
|
---|
237 | * The difference is that Class.getResource expects a leading
|
---|
238 | * slash for "absolute" resources and will strip it before
|
---|
239 | * delegating to ClassLoader.getResource - so we now have to
|
---|
240 | * emulate Class's behavior.
|
---|
241 | */
|
---|
242 | if (resource.startsWith("/")) {
|
---|
243 | resource = resource.substring(1);
|
---|
244 | } else {
|
---|
245 | resource = "org/apache/tools/ant/taskdefs/optional/"
|
---|
246 | + resource;
|
---|
247 | }
|
---|
248 |
|
---|
249 | File f = LoaderUtils.getResourceSource(getClass().getClassLoader(),
|
---|
250 | resource);
|
---|
251 | if (f != null) {
|
---|
252 | log("Found " + f.getAbsolutePath(), Project.MSG_DEBUG);
|
---|
253 | createClasspath().setLocation(f);
|
---|
254 | } else {
|
---|
255 | log("Couldn\'t find " + resource, Project.MSG_VERBOSE);
|
---|
256 | }
|
---|
257 | }
|
---|
258 |
|
---|
259 | public void execute() throws BuildException {
|
---|
260 | validateAttributes();
|
---|
261 |
|
---|
262 | //TODO: use ANTLR to parse the grammar file to do this.
|
---|
263 | File generatedFile = getGeneratedFile();
|
---|
264 | boolean targetIsOutOfDate =
|
---|
265 | target.lastModified() > generatedFile.lastModified();
|
---|
266 | boolean superGrammarIsOutOfDate = superGrammar != null
|
---|
267 | && (superGrammar.lastModified() > generatedFile.lastModified());
|
---|
268 | if (targetIsOutOfDate || superGrammarIsOutOfDate) {
|
---|
269 | if (targetIsOutOfDate) {
|
---|
270 | log("Compiling " + target + " as it is newer than "
|
---|
271 | + generatedFile, Project.MSG_VERBOSE);
|
---|
272 | } else if (superGrammarIsOutOfDate) {
|
---|
273 | log("Compiling " + target + " as " + superGrammar
|
---|
274 | + " is newer than " + generatedFile, Project.MSG_VERBOSE);
|
---|
275 | }
|
---|
276 | populateAttributes();
|
---|
277 | commandline.createArgument().setValue(target.toString());
|
---|
278 |
|
---|
279 | log(commandline.describeCommand(), Project.MSG_VERBOSE);
|
---|
280 | int err = run(commandline.getCommandline());
|
---|
281 | if (err != 0) {
|
---|
282 | throw new BuildException("ANTLR returned: " + err, getLocation());
|
---|
283 | } else {
|
---|
284 | String output = bos.toString();
|
---|
285 | if (output.indexOf("error:") > -1) {
|
---|
286 | throw new BuildException("ANTLR signaled an error: "
|
---|
287 | + output, getLocation());
|
---|
288 | }
|
---|
289 | }
|
---|
290 | } else {
|
---|
291 | log("Skipped grammar file. Generated file " + generatedFile
|
---|
292 | + " is newer.", Project.MSG_VERBOSE);
|
---|
293 | }
|
---|
294 | }
|
---|
295 |
|
---|
296 | /**
|
---|
297 | * A refactored method for populating all the command line arguments based
|
---|
298 | * on the user-specified attributes.
|
---|
299 | */
|
---|
300 | private void populateAttributes() {
|
---|
301 | commandline.createArgument().setValue("-o");
|
---|
302 | commandline.createArgument().setValue(outputDirectory.toString());
|
---|
303 | if (superGrammar != null) {
|
---|
304 | commandline.createArgument().setValue("-glib");
|
---|
305 | commandline.createArgument().setValue(superGrammar.toString());
|
---|
306 | }
|
---|
307 | if (html) {
|
---|
308 | commandline.createArgument().setValue("-html");
|
---|
309 | }
|
---|
310 | if (diagnostic) {
|
---|
311 | commandline.createArgument().setValue("-diagnostic");
|
---|
312 | }
|
---|
313 | if (trace) {
|
---|
314 | commandline.createArgument().setValue("-trace");
|
---|
315 | }
|
---|
316 | if (traceParser) {
|
---|
317 | commandline.createArgument().setValue("-traceParser");
|
---|
318 | }
|
---|
319 | if (traceLexer) {
|
---|
320 | commandline.createArgument().setValue("-traceLexer");
|
---|
321 | }
|
---|
322 | if (traceTreeWalker) {
|
---|
323 | if (is272()) {
|
---|
324 | commandline.createArgument().setValue("-traceTreeParser");
|
---|
325 | } else {
|
---|
326 | commandline.createArgument().setValue("-traceTreeWalker");
|
---|
327 | }
|
---|
328 | }
|
---|
329 | if (debug) {
|
---|
330 | commandline.createArgument().setValue("-debug");
|
---|
331 | }
|
---|
332 | }
|
---|
333 |
|
---|
334 | private void validateAttributes() throws BuildException {
|
---|
335 | if (target == null || !target.isFile()) {
|
---|
336 | throw new BuildException("Invalid target: " + target);
|
---|
337 | }
|
---|
338 |
|
---|
339 | // if no output directory is specified, used the target's directory
|
---|
340 | if (outputDirectory == null) {
|
---|
341 | setOutputdirectory(new File(target.getParent()));
|
---|
342 | }
|
---|
343 | if (!outputDirectory.isDirectory()) {
|
---|
344 | throw new BuildException("Invalid output directory: " + outputDirectory);
|
---|
345 | }
|
---|
346 | }
|
---|
347 |
|
---|
348 | private File getGeneratedFile() throws BuildException {
|
---|
349 | String generatedFileName = null;
|
---|
350 | try {
|
---|
351 | BufferedReader in = new BufferedReader(new FileReader(target));
|
---|
352 | String line;
|
---|
353 | while ((line = in.readLine()) != null) {
|
---|
354 | int extendsIndex = line.indexOf(" extends ");
|
---|
355 | if (line.startsWith("class ") && extendsIndex > -1) {
|
---|
356 | generatedFileName = line.substring(6, extendsIndex).trim();
|
---|
357 | break;
|
---|
358 | }
|
---|
359 | }
|
---|
360 | in.close();
|
---|
361 | } catch (Exception e) {
|
---|
362 | throw new BuildException("Unable to determine generated class", e);
|
---|
363 | }
|
---|
364 | if (generatedFileName == null) {
|
---|
365 | throw new BuildException("Unable to determine generated class");
|
---|
366 | }
|
---|
367 | return new File(outputDirectory, generatedFileName + ".java");
|
---|
368 | }
|
---|
369 |
|
---|
370 | /** execute in a forked VM */
|
---|
371 | private int run(String[] command) throws BuildException {
|
---|
372 | PumpStreamHandler psh =
|
---|
373 | new PumpStreamHandler(new LogOutputStream(this, Project.MSG_INFO),
|
---|
374 | new TeeOutputStream(
|
---|
375 | new LogOutputStream(this,
|
---|
376 | Project.MSG_WARN),
|
---|
377 | bos)
|
---|
378 | );
|
---|
379 | Execute exe = new Execute(psh, null);
|
---|
380 | exe.setAntRun(getProject());
|
---|
381 | if (workingdir != null) {
|
---|
382 | exe.setWorkingDirectory(workingdir);
|
---|
383 | }
|
---|
384 | exe.setCommandline(command);
|
---|
385 | try {
|
---|
386 | return exe.execute();
|
---|
387 | } catch (IOException e) {
|
---|
388 | throw new BuildException(e, getLocation());
|
---|
389 | } finally {
|
---|
390 | try {
|
---|
391 | bos.close();
|
---|
392 | } catch (IOException e) {
|
---|
393 | // ignore
|
---|
394 | }
|
---|
395 | }
|
---|
396 | }
|
---|
397 |
|
---|
398 | /**
|
---|
399 | * Whether the antlr version is 2.7.2 (or higher).
|
---|
400 | *
|
---|
401 | * @return true if the version of Antlr present is 2.7.2 or later.
|
---|
402 | * @since Ant 1.6
|
---|
403 | */
|
---|
404 | protected boolean is272() {
|
---|
405 | try {
|
---|
406 | AntClassLoader l = new AntClassLoader(getProject(),
|
---|
407 | commandline.getClasspath());
|
---|
408 | l.loadClass("antlr.Version");
|
---|
409 | return true;
|
---|
410 | } catch (ClassNotFoundException e) {
|
---|
411 | return false;
|
---|
412 | } // end of try-catch
|
---|
413 | }
|
---|
414 | }
|
---|