source: release-kits/lirk3/resources/gs3-release-maker/apache-ant-1.6.5/src/main/org/apache/tools/ant/taskdefs/SubAnt.java@ 14982

Last change on this file since 14982 was 14982, checked in by oranfry, 16 years ago

initial import of LiRK3

File size: 18.2 KB
Line 
1/*
2 * Copyright 2003-2004 The Apache Software Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17package org.apache.tools.ant.taskdefs;
18
19import java.io.File;
20import java.io.IOException;
21
22import java.util.Vector;
23import java.util.Enumeration;
24
25import org.apache.tools.ant.Task;
26import org.apache.tools.ant.Project;
27import org.apache.tools.ant.BuildException;
28
29import org.apache.tools.ant.types.Path;
30import org.apache.tools.ant.types.DirSet;
31import org.apache.tools.ant.types.FileSet;
32import org.apache.tools.ant.types.FileList;
33import org.apache.tools.ant.types.PropertySet;
34import org.apache.tools.ant.types.Reference;
35
36
37/**
38 * Calls a given target for all defined sub-builds. This is an extension
39 * of ant for bulk project execution.
40 * <p>
41 * <h2> Use with directories </h2>
42 * <p>
43 * subant can be used with directory sets to execute a build from different directories.
44 * 2 different options are offered
45 * </p>
46 * <ul>
47 * <li>
48 * run the same build file /somepath/otherpath/mybuild.xml
49 * with different base directories use the genericantfile attribute
50 * </li>
51 * <li>if you want to run directory1/build.xml, directory2/build.xml, ....
52 * use the antfile attribute. The base directory does not get set by the subant task in this case,
53 * because you can specify it in each build file.
54 * </li>
55 * </ul>
56 * @since Ant1.6
57 * @ant.task name="subant" category="control"
58 */
59public class SubAnt
60 extends Task {
61
62 private Path buildpath;
63
64 private Ant ant = null;
65 private String target = null;
66 private String antfile = "build.xml";
67 private File genericantfile = null;
68 private boolean verbose = false;
69 private boolean inheritAll = false;
70 private boolean inheritRefs = false;
71 private boolean failOnError = true;
72 private String output = null;
73
74 private Vector properties = new Vector();
75 private Vector references = new Vector();
76 private Vector propertySets = new Vector();
77
78 /**
79 * Pass output sent to System.out to the new project.
80 *
81 * @param output a line of output
82 * @since Ant 1.6.2
83 */
84 public void handleOutput(String output) {
85 if (ant != null) {
86 ant.handleOutput(output);
87 } else {
88 super.handleOutput(output);
89 }
90 }
91
92 /**
93 * Process input into the ant task
94 *
95 * @param buffer the buffer into which data is to be read.
96 * @param offset the offset into the buffer at which data is stored.
97 * @param length the amount of data to read
98 *
99 * @return the number of bytes read
100 *
101 * @exception IOException if the data cannot be read
102 *
103 * @see Task#handleInput(byte[], int, int)
104 *
105 * @since Ant 1.6.2
106 */
107 public int handleInput(byte[] buffer, int offset, int length)
108 throws IOException {
109 if (ant != null) {
110 return ant.handleInput(buffer, offset, length);
111 } else {
112 return super.handleInput(buffer, offset, length);
113 }
114 }
115
116 /**
117 * Pass output sent to System.out to the new project.
118 *
119 * @param output The output to log. Should not be <code>null</code>.
120 *
121 * @since Ant 1.6.2
122 */
123 public void handleFlush(String output) {
124 if (ant != null) {
125 ant.handleFlush(output);
126 } else {
127 super.handleFlush(output);
128 }
129 }
130
131 /**
132 * Pass output sent to System.err to the new project.
133 *
134 * @param output The error output to log. Should not be <code>null</code>.
135 *
136 * @since Ant 1.6.2
137 */
138 public void handleErrorOutput(String output) {
139 if (ant != null) {
140 ant.handleErrorOutput(output);
141 } else {
142 super.handleErrorOutput(output);
143 }
144 }
145
146 /**
147 * Pass output sent to System.err to the new project.
148 *
149 * @param output The error output to log. Should not be <code>null</code>.
150 *
151 * @since Ant 1.6.2
152 */
153 public void handleErrorFlush(String output) {
154 if (ant != null) {
155 ant.handleErrorFlush(output);
156 } else {
157 super.handleErrorFlush(output);
158 }
159 }
160
161 /**
162 * Runs the various sub-builds.
163 */
164 public void execute() {
165 if (buildpath == null) {
166 throw new BuildException("No buildpath specified");
167 }
168
169 final String[] filenames = buildpath.list();
170 final int count = filenames.length;
171 if (count < 1) {
172 log("No sub-builds to iterate on", Project.MSG_WARN);
173 return;
174 }
175/*
176 //REVISIT: there must be cleaner way of doing this, if it is merited at all
177 if (target == null) {
178 target = getOwningTarget().getName();
179 }
180*/
181 BuildException buildException = null;
182 for (int i = 0; i < count; ++i) {
183 File file = null;
184 String subdirPath = null;
185 Throwable thrownException = null;
186 try {
187 File directory = null;
188 file = new File(filenames[i]);
189 if (file.isDirectory()) {
190 if (verbose) {
191 subdirPath = file.getPath();
192 log("Entering directory: " + subdirPath + "\n", Project.MSG_INFO);
193 }
194 if (genericantfile != null) {
195 directory = file;
196 file = genericantfile;
197 } else {
198 file = new File(file, antfile);
199 }
200 }
201 execute(file, directory);
202 if (verbose && subdirPath != null) {
203 log("Leaving directory: " + subdirPath + "\n", Project.MSG_INFO);
204 }
205 } catch (RuntimeException ex) {
206 if (!(getProject().isKeepGoingMode())) {
207 if (verbose && subdirPath != null) {
208 log("Leaving directory: " + subdirPath + "\n", Project.MSG_INFO);
209 }
210 throw ex; // throw further
211 }
212 thrownException = ex;
213 } catch (Throwable ex) {
214 if (!(getProject().isKeepGoingMode())) {
215 if (verbose && subdirPath != null) {
216 log("Leaving directory: " + subdirPath + "\n", Project.MSG_INFO);
217 }
218 throw new BuildException(ex);
219 }
220 thrownException = ex;
221 }
222 if (thrownException != null) {
223 if (thrownException instanceof BuildException) {
224 log("File '" + file
225 + "' failed with message '"
226 + thrownException.getMessage() + "'.", Project.MSG_ERR);
227 // only the first build exception is reported
228 if (buildException == null) {
229 buildException = (BuildException) thrownException;
230 }
231 } else {
232 log("Target '" + file
233 + "' failed with message '"
234 + thrownException.getMessage() + "'.", Project.MSG_ERR);
235 thrownException.printStackTrace(System.err);
236 if (buildException == null) {
237 buildException =
238 new BuildException(thrownException);
239 }
240 }
241 if (verbose && subdirPath != null) {
242 log("Leaving directory: " + subdirPath + "\n", Project.MSG_INFO);
243 }
244 }
245 }
246 // check if one of the builds failed in keep going mode
247 if (buildException != null) {
248 throw buildException;
249 }
250 }
251
252 /**
253 * Runs the given target on the provided build file.
254 *
255 * @param file the build file to execute
256 * @param directory the directory of the current iteration
257 * @throws BuildException is the file cannot be found, read, is
258 * a directory, or the target called failed, but only if
259 * <code>failOnError</code> is <code>true</code>. Otherwise,
260 * a warning log message is simply output.
261 */
262 private void execute(File file, File directory)
263 throws BuildException {
264 if (!file.exists() || file.isDirectory() || !file.canRead()) {
265 String msg = "Invalid file: " + file;
266 if (failOnError) {
267 throw new BuildException(msg);
268 }
269 log(msg, Project.MSG_WARN);
270 return;
271 }
272
273 ant = createAntTask(directory);
274 String antfilename = file.getAbsolutePath();
275 ant.setAntfile(antfilename);
276 try {
277 ant.execute();
278 } catch (BuildException e) {
279 if (failOnError) {
280 throw e;
281 }
282 log("Failure for target '" + target
283 + "' of: " + antfilename + "\n"
284 + e.getMessage(), Project.MSG_WARN);
285 } catch (Throwable e) {
286 if (failOnError) {
287 throw new BuildException(e);
288 }
289 log("Failure for target '" + target
290 + "' of: " + antfilename + "\n"
291 + e.toString(),
292 Project.MSG_WARN);
293 } finally {
294 ant = null;
295 }
296 }
297
298 /**
299 * This method builds the file name to use in conjunction with directories.
300 *
301 * <p>Defaults to "build.xml".
302 * If <code>genericantfile</code> is set, this attribute is ignored.</p>
303 *
304 * @param antfile the short build file name. Defaults to "build.xml".
305 */
306 public void setAntfile(String antfile) {
307 this.antfile = antfile;
308 }
309
310 /**
311 * This method builds a file path to use in conjunction with directories.
312 *
313 * <p>Use <code>genericantfile</code>, in order to run the same build file
314 * with different basedirs.</p>
315 * If this attribute is set, <code>antfile</code> is ignored.
316 *
317 * @param afile (path of the generic ant file, absolute or relative to
318 * project base directory)
319 * */
320 public void setGenericAntfile(File afile) {
321 this.genericantfile = afile;
322 }
323
324 /**
325 * Sets whether to fail with a build exception on error, or go on.
326 *
327 * @param failOnError the new value for this boolean flag.
328 */
329 public void setFailonerror(boolean failOnError) {
330 this.failOnError = failOnError;
331 }
332
333 /**
334 * The target to call on the different sub-builds. Set to "" to execute
335 * the default target.
336 * @param target the target
337 * <p>
338 */
339 // REVISIT: Defaults to the target name that contains this task if not specified.
340 public void setTarget(String target) {
341 this.target = target;
342 }
343
344 /**
345 * Enable/ disable verbose log messages showing when each sub-build path is entered/ exited.
346 * The default value is "false".
347 * @param on true to enable verbose mode, false otherwise (default).
348 */
349 public void setVerbose(boolean on) {
350 this.verbose = on;
351 }
352
353 /**
354 * Corresponds to <code>&lt;ant&gt;</code>'s
355 * <code>output</code> attribute.
356 *
357 * @param s the filename to write the output to.
358 */
359 public void setOutput(String s) {
360 this.output = s;
361 }
362
363 /**
364 * Corresponds to <code>&lt;ant&gt;</code>'s
365 * <code>inheritall</code> attribute.
366 *
367 * @param b the new value for this boolean flag.
368 */
369 public void setInheritall(boolean b) {
370 this.inheritAll = b;
371 }
372
373 /**
374 * Corresponds to <code>&lt;ant&gt;</code>'s
375 * <code>inheritrefs</code> attribute.
376 *
377 * @param b the new value for this boolean flag.
378 */
379 public void setInheritrefs(boolean b) {
380 this.inheritRefs = b;
381 }
382
383 /**
384 * Corresponds to <code>&lt;ant&gt;</code>'s
385 * nested <code>&lt;property&gt;</code> element.
386 *
387 * @param p the property to pass on explicitly to the sub-build.
388 */
389 public void addProperty(Property p) {
390 properties.addElement(p);
391 }
392
393 /**
394 * Corresponds to <code>&lt;ant&gt;</code>'s
395 * nested <code>&lt;reference&gt;</code> element.
396 *
397 * @param r the reference to pass on explicitly to the sub-build.
398 */
399 public void addReference(Ant.Reference r) {
400 references.addElement(r);
401 }
402
403 /**
404 * Corresponds to <code>&lt;ant&gt;</code>'s
405 * nested <code>&lt;propertyset&gt;</code> element.
406 * @param ps the propertset
407 */
408 public void addPropertyset(PropertySet ps) {
409 propertySets.addElement(ps);
410 }
411
412 /**
413 * Adds a directory set to the implicit build path.
414 * <p>
415 * <em>Note that the directories will be added to the build path
416 * in no particular order, so if order is significant, one should
417 * use a file list instead!</em>
418 *
419 * @param set the directory set to add.
420 */
421 public void addDirset(DirSet set) {
422 getBuildpath().addDirset(set);
423 }
424
425 /**
426 * Adds a file set to the implicit build path.
427 * <p>
428 * <em>Note that the directories will be added to the build path
429 * in no particular order, so if order is significant, one should
430 * use a file list instead!</em>
431 *
432 * @param set the file set to add.
433 */
434 public void addFileset(FileSet set) {
435 getBuildpath().addFileset(set);
436 }
437
438 /**
439 * Adds an ordered file list to the implicit build path.
440 * <p>
441 * <em>Note that contrary to file and directory sets, file lists
442 * can reference non-existent files or directories!</em>
443 *
444 * @param list the file list to add.
445 */
446 public void addFilelist(FileList list) {
447 getBuildpath().addFilelist(list);
448 }
449
450 /**
451 * Set the buildpath to be used to find sub-projects.
452 *
453 * @param s an Ant Path object containing the buildpath.
454 */
455 public void setBuildpath(Path s) {
456 getBuildpath().append(s);
457 }
458
459 /**
460 * Creates a nested build path, and add it to the implicit build path.
461 *
462 * @return the newly created nested build path.
463 */
464 public Path createBuildpath() {
465 return getBuildpath().createPath();
466 }
467
468 /**
469 * Creates a nested <code>&lt;buildpathelement&gt;</code>,
470 * and add it to the implicit build path.
471 *
472 * @return the newly created nested build path element.
473 */
474 public Path.PathElement createBuildpathElement() {
475 return getBuildpath().createPathElement();
476 }
477
478 /**
479 * Gets the implicit build path, creating it if <code>null</code>.
480 *
481 * @return the implicit build path.
482 */
483 private Path getBuildpath() {
484 if (buildpath == null) {
485 buildpath = new Path(getProject());
486 }
487 return buildpath;
488 }
489
490 /**
491 * Buildpath to use, by reference.
492 *
493 * @param r a reference to an Ant Path object containing the buildpath.
494 */
495 public void setBuildpathRef(Reference r) {
496 createBuildpath().setRefid(r);
497 }
498
499 /**
500 * Creates the &lt;ant&gt; task configured to run a specific target.
501 *
502 * @param directory : if not null the directory where the build should run
503 *
504 * @return the ant task, configured with the explicit properties and
505 * references necessary to run the sub-build.
506 */
507 private Ant createAntTask(File directory) {
508 Ant ant = (Ant) getProject().createTask("ant");
509 ant.setOwningTarget(getOwningTarget());
510 ant.setTaskName(getTaskName());
511 ant.init();
512 if (target != null && target.length() > 0) {
513 ant.setTarget(target);
514 }
515
516
517 if (output != null) {
518 ant.setOutput(output);
519 }
520
521 if (directory != null) {
522 ant.setDir(directory);
523 }
524
525 ant.setInheritAll(inheritAll);
526 for (Enumeration i = properties.elements(); i.hasMoreElements();) {
527 copyProperty(ant.createProperty(), (Property) i.nextElement());
528 }
529
530 for (Enumeration i = propertySets.elements(); i.hasMoreElements();) {
531 ant.addPropertyset((PropertySet) i.nextElement());
532 }
533
534 ant.setInheritRefs(inheritRefs);
535 for (Enumeration i = references.elements(); i.hasMoreElements();) {
536 ant.addReference((Ant.Reference) i.nextElement());
537 }
538
539 return ant;
540 }
541
542 /**
543 * Assigns an Ant property to another.
544 *
545 * @param to the destination property whose content is modified.
546 * @param from the source property whose content is copied.
547 */
548 private static void copyProperty(Property to, Property from) {
549 to.setName(from.getName());
550
551 if (from.getValue() != null) {
552 to.setValue(from.getValue());
553 }
554 if (from.getFile() != null) {
555 to.setFile(from.getFile());
556 }
557 if (from.getResource() != null) {
558 to.setResource(from.getResource());
559 }
560 if (from.getPrefix() != null) {
561 to.setPrefix(from.getPrefix());
562 }
563 if (from.getRefid() != null) {
564 to.setRefid(from.getRefid());
565 }
566 if (from.getEnvironment() != null) {
567 to.setEnvironment(from.getEnvironment());
568 }
569 if (from.getClasspath() != null) {
570 to.setClasspath(from.getClasspath());
571 }
572 }
573
574} // END class SubAnt
Note: See TracBrowser for help on using the repository browser.