001 /* 002 $Id: Groovyc.java,v 1.14 2005/06/10 09:55:30 cstein Exp $ 003 004 Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved. 005 006 Redistribution and use of this software and associated documentation 007 ("Software"), with or without modification, are permitted provided 008 that the following conditions are met: 009 010 1. Redistributions of source code must retain copyright 011 statements and notices. Redistributions must also contain a 012 copy of this document. 013 014 2. Redistributions in binary form must reproduce the 015 above copyright notice, this list of conditions and the 016 following disclaimer in the documentation and/or other 017 materials provided with the distribution. 018 019 3. The name "groovy" must not be used to endorse or promote 020 products derived from this Software without prior written 021 permission of The Codehaus. For written permission, 022 please contact info@codehaus.org. 023 024 4. Products derived from this Software may not be called "groovy" 025 nor may "groovy" appear in their names without prior written 026 permission of The Codehaus. "groovy" is a registered 027 trademark of The Codehaus. 028 029 5. Due credit should be given to The Codehaus - 030 http://groovy.codehaus.org/ 031 032 THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS 033 ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT 034 NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 035 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 036 THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 037 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 038 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 039 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 040 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 041 STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 042 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 043 OF THE POSSIBILITY OF SUCH DAMAGE. 044 045 */ 046 package org.codehaus.groovy.ant; 047 048 import java.io.File; 049 import java.io.PrintWriter; 050 import java.io.StringWriter; 051 import java.nio.charset.Charset; 052 053 import org.apache.tools.ant.BuildException; 054 import org.apache.tools.ant.DirectoryScanner; 055 import org.apache.tools.ant.Project; 056 import org.apache.tools.ant.listener.AnsiColorLogger; 057 import org.apache.tools.ant.taskdefs.MatchingTask; 058 import org.apache.tools.ant.types.Path; 059 import org.apache.tools.ant.types.Reference; 060 import org.apache.tools.ant.util.GlobPatternMapper; 061 import org.apache.tools.ant.util.SourceFileScanner; 062 import org.codehaus.groovy.control.CompilationUnit; 063 import org.codehaus.groovy.control.CompilerConfiguration; 064 import org.codehaus.groovy.tools.ErrorReporter; 065 066 067 /** 068 * Compiles Groovy source files. This task can take the following 069 * arguments: 070 * <ul> 071 * <li>sourcedir 072 * <li>destdir 073 * <li>classpath 074 * </ul> 075 * Of these arguments, the <b>sourcedir</b> and <b>destdir</b> are required. 076 * <p> 077 * When this task executes, it will recursively scan the sourcedir and 078 * destdir looking for Groovy source files to compile. This task makes its 079 * compile decision based on timestamp. 080 * 081 * Based heavily on the Javac implementation in Ant 082 * 083 * @author <a href="mailto:james@coredevelopers.net">James Strachan</a> 084 * @version $Revision: 1.14 $ 085 */ 086 public class Groovyc extends MatchingTask { 087 088 private CompilerConfiguration configuration = new CompilerConfiguration(); 089 private Path src; 090 private File destDir; 091 private Path compileClasspath; 092 private Path compileSourcepath; 093 private String encoding; 094 095 protected boolean failOnError = true; 096 protected boolean listFiles = false; 097 protected File[] compileList = new File[0]; 098 099 public static void main(String[] args) { 100 String dest = "."; 101 String src = "."; 102 boolean listFiles = false; 103 if (args.length > 0) { 104 dest = args[0]; 105 } 106 if (args.length > 1) { 107 src = args[1]; 108 } 109 if (args.length > 2) { 110 String flag = args[2]; 111 if (flag.equalsIgnoreCase("true")) { 112 listFiles = true; 113 } 114 } 115 116 Project project = new Project(); 117 project.addBuildListener(new AnsiColorLogger()); 118 119 Groovyc compiler = new Groovyc(); 120 compiler.setProject(project); 121 compiler.setSrcdir(new Path(project, src)); 122 compiler.setDestdir(project.resolveFile(dest)); 123 compiler.setListfiles(listFiles); 124 compiler.execute(); 125 } 126 127 public Groovyc() { 128 } 129 130 /** 131 * Adds a path for source compilation. 132 * 133 * @return a nested src element. 134 */ 135 public Path createSrc() { 136 if (src == null) { 137 src = new Path(getProject()); 138 } 139 return src.createPath(); 140 } 141 142 /** 143 * Recreate src. 144 * 145 * @return a nested src element. 146 */ 147 protected Path recreateSrc() { 148 src = null; 149 return createSrc(); 150 } 151 152 /** 153 * Set the source directories to find the source Java files. 154 * @param srcDir the source directories as a path 155 */ 156 public void setSrcdir(Path srcDir) { 157 if (src == null) { 158 src = srcDir; 159 } 160 else { 161 src.append(srcDir); 162 } 163 } 164 165 /** 166 * Gets the source dirs to find the source java files. 167 * @return the source directorys as a path 168 */ 169 public Path getSrcdir() { 170 return src; 171 } 172 173 /** 174 * Set the destination directory into which the Java source 175 * files should be compiled. 176 * @param destDir the destination director 177 */ 178 public void setDestdir(File destDir) { 179 this.destDir = destDir; 180 } 181 182 /** 183 * Enable verbose compiling which will display which files 184 * are being compiled 185 * @param verbose 186 */ 187 public void setVerbose(boolean verbose) { 188 configuration.setVerbose( verbose ); 189 } 190 191 /** 192 * Gets the destination directory into which the java source files 193 * should be compiled. 194 * @return the destination directory 195 */ 196 public File getDestdir() { 197 return destDir; 198 } 199 200 /** 201 * Set the sourcepath to be used for this compilation. 202 * @param sourcepath the source path 203 */ 204 public void setSourcepath(Path sourcepath) { 205 if (compileSourcepath == null) { 206 compileSourcepath = sourcepath; 207 } 208 else { 209 compileSourcepath.append(sourcepath); 210 } 211 } 212 213 /** 214 * Gets the sourcepath to be used for this compilation. 215 * @return the source path 216 */ 217 public Path getSourcepath() { 218 return compileSourcepath; 219 } 220 221 /** 222 * Adds a path to sourcepath. 223 * @return a sourcepath to be configured 224 */ 225 public Path createSourcepath() { 226 if (compileSourcepath == null) { 227 compileSourcepath = new Path(getProject()); 228 } 229 return compileSourcepath.createPath(); 230 } 231 232 /** 233 * Adds a reference to a source path defined elsewhere. 234 * @param r a reference to a source path 235 */ 236 public void setSourcepathRef(Reference r) { 237 createSourcepath().setRefid(r); 238 } 239 240 /** 241 * Set the classpath to be used for this compilation. 242 * 243 * @param classpath an Ant Path object containing the compilation classpath. 244 */ 245 public void setClasspath(Path classpath) { 246 if (compileClasspath == null) { 247 compileClasspath = classpath; 248 } 249 else { 250 compileClasspath.append(classpath); 251 } 252 } 253 254 /** 255 * Gets the classpath to be used for this compilation. 256 * @return the class path 257 */ 258 public Path getClasspath() { 259 return compileClasspath; 260 } 261 262 /** 263 * Adds a path to the classpath. 264 * @return a class path to be configured 265 */ 266 public Path createClasspath() { 267 if (compileClasspath == null) { 268 compileClasspath = new Path(getProject()); 269 } 270 return compileClasspath.createPath(); 271 } 272 273 /** 274 * Adds a reference to a classpath defined elsewhere. 275 * @param r a reference to a classpath 276 */ 277 public void setClasspathRef(Reference r) { 278 createClasspath().setRefid(r); 279 } 280 281 public String createEncoding() { 282 if (encoding == null) { 283 encoding = System.getProperty("file.encoding"); 284 } 285 return encoding; 286 } 287 288 public void setEncoding(String encoding) { 289 this.encoding = encoding; 290 } 291 292 public String getEncoding() { 293 return encoding; 294 } 295 296 /** 297 * If true, list the source files being handed off to the compiler. 298 * @param list if true list the source files 299 */ 300 public void setListfiles(boolean list) { 301 listFiles = list; 302 } 303 304 /** 305 * Get the listfiles flag. 306 * @return the listfiles flag 307 */ 308 public boolean getListfiles() { 309 return listFiles; 310 } 311 312 /** 313 * Indicates whether the build will continue 314 * even if there are compilation errors; defaults to true. 315 * @param fail if true halt the build on failure 316 */ 317 public void setFailonerror(boolean fail) { 318 failOnError = fail; 319 } 320 321 /** 322 * @ant.attribute ignore="true" 323 * @param proceed inverse of failoferror 324 */ 325 public void setProceed(boolean proceed) { 326 failOnError = !proceed; 327 } 328 329 /** 330 * Gets the failonerror flag. 331 * @return the failonerror flag 332 */ 333 public boolean getFailonerror() { 334 return failOnError; 335 } 336 337 /** 338 * Executes the task. 339 * @exception BuildException if an error occurs 340 */ 341 public void execute() throws BuildException { 342 checkParameters(); 343 resetFileLists(); 344 345 // scan source directories and dest directory to build up 346 // compile lists 347 String[] list = src.list(); 348 for (int i = 0; i < list.length; i++) { 349 File srcDir = getProject().resolveFile(list[i]); 350 if (!srcDir.exists()) { 351 throw new BuildException("srcdir \"" + srcDir.getPath() + "\" does not exist!", getLocation()); 352 } 353 354 DirectoryScanner ds = this.getDirectoryScanner(srcDir); 355 String[] files = ds.getIncludedFiles(); 356 357 scanDir(srcDir, destDir != null ? destDir : srcDir, files); 358 } 359 360 compile(); 361 } 362 363 /** 364 * Clear the list of files to be compiled and copied.. 365 */ 366 protected void resetFileLists() { 367 compileList = new File[0]; 368 } 369 370 /** 371 * Scans the directory looking for source files to be compiled. 372 * The results are returned in the class variable compileList 373 * 374 * @param srcDir The source directory 375 * @param destDir The destination directory 376 * @param files An array of filenames 377 */ 378 protected void scanDir(File srcDir, File destDir, String[] files) { 379 GlobPatternMapper m = new GlobPatternMapper(); 380 m.setFrom("*.groovy"); 381 m.setTo("*.class"); 382 SourceFileScanner sfs = new SourceFileScanner(this); 383 File[] newFiles = sfs.restrictAsFiles(files, srcDir, destDir, m); 384 385 if (newFiles.length > 0) { 386 File[] newCompileList = new File[compileList.length + newFiles.length]; 387 System.arraycopy(compileList, 0, newCompileList, 0, compileList.length); 388 System.arraycopy(newFiles, 0, newCompileList, compileList.length, newFiles.length); 389 compileList = newCompileList; 390 } 391 } 392 393 /** 394 * Gets the list of files to be compiled. 395 * @return the list of files as an array 396 */ 397 public File[] getFileList() { 398 return compileList; 399 } 400 401 protected void checkParameters() throws BuildException { 402 if (src == null) { 403 throw new BuildException("srcdir attribute must be set!", getLocation()); 404 } 405 if (src.size() == 0) { 406 throw new BuildException("srcdir attribute must be set!", getLocation()); 407 } 408 409 if (destDir != null && !destDir.isDirectory()) { 410 throw new BuildException( 411 "destination directory \"" + destDir + "\" does not exist " + "or is not a directory", 412 getLocation()); 413 } 414 415 if (encoding != null && !Charset.isSupported(encoding)) { 416 throw new BuildException("encoding \"\" not supported"); 417 } 418 } 419 420 protected void compile() { 421 422 if (compileList.length > 0) { 423 log( 424 "Compiling " 425 + compileList.length 426 + " source file" 427 + (compileList.length == 1 ? "" : "s") 428 + (destDir != null ? " to " + destDir : "")); 429 430 if (listFiles) { 431 for (int i = 0; i < compileList.length; i++) { 432 String filename = compileList[i].getAbsolutePath(); 433 434 // TODO this logging does not seem to appear in the maven build?? 435 log(filename); 436 System.out.println("compiling: " + filename); 437 } 438 } 439 440 try { 441 Path classpath = getClasspath(); 442 if (classpath != null) { 443 configuration.setClasspath(classpath.toString()); 444 } 445 configuration.setTargetDirectory(destDir); 446 447 if (encoding != null) { 448 configuration.setSourceEncoding(encoding); 449 } 450 451 CompilationUnit unit = new CompilationUnit( configuration ); 452 unit.addSources( compileList ); 453 unit.compile( ); 454 } 455 catch (Exception e) { 456 457 StringWriter writer = new StringWriter(); 458 new ErrorReporter( e, false ).write( new PrintWriter(writer) ); 459 String message = writer.toString(); 460 461 if (failOnError) { 462 throw new BuildException(message, e, getLocation()); 463 } 464 else { 465 log(message, Project.MSG_ERR); 466 } 467 468 } 469 } 470 } 471 }