KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > tools > ant > taskdefs > SubAnt


1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */

18 package org.apache.tools.ant.taskdefs;
19
20 import java.io.File JavaDoc;
21 import java.io.IOException JavaDoc;
22
23 import java.util.Vector JavaDoc;
24 import java.util.Enumeration JavaDoc;
25
26 import org.apache.tools.ant.Task;
27 import org.apache.tools.ant.Project;
28 import org.apache.tools.ant.BuildException;
29
30 import org.apache.tools.ant.types.Path;
31 import org.apache.tools.ant.types.DirSet;
32 import org.apache.tools.ant.types.FileSet;
33 import org.apache.tools.ant.types.FileList;
34 import org.apache.tools.ant.types.PropertySet;
35 import org.apache.tools.ant.types.Reference;
36 import org.apache.tools.ant.types.ResourceCollection;
37
38 import org.apache.tools.ant.taskdefs.Ant.TargetElement;
39
40
41 /**
42  * Calls a given target for all defined sub-builds. This is an extension
43  * of ant for bulk project execution.
44  * <p>
45  * <h2> Use with directories </h2>
46  * <p>
47  * subant can be used with directory sets to execute a build from different directories.
48  * 2 different options are offered
49  * </p>
50  * <ul>
51  * <li>
52  * run the same build file /somepath/otherpath/mybuild.xml
53  * with different base directories use the genericantfile attribute
54  * </li>
55  * <li>if you want to run directory1/build.xml, directory2/build.xml, ....
56  * use the antfile attribute. The base directory does not get set by the subant task in this case,
57  * because you can specify it in each build file.
58  * </li>
59  * </ul>
60  * @since Ant1.6
61  * @ant.task name="subant" category="control"
62  */

63 public class SubAnt
64              extends Task {
65
66     private Path buildpath;
67
68     private Ant ant = null;
69     private String JavaDoc subTarget = null;
70     private String JavaDoc antfile = "build.xml";
71     private File JavaDoc genericantfile = null;
72     private boolean verbose = false;
73     private boolean inheritAll = false;
74     private boolean inheritRefs = false;
75     private boolean failOnError = true;
76     private String JavaDoc output = null;
77
78     private Vector JavaDoc properties = new Vector JavaDoc();
79     private Vector JavaDoc references = new Vector JavaDoc();
80     private Vector JavaDoc propertySets = new Vector JavaDoc();
81
82     /** the targets to call on the new project */
83     private Vector JavaDoc/*<TargetElement>*/ targets = new Vector JavaDoc();
84
85     /**
86      * Pass output sent to System.out to the new project.
87      *
88      * @param output a line of output
89      * @since Ant 1.6.2
90      */

91     public void handleOutput(String JavaDoc output) {
92         if (ant != null) {
93             ant.handleOutput(output);
94         } else {
95             super.handleOutput(output);
96         }
97     }
98
99     /**
100      * Process input into the ant task
101      *
102      * @param buffer the buffer into which data is to be read.
103      * @param offset the offset into the buffer at which data is stored.
104      * @param length the amount of data to read
105      *
106      * @return the number of bytes read
107      *
108      * @exception IOException if the data cannot be read
109      *
110      * @see Task#handleInput(byte[], int, int)
111      *
112      * @since Ant 1.6.2
113      */

114     public int handleInput(byte[] buffer, int offset, int length)
115         throws IOException JavaDoc {
116         if (ant != null) {
117             return ant.handleInput(buffer, offset, length);
118         } else {
119             return super.handleInput(buffer, offset, length);
120         }
121     }
122
123     /**
124      * Pass output sent to System.out to the new project.
125      *
126      * @param output The output to log. Should not be <code>null</code>.
127      *
128      * @since Ant 1.6.2
129      */

130     public void handleFlush(String JavaDoc output) {
131         if (ant != null) {
132             ant.handleFlush(output);
133         } else {
134             super.handleFlush(output);
135         }
136     }
137
138     /**
139      * Pass output sent to System.err to the new project.
140      *
141      * @param output The error output to log. Should not be <code>null</code>.
142      *
143      * @since Ant 1.6.2
144      */

145     public void handleErrorOutput(String JavaDoc output) {
146         if (ant != null) {
147             ant.handleErrorOutput(output);
148         } else {
149             super.handleErrorOutput(output);
150         }
151     }
152
153     /**
154      * Pass output sent to System.err to the new project.
155      *
156      * @param output The error output to log. Should not be <code>null</code>.
157      *
158      * @since Ant 1.6.2
159      */

160     public void handleErrorFlush(String JavaDoc output) {
161         if (ant != null) {
162             ant.handleErrorFlush(output);
163         } else {
164             super.handleErrorFlush(output);
165         }
166     }
167
168     /**
169      * Runs the various sub-builds.
170      */

171     public void execute() {
172         if (buildpath == null) {
173             throw new BuildException("No buildpath specified");
174         }
175
176         final String JavaDoc[] filenames = buildpath.list();
177         final int count = filenames.length;
178         if (count < 1) {
179             log("No sub-builds to iterate on", Project.MSG_WARN);
180             return;
181         }
182 /*
183     //REVISIT: there must be cleaner way of doing this, if it is merited at all
184         if (subTarget == null) {
185             subTarget = getOwningTarget().getName();
186         }
187 */

188         BuildException buildException = null;
189         for (int i = 0; i < count; ++i) {
190             File JavaDoc file = null;
191             String JavaDoc subdirPath = null;
192             Throwable JavaDoc thrownException = null;
193             try {
194                 File JavaDoc directory = null;
195                 file = new File JavaDoc(filenames[i]);
196                 if (file.isDirectory()) {
197                     if (verbose) {
198                         subdirPath = file.getPath();
199                         log("Entering directory: " + subdirPath + "\n", Project.MSG_INFO);
200                     }
201                     if (genericantfile != null) {
202                         directory = file;
203                         file = genericantfile;
204                     } else {
205                         file = new File JavaDoc(file, antfile);
206                     }
207                 }
208                 execute(file, directory);
209                 if (verbose && subdirPath != null) {
210                     log("Leaving directory: " + subdirPath + "\n", Project.MSG_INFO);
211                 }
212             } catch (RuntimeException JavaDoc ex) {
213                 if (!(getProject().isKeepGoingMode())) {
214                     if (verbose && subdirPath != null) {
215                         log("Leaving directory: " + subdirPath + "\n", Project.MSG_INFO);
216                     }
217                     throw ex; // throw further
218
}
219                 thrownException = ex;
220             } catch (Throwable JavaDoc ex) {
221                 if (!(getProject().isKeepGoingMode())) {
222                     if (verbose && subdirPath != null) {
223                         log("Leaving directory: " + subdirPath + "\n", Project.MSG_INFO);
224                     }
225                     throw new BuildException(ex);
226                 }
227                 thrownException = ex;
228             }
229             if (thrownException != null) {
230                 if (thrownException instanceof BuildException) {
231                     log("File '" + file
232                         + "' failed with message '"
233                         + thrownException.getMessage() + "'.", Project.MSG_ERR);
234                     // only the first build exception is reported
235
if (buildException == null) {
236                         buildException = (BuildException) thrownException;
237                     }
238                 } else {
239                     log("Target '" + file
240                         + "' failed with message '"
241                         + thrownException.getMessage() + "'.", Project.MSG_ERR);
242                     thrownException.printStackTrace(System.err);
243                     if (buildException == null) {
244                         buildException =
245                             new BuildException(thrownException);
246                     }
247                 }
248                 if (verbose && subdirPath != null) {
249                     log("Leaving directory: " + subdirPath + "\n", Project.MSG_INFO);
250                 }
251             }
252         }
253         // check if one of the builds failed in keep going mode
254
if (buildException != null) {
255             throw buildException;
256         }
257     }
258
259     /**
260      * Runs the given target on the provided build file.
261      *
262      * @param file the build file to execute
263      * @param directory the directory of the current iteration
264      * @throws BuildException is the file cannot be found, read, is
265      * a directory, or the target called failed, but only if
266      * <code>failOnError</code> is <code>true</code>. Otherwise,
267      * a warning log message is simply output.
268      */

269     private void execute(File JavaDoc file, File JavaDoc directory)
270                 throws BuildException {
271         if (!file.exists() || file.isDirectory() || !file.canRead()) {
272             String JavaDoc msg = "Invalid file: " + file;
273             if (failOnError) {
274                 throw new BuildException(msg);
275             }
276             log(msg, Project.MSG_WARN);
277             return;
278         }
279
280         ant = createAntTask(directory);
281         String JavaDoc antfilename = file.getAbsolutePath();
282         ant.setAntfile(antfilename);
283         for (int i = 0; i < targets.size(); i++) {
284             TargetElement targetElement = (TargetElement) targets.get(i);
285             ant.addConfiguredTarget(targetElement);
286         }
287
288         try {
289             ant.execute();
290         } catch (BuildException e) {
291             if (failOnError) {
292                 throw e;
293             }
294             log("Failure for target '" + subTarget
295                + "' of: " + antfilename + "\n"
296                + e.getMessage(), Project.MSG_WARN);
297         } catch (Throwable JavaDoc e) {
298             if (failOnError) {
299                 throw new BuildException(e);
300             }
301             log("Failure for target '" + subTarget
302                 + "' of: " + antfilename + "\n"
303                 + e.toString(),
304                 Project.MSG_WARN);
305         } finally {
306             ant = null;
307         }
308     }
309
310     /**
311      * This method builds the file name to use in conjunction with directories.
312      *
313      * <p>Defaults to "build.xml".
314      * If <code>genericantfile</code> is set, this attribute is ignored.</p>
315      *
316      * @param antfile the short build file name. Defaults to "build.xml".
317      */

318     public void setAntfile(String JavaDoc antfile) {
319         this.antfile = antfile;
320     }
321
322     /**
323      * This method builds a file path to use in conjunction with directories.
324      *
325      * <p>Use <code>genericantfile</code>, in order to run the same build file
326      * with different basedirs.</p>
327      * If this attribute is set, <code>antfile</code> is ignored.
328      *
329      * @param afile (path of the generic ant file, absolute or relative to
330      * project base directory)
331      * */

332     public void setGenericAntfile(File JavaDoc afile) {
333         this.genericantfile = afile;
334     }
335
336     /**
337      * Sets whether to fail with a build exception on error, or go on.
338      *
339      * @param failOnError the new value for this boolean flag.
340      */

341     public void setFailonerror(boolean failOnError) {
342         this.failOnError = failOnError;
343     }
344
345     /**
346      * The target to call on the different sub-builds. Set to "" to execute
347      * the default target.
348      * @param target the target
349      * <p>
350      */

351     // REVISIT: Defaults to the target name that contains this task if not specified.
352
public void setTarget(String JavaDoc target) {
353         this.subTarget = target;
354     }
355
356     /**
357      * Add a target to this Ant invocation.
358      * @param t the <code>TargetElement</code> to add.
359      * @since Ant 1.7
360      */

361     public void addConfiguredTarget(TargetElement t) {
362         String JavaDoc name = t.getName();
363         if ("".equals(name)) {
364             throw new BuildException("target name must not be empty");
365         }
366         targets.add(t);
367     }
368
369     /**
370      * Enable/ disable verbose log messages showing when each sub-build path is entered/ exited.
371      * The default value is "false".
372      * @param on true to enable verbose mode, false otherwise (default).
373      */

374     public void setVerbose(boolean on) {
375         this.verbose = on;
376     }
377
378     /**
379      * Corresponds to <code>&lt;ant&gt;</code>'s
380      * <code>output</code> attribute.
381      *
382      * @param s the filename to write the output to.
383      */

384     public void setOutput(String JavaDoc s) {
385         this.output = s;
386     }
387
388     /**
389      * Corresponds to <code>&lt;ant&gt;</code>'s
390      * <code>inheritall</code> attribute.
391      *
392      * @param b the new value for this boolean flag.
393      */

394     public void setInheritall(boolean b) {
395         this.inheritAll = b;
396     }
397
398     /**
399      * Corresponds to <code>&lt;ant&gt;</code>'s
400      * <code>inheritrefs</code> attribute.
401      *
402      * @param b the new value for this boolean flag.
403      */

404     public void setInheritrefs(boolean b) {
405         this.inheritRefs = b;
406     }
407
408     /**
409      * Corresponds to <code>&lt;ant&gt;</code>'s
410      * nested <code>&lt;property&gt;</code> element.
411      *
412      * @param p the property to pass on explicitly to the sub-build.
413      */

414     public void addProperty(Property p) {
415         properties.addElement(p);
416     }
417
418     /**
419      * Corresponds to <code>&lt;ant&gt;</code>'s
420      * nested <code>&lt;reference&gt;</code> element.
421      *
422      * @param r the reference to pass on explicitly to the sub-build.
423      */

424     public void addReference(Ant.Reference r) {
425         references.addElement(r);
426     }
427
428     /**
429      * Corresponds to <code>&lt;ant&gt;</code>'s
430      * nested <code>&lt;propertyset&gt;</code> element.
431      * @param ps the propertset
432      */

433     public void addPropertyset(PropertySet ps) {
434         propertySets.addElement(ps);
435     }
436
437     /**
438      * Adds a directory set to the implicit build path.
439      * <p>
440      * <em>Note that the directories will be added to the build path
441      * in no particular order, so if order is significant, one should
442      * use a file list instead!</em>
443      *
444      * @param set the directory set to add.
445      */

446     public void addDirset(DirSet set) {
447         add(set);
448     }
449
450     /**
451      * Adds a file set to the implicit build path.
452      * <p>
453      * <em>Note that the directories will be added to the build path
454      * in no particular order, so if order is significant, one should
455      * use a file list instead!</em>
456      *
457      * @param set the file set to add.
458      */

459     public void addFileset(FileSet set) {
460         add(set);
461     }
462
463     /**
464      * Adds an ordered file list to the implicit build path.
465      * <p>
466      * <em>Note that contrary to file and directory sets, file lists
467      * can reference non-existent files or directories!</em>
468      *
469      * @param list the file list to add.
470      */

471     public void addFilelist(FileList list) {
472         add(list);
473     }
474
475     /**
476      * Adds a resource collection to the implicit build path.
477      *
478      * @param rc the resource collection to add.
479      * @since Ant 1.7
480      */

481     public void add(ResourceCollection rc) {
482         getBuildpath().add(rc);
483     }
484
485     /**
486      * Set the buildpath to be used to find sub-projects.
487      *
488      * @param s an Ant Path object containing the buildpath.
489      */

490     public void setBuildpath(Path s) {
491         getBuildpath().append(s);
492     }
493
494     /**
495      * Creates a nested build path, and add it to the implicit build path.
496      *
497      * @return the newly created nested build path.
498      */

499     public Path createBuildpath() {
500         return getBuildpath().createPath();
501     }
502
503     /**
504      * Creates a nested <code>&lt;buildpathelement&gt;</code>,
505      * and add it to the implicit build path.
506      *
507      * @return the newly created nested build path element.
508      */

509     public Path.PathElement createBuildpathElement() {
510         return getBuildpath().createPathElement();
511     }
512
513     /**
514      * Gets the implicit build path, creating it if <code>null</code>.
515      *
516      * @return the implicit build path.
517      */

518     private Path getBuildpath() {
519         if (buildpath == null) {
520             buildpath = new Path(getProject());
521         }
522         return buildpath;
523     }
524
525     /**
526      * Buildpath to use, by reference.
527      *
528      * @param r a reference to an Ant Path object containing the buildpath.
529      */

530     public void setBuildpathRef(Reference r) {
531         createBuildpath().setRefid(r);
532     }
533
534     /**
535      * Creates the &lt;ant&gt; task configured to run a specific target.
536      *
537      * @param directory : if not null the directory where the build should run
538      *
539      * @return the ant task, configured with the explicit properties and
540      * references necessary to run the sub-build.
541      */

542     private Ant createAntTask(File JavaDoc directory) {
543         Ant antTask = new Ant(this);
544         antTask.init();
545         if (subTarget != null && subTarget.length() > 0) {
546             antTask.setTarget(subTarget);
547         }
548
549
550         if (output != null) {
551             antTask.setOutput(output);
552         }
553
554         if (directory != null) {
555             antTask.setDir(directory);
556         }
557
558         antTask.setInheritAll(inheritAll);
559         for (Enumeration JavaDoc i = properties.elements(); i.hasMoreElements();) {
560             copyProperty(antTask.createProperty(), (Property) i.nextElement());
561         }
562
563         for (Enumeration JavaDoc i = propertySets.elements(); i.hasMoreElements();) {
564             antTask.addPropertyset((PropertySet) i.nextElement());
565         }
566
567         antTask.setInheritRefs(inheritRefs);
568         for (Enumeration JavaDoc i = references.elements(); i.hasMoreElements();) {
569             antTask.addReference((Ant.Reference) i.nextElement());
570         }
571
572         return antTask;
573     }
574
575     /**
576      * Assigns an Ant property to another.
577      *
578      * @param to the destination property whose content is modified.
579      * @param from the source property whose content is copied.
580      */

581     private static void copyProperty(Property to, Property from) {
582         to.setName(from.getName());
583
584         if (from.getValue() != null) {
585             to.setValue(from.getValue());
586         }
587         if (from.getFile() != null) {
588             to.setFile(from.getFile());
589         }
590         if (from.getResource() != null) {
591             to.setResource(from.getResource());
592         }
593         if (from.getPrefix() != null) {
594             to.setPrefix(from.getPrefix());
595         }
596         if (from.getRefid() != null) {
597             to.setRefid(from.getRefid());
598         }
599         if (from.getEnvironment() != null) {
600             to.setEnvironment(from.getEnvironment());
601         }
602         if (from.getClasspath() != null) {
603             to.setClasspath(from.getClasspath());
604         }
605     }
606
607 } // END class SubAnt
608
Popular Tags