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 * 2006-12-29: Modified to work for antlr3 by J�rgen Pfundt 17 * 2007-01-04: Some minor correction after checking code with findBugs tool 18 * 2007-02-10: Adapted the grammar type recognition to the changed naming 19 * conventions for Tree Parser 20 * 2007-10-17: Options "trace", "traceLexer", "traceParser" and "glib" emit 21 * warnings when being used. 22 * Added recognition of "parser grammar T". 23 * Added options "nocollapse", "noprune". 24 * ANTLR option "depend" is being used to resolve build dependencies. 25 * 2007-11-15: Embedded Classpath statement had not been observed 26 * with option depend="true" (Reported by Mats Behre) 27 * 2008-03-31: Support the option conversiontimeout. (Jim Idle) 28 * 2007-12-31: With option "depend=true" proceed even if first pass failed so 29 * that ANTLR can spit out its errors 30 * 2008-08-09: Inspecting environment variable ANTLR_HOME to detect and add 31 * antlr- and stringtemplate libraries to the classpath 32 * 2008-08-09: Removed routine checkGenerateFile. It got feeble with the 33 * introduction of composed grammars, e.g. "import T.g" and after 34 * a short struggle it started it's journey to /dev/null. 35 * From now one it is always antlr itself via the depend option 36 * which decides about dependecies 37 * 2008-08-19: Dependency check for composed grammars added. 38 * Might need some further improvements. 39 */ 40 package org.apache.tools.ant.antlr; 41 42 import java.util.regex.*; 43 import java.io.*; 44 import java.util.Map; 45 import org.apache.tools.ant.BuildException; 46 import org.apache.tools.ant.DirectoryScanner; 47 import org.apache.tools.ant.Project; 48 import org.apache.tools.ant.Task; 49 import org.apache.tools.ant.taskdefs.Execute; 50 import org.apache.tools.ant.taskdefs.LogOutputStream; 51 import org.apache.tools.ant.taskdefs.PumpStreamHandler; 52 import org.apache.tools.ant.taskdefs.Redirector; 53 import org.apache.tools.ant.types.Commandline; 54 import org.apache.tools.ant.types.CommandlineJava; 55 import org.apache.tools.ant.types.Path; 56 import org.apache.tools.ant.util.JavaEnvUtils; 57 import org.apache.tools.ant.util.LoaderUtils; 58 import org.apache.tools.ant.util.TeeOutputStream; 59 import org.apache.tools.ant.util.FileUtils; 60 61 /** 62 * Invokes the ANTLR3 Translator generator on a grammar file. 63 * 64 */ 65 public class ANTLR3 extends Task { 66 67 private CommandlineJava commandline = new CommandlineJava(); 68 /** the file to process */ 69 private File target = null; 70 /** where to output the result */ 71 private File outputDirectory = null; 72 /** location of token files */ 73 private File libDirectory = null; 74 /** an optional super grammar file */ 75 private File superGrammar; 76 /** depend */ 77 private boolean depend = false; 78 /** fork */ 79 private boolean fork; 80 /** name of output style for messages */ 81 private String messageFormatName; 82 /** optional flag to print out a diagnostic file */ 83 private boolean diagnostic; 84 /** optional flag to add methods */ 85 private boolean trace; 86 /** optional flag to add trace methods to the parser only */ 87 private boolean traceParser; 88 /** optional flag to add trace methods to the lexer only */ 89 private boolean traceLexer; 90 /** working directory */ 91 private File workingdir = null; 92 /** captures ANTLR's output */ 93 private ByteArrayOutputStream bos = new ByteArrayOutputStream(); 94 /** The debug attribute */ 95 private boolean debug; 96 /** The report attribute */ 97 private boolean report; 98 /** The print attribute */ 99 private boolean print; 100 /** The profile attribute */ 101 private boolean profile; 102 /** The nfa attribute */ 103 private boolean nfa; 104 /** The dfa attribute */ 105 private boolean dfa; 106 /** multi threaded analysis */ 107 private boolean multiThreaded; 108 /** collapse incident edges into DFA states */ 109 private boolean nocollapse; 110 /** test lookahead against EBNF block exit branches */ 111 private boolean noprune; 112 /** put tags at start/stop of all templates in output */ 113 private boolean dbgST; 114 /** print AST */ 115 private boolean grammarTree; 116 /** Instance of a utility class to use for file operations. */ 117 private FileUtils fileUtils; 118 /** 119 * Whether to override the default conversion timeout with -Xconversiontimeout nnnn 120 */ 121 private String conversiontimeout; 122 ANTLR3()123 public ANTLR3() { 124 commandline.setVm(JavaEnvUtils.getJreExecutable("java")); 125 commandline.setClassname("org.antlr.Tool"); 126 fileUtils = FileUtils.getFileUtils(); 127 } 128 129 /** 130 * The grammar file to process. 131 */ setTarget(File targetFile)132 public void setTarget(File targetFile) { 133 log("Setting target to: " + targetFile.toString(), Project.MSG_VERBOSE); 134 this.target = targetFile; 135 } 136 137 /** 138 * The directory to write the generated files to. 139 */ setOutputdirectory(File outputDirectoryFile)140 public void setOutputdirectory(File outputDirectoryFile) { 141 log("Setting output directory to: " + outputDirectoryFile.toString(), Project.MSG_VERBOSE); 142 this.outputDirectory = outputDirectoryFile; 143 } 144 145 /** 146 * The directory to write the generated files to. 147 */ getOutputdirectory()148 public File getOutputdirectory() { 149 return outputDirectory; 150 } 151 152 /** 153 * The token files output directory. 154 */ setLibdirectory(File libDirectoryFile)155 public void setLibdirectory(File libDirectoryFile) { 156 log("Setting lib directory to: " + libDirectoryFile.toString(), Project.MSG_VERBOSE); 157 this.libDirectory = libDirectoryFile; 158 } 159 160 /** 161 * The output style for messages. 162 */ setMessageformat(String name)163 public void setMessageformat(String name) { 164 log("Setting message-format to: " + name, Project.MSG_VERBOSE); 165 this.messageFormatName = name; 166 } 167 168 /** 169 * Sets an optional super grammar file 170 * @deprecated 171 */ setGlib(File superGrammarFile)172 public void setGlib(File superGrammarFile) { 173 this.superGrammar = superGrammarFile; 174 } 175 176 /** 177 * Sets a flag to enable ParseView debugging 178 */ setDebug(boolean enable)179 public void setDebug(boolean enable) { 180 this.debug = enable; 181 } 182 183 /** 184 * Sets a flag to enable report statistics 185 */ setReport(boolean enable)186 public void setReport(boolean enable) { 187 this.report = enable; 188 } 189 190 /** 191 * Sets a flag to print out the grammar without actions 192 */ setPrint(boolean enable)193 public void setPrint(boolean enable) { 194 this.print = enable; 195 } 196 197 /** 198 * Sets a flag to enable profiling 199 */ setProfile(boolean enable)200 public void setProfile(boolean enable) { 201 this.profile = enable; 202 } 203 204 /** 205 * Sets a flag to enable nfa generation 206 */ setNfa(boolean enable)207 public void setNfa(boolean enable) { 208 this.nfa = enable; 209 } 210 211 /** 212 * Sets a flag to enable nfa generation 213 */ setDfa(boolean enable)214 public void setDfa(boolean enable) { 215 this.dfa = enable; 216 } 217 218 /** 219 * Run the analysis multithreaded 220 * @param enable 221 */ setMultithreaded(boolean enable)222 public void setMultithreaded(boolean enable) { 223 multiThreaded = enable; 224 } 225 226 /** 227 * collapse incident edges into DFA states 228 * @param enable 229 */ setNocollapse(boolean enable)230 public void setNocollapse(boolean enable) { 231 nocollapse = enable; 232 } 233 234 /** 235 * test lookahead against EBNF block exit branches 236 * @param enable 237 */ setNoprune(boolean enable)238 public void setNoprune(boolean enable) { 239 noprune = enable; 240 } 241 242 /** 243 * test lookahead against EBNF block exit branches 244 * @param enable 245 */ setDbgST(boolean enable)246 public void setDbgST(boolean enable) { 247 dbgST = enable; 248 } 249 250 /** 251 * override the default conversion timeout with -Xconversiontimeout nnnn 252 * @param conversiontimeoutString 253 */ setConversiontimeout(String conversiontimeoutString)254 public void setConversiontimeout(String conversiontimeoutString) { 255 log("Setting conversiontimeout to: " + conversiontimeoutString, Project.MSG_VERBOSE); 256 try { 257 int timeout = Integer.valueOf(conversiontimeoutString); 258 this.conversiontimeout = conversiontimeoutString; 259 } catch (NumberFormatException e) { 260 log("Option ConversionTimeOut ignored due to illegal value: '" + conversiontimeoutString + "'", Project.MSG_ERR); 261 } 262 } 263 264 /** 265 * Set a flag to enable printing of the grammar tree 266 */ setGrammartree(boolean enable)267 public void setGrammartree(boolean enable) { 268 grammarTree = enable; 269 } 270 271 /** 272 * Set a flag to enable dependency checking by ANTLR itself 273 * The depend option is always used implicitely inside the antlr3 task 274 * @deprecated 275 */ setDepend(boolean s)276 public void setDepend(boolean s) { 277 this.depend = s; 278 } 279 280 /** 281 * Sets a flag to emit diagnostic text 282 */ setDiagnostic(boolean enable)283 public void setDiagnostic(boolean enable) { 284 diagnostic = enable; 285 } 286 287 /** 288 * If true, enables all tracing. 289 * @deprecated 290 */ setTrace(boolean enable)291 public void setTrace(boolean enable) { 292 trace = enable; 293 } 294 295 /** 296 * If true, enables parser tracing. 297 * @deprecated 298 */ setTraceParser(boolean enable)299 public void setTraceParser(boolean enable) { 300 traceParser = enable; 301 } 302 303 /** 304 * If true, enables lexer tracing. 305 * @deprecated 306 */ setTraceLexer(boolean enable)307 public void setTraceLexer(boolean enable) { 308 traceLexer = enable; 309 } 310 311 // we are forced to fork ANTLR since there is a call 312 // to System.exit() and there is nothing we can do 313 // right now to avoid this. :-( (SBa) 314 // I'm not removing this method to keep backward compatibility 315 /** 316 * @ant.attribute ignore="true" 317 */ setFork(boolean s)318 public void setFork(boolean s) { 319 this.fork = s; 320 } 321 322 /** 323 * The working directory of the process 324 */ setDir(File d)325 public void setDir(File d) { 326 this.workingdir = d; 327 } 328 329 /** 330 * Adds a classpath to be set 331 * because a directory might be given for Antlr debug. 332 */ createClasspath()333 public Path createClasspath() { 334 return commandline.createClasspath(getProject()).createPath(); 335 } 336 337 /** 338 * Adds a new JVM argument. 339 * @return create a new JVM argument so that any argument can be passed to the JVM. 340 * @see #setFork(boolean) 341 */ createJvmarg()342 public Commandline.Argument createJvmarg() { 343 return commandline.createVmArgument(); 344 } 345 346 /** 347 * Adds the jars or directories containing Antlr and associates. 348 * This should make the forked JVM work without having to 349 * specify it directly. 350 */ 351 @Override init()352 public void init() throws BuildException { 353 /* Inquire environment variables */ 354 Map<String, String> variables = System.getenv(); 355 /* Get value for key "ANTLR_HOME" which should hopefully point to 356 * the directory where the current version of antlr3 is installed */ 357 String antlrHome = variables.get("ANTLR_HOME"); 358 if (antlrHome != null) { 359 /* Environment variable ANTLR_HOME has been defined. 360 * Now add all antlr and stringtemplate libraries to the 361 * classpath */ 362 addAntlrJarsToClasspath(antlrHome + "/lib"); 363 } 364 addClasspathEntry("/antlr/ANTLRGrammarParseBehavior.class", "AntLR2"); 365 addClasspathEntry("/org/antlr/tool/ANTLRParser.class", "AntLR3"); 366 addClasspathEntry("/org/antlr/stringtemplate/StringTemplate.class", "Stringtemplate"); 367 368 369 } 370 371 /** 372 * Search for the given resource and add the directory or archive 373 * that contains it to the classpath. 374 * 375 * <p>Doesn't work for archives in JDK 1.1 as the URL returned by 376 * getResource doesn't contain the name of the archive.</p> 377 */ addClasspathEntry(String resource, String msg)378 protected void addClasspathEntry(String resource, String msg) { 379 /* 380 * pre Ant 1.6 this method used to call getClass().getResource 381 * while Ant 1.6 will call ClassLoader.getResource(). 382 * 383 * The difference is that Class.getResource expects a leading 384 * slash for "absolute" resources and will strip it before 385 * delegating to ClassLoader.getResource - so we now have to 386 * emulate Class's behavior. 387 */ 388 if (resource.startsWith("/")) { 389 resource = resource.substring(1); 390 } else { 391 resource = "org/apache/tools/ant/taskdefs/optional/" + resource; 392 } 393 394 File f = LoaderUtils.getResourceSource(getClass().getClassLoader(), resource); 395 if (f != null) { 396 log("Found via classpath: " + f.getAbsolutePath(), Project.MSG_VERBOSE); 397 createClasspath().setLocation(f); 398 } else { 399 log("Couldn\'t find resource " + resource + " for library " + msg + " in external classpath", Project.MSG_VERBOSE); 400 } 401 } 402 403 /** 404 * If the environment variable ANTLR_HOME is defined and points 405 * to the installation directory of antlr3 then look for all antlr-*.jar and 406 * stringtemplate-*.jar files in the lib directory and add them 407 * to the classpath. 408 * This feature should make working with eclipse or netbeans projects a 409 * little bit easier. As wildcards are being used for the version part 410 * of the jar-archives it makes the task independent of 411 * new releases. Just let ANTLR_HOME point to the new installation 412 * directory. 413 */ addAntlrJarsToClasspath(String antlrLibDir)414 private void addAntlrJarsToClasspath(String antlrLibDir) { 415 String[] includes = {"antlr-*.jar", "stringtemplate-*.jar"}; 416 417 DirectoryScanner ds = new DirectoryScanner(); 418 ds.setIncludes(includes); 419 ds.setBasedir(new File(antlrLibDir)); 420 ds.setCaseSensitive(true); 421 ds.scan(); 422 423 String separator = System.getProperty("file.separator"); 424 String[] files = ds.getIncludedFiles(); 425 for (String file : files) { 426 File f = new File(antlrLibDir + separator + file); 427 log("Found via ANTLR_HOME: " + f.getAbsolutePath(), Project.MSG_VERBOSE); 428 createClasspath().setLocation(f); 429 } 430 } 431 432 @Override execute()433 public void execute() throws BuildException { 434 435 validateAttributes(); 436 437 // Use ANTLR itself to resolve dependencies and decide whether 438 // to invoke ANTLR for compilation 439 if (dependencyCheck()) { 440 populateAttributes(); 441 commandline.createArgument().setValue(target.toString()); 442 443 log(commandline.describeCommand(), Project.MSG_VERBOSE); 444 int err = 0; 445 try { 446 err = run(commandline.getCommandline(), new LogOutputStream(this, Project.MSG_INFO), new LogOutputStream(this, Project.MSG_WARN)); 447 } catch (IOException e) { 448 throw new BuildException(e, getLocation()); 449 } finally { 450 try { 451 bos.close(); 452 } catch (IOException e) { 453 // ignore 454 } 455 } 456 457 if (err != 0) { 458 throw new BuildException("ANTLR returned: " + err, getLocation()); 459 } else { 460 Pattern p = Pattern.compile("error\\([0-9]+\\):"); 461 Matcher m = p.matcher(bos.toString()); 462 if (m.find()) { 463 throw new BuildException("ANTLR signaled an error.", getLocation()); 464 } 465 } 466 } else { 467 try { 468 log("All dependencies of grammar file \'" + target.getCanonicalPath() + "\' are up to date.", Project.MSG_VERBOSE); 469 } catch (IOException ex) { 470 log("All dependencies of grammar file \'" + target.toString() + "\' are up to date.", Project.MSG_VERBOSE); 471 } 472 } 473 } 474 475 /** 476 * A refactored method for populating all the command line arguments based 477 * on the user-specified attributes. 478 */ populateAttributes()479 private void populateAttributes() { 480 481 commandline.createArgument().setValue("-o"); 482 commandline.createArgument().setValue(outputDirectory.toString()); 483 484 commandline.createArgument().setValue("-lib"); 485 commandline.createArgument().setValue(libDirectory.toString()); 486 487 if (superGrammar != null) { 488 log("Option 'glib' is not supported by ANTLR v3. Option ignored!", Project.MSG_WARN); 489 } 490 491 if (diagnostic) { 492 commandline.createArgument().setValue("-diagnostic"); 493 } 494 if (depend) { 495 log("Option 'depend' is implicitely always used by ANTLR v3. Option can safely be omitted!", Project.MSG_WARN); 496 } 497 if (trace) { 498 log("Option 'trace' is not supported by ANTLR v3. Option ignored!", Project.MSG_WARN); 499 } 500 if (traceParser) { 501 log("Option 'traceParser' is not supported by ANTLR v3. Option ignored!", Project.MSG_WARN); 502 } 503 if (traceLexer) { 504 log("Option 'traceLexer' is not supported by ANTLR v3. Option ignored!", Project.MSG_WARN); 505 } 506 if (debug) { 507 commandline.createArgument().setValue("-debug"); 508 } 509 if (report) { 510 commandline.createArgument().setValue("-report"); 511 } 512 if (print) { 513 commandline.createArgument().setValue("-print"); 514 } 515 if (profile) { 516 commandline.createArgument().setValue("-profile"); 517 } 518 if (messageFormatName != null) { 519 commandline.createArgument().setValue("-message-format"); 520 commandline.createArgument().setValue(messageFormatName); 521 } 522 if (nfa) { 523 commandline.createArgument().setValue("-nfa"); 524 } 525 if (dfa) { 526 commandline.createArgument().setValue("-dfa"); 527 } 528 if (multiThreaded) { 529 commandline.createArgument().setValue("-Xmultithreaded"); 530 } 531 if (nocollapse) { 532 commandline.createArgument().setValue("-Xnocollapse"); 533 } 534 if (noprune) { 535 commandline.createArgument().setValue("-Xnoprune"); 536 } 537 if (dbgST) { 538 commandline.createArgument().setValue("-XdbgST"); 539 } 540 if (conversiontimeout != null) { 541 commandline.createArgument().setValue("-Xconversiontimeout"); 542 commandline.createArgument().setValue(conversiontimeout); 543 } 544 if (grammarTree) { 545 commandline.createArgument().setValue("-Xgrtree"); 546 } 547 } 548 validateAttributes()549 private void validateAttributes() throws BuildException { 550 551 if (target == null) { 552 throw new BuildException("No target grammar, lexer grammar or tree parser specified!"); 553 } else if (!target.isFile()) { 554 throw new BuildException("Target: " + target + " is not a file!"); 555 } 556 557 // if no output directory is specified, use the target's directory 558 if (outputDirectory == null) { 559 setOutputdirectory(new File(target.getParent())); 560 } 561 562 if (!outputDirectory.isDirectory()) { 563 throw new BuildException("Invalid output directory: " + outputDirectory); 564 } 565 566 if (workingdir != null && !workingdir.isDirectory()) { 567 throw new BuildException("Invalid working directory: " + workingdir); 568 } 569 570 // if no libDirectory is specified, use the target's directory 571 if (libDirectory == null) { 572 setLibdirectory(new File(target.getParent())); 573 } 574 575 if (!libDirectory.isDirectory()) { 576 throw new BuildException("Invalid lib directory: " + libDirectory); 577 } 578 } 579 dependencyCheck()580 private boolean dependencyCheck() throws BuildException { 581 // using "antlr -o <OutputDirectory> -lib <LibDirectory> -depend <T>" 582 // to get the list of dependencies 583 CommandlineJava cmdline; 584 try { 585 cmdline = (CommandlineJava) commandline.clone(); 586 } catch (java.lang.CloneNotSupportedException e) { 587 throw new BuildException("Clone of commandline failed: " + e); 588 } 589 590 cmdline.createArgument().setValue("-depend"); 591 cmdline.createArgument().setValue("-o"); 592 cmdline.createArgument().setValue(outputDirectory.toString()); 593 cmdline.createArgument().setValue("-lib"); 594 cmdline.createArgument().setValue(libDirectory.toString()); 595 cmdline.createArgument().setValue(target.toString()); 596 597 log(cmdline.describeCommand(), Project.MSG_VERBOSE); 598 599 // redirect output generated by ANTLR to temporary file 600 Redirector r = new Redirector(this); 601 File f; 602 try { 603 f = File.createTempFile("depend", null, getOutputdirectory()); 604 f.deleteOnExit(); 605 log("Write dependencies for '" + target.toString() + "' to file '" + f.getCanonicalPath() + "'", Project.MSG_VERBOSE); 606 r.setOutput(f); 607 r.setAlwaysLog(false); 608 r.createStreams(); 609 } catch (IOException e) { 610 throw new BuildException("Redirection of output failed: " + e); 611 } 612 613 // execute antlr -depend ... 614 int err = 0; 615 try { 616 err = run(cmdline.getCommandline(), r.getOutputStream(), null); 617 } catch (IOException e) { 618 try { 619 r.complete(); 620 log("Redirection of output terminated.", Project.MSG_VERBOSE); 621 } catch (IOException ex) { 622 log("Termination of output redirection failed: " + ex, Project.MSG_ERR); 623 } 624 throw new BuildException(e, getLocation()); 625 } finally { 626 try { 627 bos.close(); 628 } catch (IOException e) { 629 // ignore 630 } 631 } 632 633 try { 634 r.complete(); 635 log("Redirection of output terminated.", Project.MSG_VERBOSE); 636 } catch (IOException e) { 637 log("Termination of output redirection failed: " + e, Project.MSG_ERR); 638 } 639 640 if (err != 0) { 641 if (f.exists()) { 642 f.delete(); 643 } 644 if (cmdline.getClasspath() == null) { 645 log("Antlr libraries not found in external classpath or embedded classpath statement ", Project.MSG_ERR); 646 } 647 log("Dependency check failed. ANTLR returned: " + err, Project.MSG_ERR); 648 return true; 649 } else { 650 Pattern p = Pattern.compile("error\\([0-9]+\\):"); 651 Matcher m = p.matcher(bos.toString()); 652 if (m.find()) { 653 if (f.exists()) { 654 f.delete(); 655 } 656 // On error always recompile 657 return true; 658 } 659 } 660 661 boolean compile = false; 662 663 // open temporary file 664 BufferedReader in = null; 665 try { 666 in = new BufferedReader(new FileReader(f)); 667 } catch (IOException e) { 668 try { 669 if (in != null) { 670 in.close(); 671 } 672 } catch (IOException ex) { 673 throw new BuildException("Could not close file\'" + f.toString() + "\'."); 674 } 675 if (f.exists()) { 676 f.delete(); 677 } 678 try { 679 throw new BuildException("Could not open \'" + f.getCanonicalPath() + "\' for reading."); 680 } catch (IOException ex) { 681 throw new BuildException("Could not open \'" + f.toString() + "\' for reading."); 682 } 683 } 684 685 // evaluate dependencies in temporary file 686 String s; 687 688 try { 689 while ((s = in.readLine()) != null) { 690 String a; 691 String b; 692 // As the separator string in lines emitted by the depend option 693 // is either " : " or ": " trim is invoked for the first file name of a line 694 int to = s.indexOf(": "); 695 if (to >= 0) { 696 a = s.substring(0, to).trim(); 697 File lhs = new File(a); 698 if (!lhs.isFile()) { 699 log("File '" + a + "' is not a regular file", Project.MSG_VERBOSE); 700 String name = lhs.getName(); 701 String[] parts = splitRightHandSide(name, "\\u002E"); 702 if (parts.length <= 1) { 703 a += ".java"; 704 lhs = new File(a); 705 if (lhs.isFile()) { 706 log("File '" + a + "' is a regular file last modified at " + lhs.lastModified(), Project.MSG_VERBOSE); 707 } 708 } 709 } 710 711 b = s.substring(to + ": ".length()); 712 String[] names = splitRightHandSide(b, ", ?"); 713 File aFile = new File(a); 714 for (String name : names) { 715 File bFile = new File(name); 716 log("File '" + a + "' depends on file '" + name + "'", Project.MSG_VERBOSE); 717 log("File '" + a + "' modified at " + aFile.lastModified(), Project.MSG_VERBOSE); 718 log("File '" + name + "' modified at " + bFile.lastModified(), Project.MSG_VERBOSE); 719 if (fileUtils.isUpToDate(aFile, bFile)) { 720 log("Compiling " + target + " as '" + name + "' is newer than '" + a + "'", Project.MSG_VERBOSE); 721 // Feeling not quite comfortable with touching the file 722 fileUtils.setFileLastModified(aFile, -1); 723 log("Touching file '" + a + "'", Project.MSG_VERBOSE); 724 compile = true; 725 break; 726 } 727 } 728 if (compile) { 729 break; 730 } 731 } 732 } 733 in.close(); 734 } catch (IOException e) { 735 if (f.exists()) { 736 f.delete(); 737 } 738 throw new BuildException("Error reading file '" + f.toString() + "'"); 739 } 740 741 if (f.exists()) { 742 f.delete(); 743 } 744 745 return compile; 746 } 747 splitRightHandSide(String fileNames, String pattern)748 private String[] splitRightHandSide(String fileNames, String pattern) { 749 String[] names = fileNames.split(pattern); 750 for (String name : names) { 751 log("Split right hand side '" + name + "'", Project.MSG_VERBOSE); 752 } 753 return names; 754 } 755 756 /** execute in a forked VM */ run(String[] command, OutputStream out, OutputStream err)757 private int run(String[] command, OutputStream out, OutputStream err) throws IOException { 758 PumpStreamHandler psh; 759 if (err == null) { 760 psh = new PumpStreamHandler(out, bos); 761 } else { 762 psh = new PumpStreamHandler(out, new TeeOutputStream(err, bos)); 763 } 764 765 Execute exe = new Execute(psh, null); 766 767 exe.setAntRun(getProject()); 768 if (workingdir != null) { 769 exe.setWorkingDirectory(workingdir); 770 } 771 772 exe.setCommandline(command); 773 774 return exe.execute(); 775 } 776 } 777