View Javadoc

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