View Javadoc

1   /*
2    $Id: Groovyc.java,v 1.14 2005/06/10 09:55:30 cstein 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.listener.AnsiColorLogger;
57  import org.apache.tools.ant.taskdefs.MatchingTask;
58  import org.apache.tools.ant.types.Path;
59  import org.apache.tools.ant.types.Reference;
60  import org.apache.tools.ant.util.GlobPatternMapper;
61  import org.apache.tools.ant.util.SourceFileScanner;
62  import org.codehaus.groovy.control.CompilationUnit;
63  import org.codehaus.groovy.control.CompilerConfiguration;
64  import org.codehaus.groovy.tools.ErrorReporter;
65  
66  
67  /***
68   * Compiles Groovy source files. This task can take the following
69   * arguments:
70   * <ul>
71   * <li>sourcedir
72   * <li>destdir
73   * <li>classpath
74   * </ul>
75   * Of these arguments, the <b>sourcedir</b> and <b>destdir</b> are required.
76   * <p>
77   * When this task executes, it will recursively scan the sourcedir and
78   * destdir looking for Groovy source files to compile. This task makes its
79   * compile decision based on timestamp.
80   * 
81   * Based heavily on the Javac implementation in Ant
82   *
83   * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
84   * @version $Revision: 1.14 $ 
85   */
86  public class Groovyc extends MatchingTask {
87  
88      private CompilerConfiguration configuration = new CompilerConfiguration();
89      private Path src;
90      private File destDir;
91      private Path compileClasspath;
92      private Path compileSourcepath;
93      private String encoding;
94  
95      protected boolean failOnError = true;
96      protected boolean listFiles = false;
97      protected File[] compileList = new File[0];
98  
99      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 }