1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46 package org.codehaus.groovy.ant;
47
48 import groovy.lang.GroovyClassLoader;
49
50 import java.io.File;
51 import java.io.PrintWriter;
52 import java.io.StringWriter;
53 import java.nio.charset.Charset;
54 import java.util.Iterator;
55 import java.util.List;
56
57 import org.apache.tools.ant.AntClassLoader;
58 import org.apache.tools.ant.BuildException;
59 import org.apache.tools.ant.DirectoryScanner;
60 import org.apache.tools.ant.Project;
61 import org.apache.tools.ant.listener.AnsiColorLogger;
62 import org.apache.tools.ant.taskdefs.MatchingTask;
63 import org.apache.tools.ant.types.Path;
64 import org.apache.tools.ant.types.Reference;
65 import org.apache.tools.ant.util.GlobPatternMapper;
66 import org.apache.tools.ant.util.SourceFileScanner;
67 import org.codehaus.groovy.control.CompilationUnit;
68 import org.codehaus.groovy.control.CompilerConfiguration;
69 import org.codehaus.groovy.tools.ErrorReporter;
70
71
72 /***
73 * Compiles Groovy source files. This task can take the following
74 * arguments:
75 * <ul>
76 * <li>sourcedir
77 * <li>destdir
78 * <li>classpath
79 * <li>stacktrace
80 * </ul>
81 * Of these arguments, the <b>sourcedir</b> and <b>destdir</b> are required.
82 * <p>
83 * When this task executes, it will recursively scan the sourcedir and
84 * destdir looking for Groovy source files to compile. This task makes its
85 * compile decision based on timestamp.
86 *
87 * Based heavily on the Javac implementation in Ant
88 *
89 * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
90 * @author Hein Meling
91 * @version $Revision: 1.15 $
92 */
93 public class Groovyc extends MatchingTask {
94
95 private CompilerConfiguration configuration = new CompilerConfiguration();
96 private Path src;
97 private File destDir;
98 private Path compileClasspath;
99 private Path compileSourcepath;
100 private String encoding;
101
102 protected boolean failOnError = true;
103 protected boolean listFiles = false;
104 protected File[] compileList = new File[0];
105
106 public static void main(String[] args) {
107 String dest = ".";
108 String src = ".";
109 boolean listFiles = false;
110 if (args.length > 0) {
111 dest = args[0];
112 }
113 if (args.length > 1) {
114 src = args[1];
115 }
116 if (args.length > 2) {
117 String flag = args[2];
118 if (flag.equalsIgnoreCase("true")) {
119 listFiles = true;
120 }
121 }
122
123 Project project = new Project();
124 project.addBuildListener(new AnsiColorLogger());
125
126 Groovyc compiler = new Groovyc();
127 compiler.setProject(project);
128 compiler.setSrcdir(new Path(project, src));
129 compiler.setDestdir(project.resolveFile(dest));
130 compiler.setListfiles(listFiles);
131 compiler.execute();
132 }
133
134 public Groovyc() {
135 }
136
137 /***
138 * Adds a path for source compilation.
139 *
140 * @return a nested src element.
141 */
142 public Path createSrc() {
143 if (src == null) {
144 src = new Path(getProject());
145 }
146 return src.createPath();
147 }
148
149 /***
150 * Recreate src.
151 *
152 * @return a nested src element.
153 */
154 protected Path recreateSrc() {
155 src = null;
156 return createSrc();
157 }
158
159 /***
160 * Set the source directories to find the source Java files.
161 * @param srcDir the source directories as a path
162 */
163 public void setSrcdir(Path srcDir) {
164 if (src == null) {
165 src = srcDir;
166 }
167 else {
168 src.append(srcDir);
169 }
170 }
171
172 /***
173 * Gets the source dirs to find the source java files.
174 * @return the source directorys as a path
175 */
176 public Path getSrcdir() {
177 return src;
178 }
179
180 /***
181 * Set the destination directory into which the Java source
182 * files should be compiled.
183 * @param destDir the destination director
184 */
185 public void setDestdir(File destDir) {
186 this.destDir = destDir;
187 }
188
189 /***
190 * Enable verbose compiling which will display which files
191 * are being compiled
192 * @param verbose
193 */
194 public void setVerbose(boolean verbose) {
195 configuration.setVerbose( verbose );
196 }
197
198 /***
199 * Enable compiler to report stack trace information if a problem occurs
200 * during compilation.
201 * @param stacktrace
202 */
203 public void setStacktrace(boolean stacktrace) {
204 configuration.setDebug(stacktrace);
205 }
206
207 /***
208 * Gets the destination directory into which the java source files
209 * should be compiled.
210 * @return the destination directory
211 */
212 public File getDestdir() {
213 return destDir;
214 }
215
216 /***
217 * Set the sourcepath to be used for this compilation.
218 * @param sourcepath the source path
219 */
220 public void setSourcepath(Path sourcepath) {
221 if (compileSourcepath == null) {
222 compileSourcepath = sourcepath;
223 }
224 else {
225 compileSourcepath.append(sourcepath);
226 }
227 }
228
229 /***
230 * Gets the sourcepath to be used for this compilation.
231 * @return the source path
232 */
233 public Path getSourcepath() {
234 return compileSourcepath;
235 }
236
237 /***
238 * Adds a path to sourcepath.
239 * @return a sourcepath to be configured
240 */
241 public Path createSourcepath() {
242 if (compileSourcepath == null) {
243 compileSourcepath = new Path(getProject());
244 }
245 return compileSourcepath.createPath();
246 }
247
248 /***
249 * Adds a reference to a source path defined elsewhere.
250 * @param r a reference to a source path
251 */
252 public void setSourcepathRef(Reference r) {
253 createSourcepath().setRefid(r);
254 }
255
256 /***
257 * Set the classpath to be used for this compilation.
258 *
259 * @param classpath an Ant Path object containing the compilation classpath.
260 */
261 public void setClasspath(Path classpath) {
262 if (compileClasspath == null) {
263 compileClasspath = classpath;
264 }
265 else {
266 compileClasspath.append(classpath);
267 }
268 }
269
270 /***
271 * Gets the classpath to be used for this compilation.
272 * @return the class path
273 */
274 public Path getClasspath() {
275 return compileClasspath;
276 }
277
278 /***
279 * Adds a path to the classpath.
280 * @return a class path to be configured
281 */
282 public Path createClasspath() {
283 if (compileClasspath == null) {
284 compileClasspath = new Path(getProject());
285 }
286 return compileClasspath.createPath();
287 }
288
289 /***
290 * Adds a reference to a classpath defined elsewhere.
291 * @param r a reference to a classpath
292 */
293 public void setClasspathRef(Reference r) {
294 createClasspath().setRefid(r);
295 }
296
297 public String createEncoding() {
298 if (encoding == null) {
299 encoding = System.getProperty("file.encoding");
300 }
301 return encoding;
302 }
303
304 public void setEncoding(String encoding) {
305 this.encoding = encoding;
306 }
307
308 public String getEncoding() {
309 return encoding;
310 }
311
312 /***
313 * If true, list the source files being handed off to the compiler.
314 * @param list if true list the source files
315 */
316 public void setListfiles(boolean list) {
317 listFiles = list;
318 }
319
320 /***
321 * Get the listfiles flag.
322 * @return the listfiles flag
323 */
324 public boolean getListfiles() {
325 return listFiles;
326 }
327
328 /***
329 * Indicates whether the build will continue
330 * even if there are compilation errors; defaults to true.
331 * @param fail if true halt the build on failure
332 */
333 public void setFailonerror(boolean fail) {
334 failOnError = fail;
335 }
336
337 /***
338 * @ant.attribute ignore="true"
339 * @param proceed inverse of failoferror
340 */
341 public void setProceed(boolean proceed) {
342 failOnError = !proceed;
343 }
344
345 /***
346 * Gets the failonerror flag.
347 * @return the failonerror flag
348 */
349 public boolean getFailonerror() {
350 return failOnError;
351 }
352
353 /***
354 * Executes the task.
355 * @exception BuildException if an error occurs
356 */
357 public void execute() throws BuildException {
358 checkParameters();
359 resetFileLists();
360
361
362
363 String[] list = src.list();
364 for (int i = 0; i < list.length; i++) {
365 File srcDir = getProject().resolveFile(list[i]);
366 if (!srcDir.exists()) {
367 throw new BuildException("srcdir \"" + srcDir.getPath() + "\" does not exist!", getLocation());
368 }
369
370 DirectoryScanner ds = this.getDirectoryScanner(srcDir);
371 String[] files = ds.getIncludedFiles();
372
373 scanDir(srcDir, destDir != null ? destDir : srcDir, files);
374 }
375
376 compile();
377 }
378
379 /***
380 * Clear the list of files to be compiled and copied..
381 */
382 protected void resetFileLists() {
383 compileList = new File[0];
384 }
385
386 /***
387 * Scans the directory looking for source files to be compiled.
388 * The results are returned in the class variable compileList
389 *
390 * @param srcDir The source directory
391 * @param destDir The destination directory
392 * @param files An array of filenames
393 */
394 protected void scanDir(File srcDir, File destDir, String[] files) {
395 GlobPatternMapper m = new GlobPatternMapper();
396 m.setFrom("*.groovy");
397 m.setTo("*.class");
398 SourceFileScanner sfs = new SourceFileScanner(this);
399 File[] newFiles = sfs.restrictAsFiles(files, srcDir, destDir, m);
400
401 if (newFiles.length > 0) {
402 File[] newCompileList = new File[compileList.length + newFiles.length];
403 System.arraycopy(compileList, 0, newCompileList, 0, compileList.length);
404 System.arraycopy(newFiles, 0, newCompileList, compileList.length, newFiles.length);
405 compileList = newCompileList;
406 }
407 }
408
409 /***
410 * Gets the list of files to be compiled.
411 * @return the list of files as an array
412 */
413 public File[] getFileList() {
414 return compileList;
415 }
416
417 protected void checkParameters() throws BuildException {
418 if (src == null) {
419 throw new BuildException("srcdir attribute must be set!", getLocation());
420 }
421 if (src.size() == 0) {
422 throw new BuildException("srcdir attribute must be set!", getLocation());
423 }
424
425 if (destDir != null && !destDir.isDirectory()) {
426 throw new BuildException(
427 "destination directory \"" + destDir + "\" does not exist " + "or is not a directory",
428 getLocation());
429 }
430
431 if (encoding != null && !Charset.isSupported(encoding)) {
432 throw new BuildException("encoding \"\" not supported");
433 }
434 }
435
436 protected void compile() {
437
438 if (compileList.length > 0) {
439 log(
440 "Compiling "
441 + compileList.length
442 + " source file"
443 + (compileList.length == 1 ? "" : "s")
444 + (destDir != null ? " to " + destDir : ""));
445
446 if (listFiles) {
447 for (int i = 0; i < compileList.length; i++) {
448 String filename = compileList[i].getAbsolutePath();
449
450
451
452
453 log(filename);
454
455 }
456 }
457
458 try {
459 Path classpath = getClasspath();
460 if (classpath != null) {
461 configuration.setClasspath(classpath.toString());
462 }
463 configuration.setTargetDirectory(destDir);
464
465 if (encoding != null) {
466 configuration.setSourceEncoding(encoding);
467 }
468
469 CompilationUnit unit = new CompilationUnit(configuration, null, buildClassLoaderFor());
470 unit.addSources(compileList);
471 unit.compile();
472 }
473 catch (Exception e) {
474
475 StringWriter writer = new StringWriter();
476 new ErrorReporter( e, false ).write( new PrintWriter(writer) );
477 String message = writer.toString();
478
479 if (failOnError) {
480 throw new BuildException(message, e, getLocation());
481 }
482 else {
483 log(message, Project.MSG_ERR);
484 }
485
486 }
487 }
488 }
489
490 private GroovyClassLoader buildClassLoaderFor() {
491 ClassLoader parent = this.getClass().getClassLoader();
492 if (parent instanceof AntClassLoader) {
493 AntClassLoader antLoader = (AntClassLoader) parent;
494 String[] pathElm = antLoader.getClasspath().split(File.pathSeparator);
495 List classpath = configuration.getClasspath();
496
497
498
499
500
501 for (Iterator iter = classpath.iterator(); iter.hasNext();) {
502 String cpEntry = (String) iter.next();
503 boolean found = false;
504 for (int i = 0; i < pathElm.length; i++) {
505 if (cpEntry.equals(pathElm[i])) {
506 found = true;
507 break;
508 }
509 }
510 if (!found)
511 antLoader.addPathElement(cpEntry);
512 }
513 }
514 return new GroovyClassLoader(parent, configuration);
515 }
516
517 }