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 }