All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.tools.ant.taskdefs.SubAnt Maven / Gradle / Ivy

The newest version!
/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */
package org.apache.tools.ant.taskdefs;

import java.io.File;
import java.io.IOException;

import java.util.Vector;
import java.util.Enumeration;

import org.apache.tools.ant.Main;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.BuildException;

import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.DirSet;
import org.apache.tools.ant.types.FileSet;
import org.apache.tools.ant.types.FileList;
import org.apache.tools.ant.types.PropertySet;
import org.apache.tools.ant.types.Reference;
import org.apache.tools.ant.types.ResourceCollection;

import org.apache.tools.ant.taskdefs.Ant.TargetElement;


/**
 * Calls a given target for all defined sub-builds. This is an extension
 * of ant for bulk project execution.
 * 

*

Use with directories

*

* subant can be used with directory sets to execute a build from different directories. * 2 different options are offered *

*
    *
  • * run the same build file /somepath/otherpath/mybuild.xml * with different base directories use the genericantfile attribute *
  • *
  • if you want to run directory1/build.xml, directory2/build.xml, .... * use the antfile attribute. The base directory does not get set by the subant task in this case, * because you can specify it in each build file. *
  • *
* @since Ant1.6 * @ant.task name="subant" category="control" */ public class SubAnt extends Task { private Path buildpath; private Ant ant = null; private String subTarget = null; private String antfile = getDefaultBuildFile(); private File genericantfile = null; private boolean verbose = false; private boolean inheritAll = false; private boolean inheritRefs = false; private boolean failOnError = true; private String output = null; private Vector properties = new Vector(); private Vector references = new Vector(); private Vector propertySets = new Vector(); /** the targets to call on the new project */ private Vector/**/ targets = new Vector(); /** * Get the default build file name to use when launching the task. *

* This function may be overrided by providers of custom ProjectHelper so they can implement easily their sub * launcher. * * @return the name of the default file * @since Ant 1.8.0 */ protected String getDefaultBuildFile() { return Main.DEFAULT_BUILD_FILENAME; } /** * Pass output sent to System.out to the new project. * * @param output a line of output * @since Ant 1.6.2 */ public void handleOutput(String output) { if (ant != null) { ant.handleOutput(output); } else { super.handleOutput(output); } } /** * Process input into the ant task * * @param buffer the buffer into which data is to be read. * @param offset the offset into the buffer at which data is stored. * @param length the amount of data to read * * @return the number of bytes read * * @exception IOException if the data cannot be read * * @see Task#handleInput(byte[], int, int) * * @since Ant 1.6.2 */ public int handleInput(byte[] buffer, int offset, int length) throws IOException { if (ant != null) { return ant.handleInput(buffer, offset, length); } else { return super.handleInput(buffer, offset, length); } } /** * Pass output sent to System.out to the new project. * * @param output The output to log. Should not be null. * * @since Ant 1.6.2 */ public void handleFlush(String output) { if (ant != null) { ant.handleFlush(output); } else { super.handleFlush(output); } } /** * Pass output sent to System.err to the new project. * * @param output The error output to log. Should not be null. * * @since Ant 1.6.2 */ public void handleErrorOutput(String output) { if (ant != null) { ant.handleErrorOutput(output); } else { super.handleErrorOutput(output); } } /** * Pass output sent to System.err to the new project. * * @param output The error output to log. Should not be null. * * @since Ant 1.6.2 */ public void handleErrorFlush(String output) { if (ant != null) { ant.handleErrorFlush(output); } else { super.handleErrorFlush(output); } } /** * Runs the various sub-builds. */ public void execute() { if (buildpath == null) { throw new BuildException("No buildpath specified"); } final String[] filenames = buildpath.list(); final int count = filenames.length; if (count < 1) { log("No sub-builds to iterate on", Project.MSG_WARN); return; } /* //REVISIT: there must be cleaner way of doing this, if it is merited at all if (subTarget == null) { subTarget = getOwningTarget().getName(); } */ BuildException buildException = null; for (int i = 0; i < count; ++i) { File file = null; String subdirPath = null; Throwable thrownException = null; try { File directory = null; file = new File(filenames[i]); if (file.isDirectory()) { if (verbose) { subdirPath = file.getPath(); log("Entering directory: " + subdirPath + "\n", Project.MSG_INFO); } if (genericantfile != null) { directory = file; file = genericantfile; } else { file = new File(file, antfile); } } execute(file, directory); if (verbose && subdirPath != null) { log("Leaving directory: " + subdirPath + "\n", Project.MSG_INFO); } } catch (RuntimeException ex) { if (!(getProject().isKeepGoingMode())) { if (verbose && subdirPath != null) { log("Leaving directory: " + subdirPath + "\n", Project.MSG_INFO); } throw ex; // throw further } thrownException = ex; } catch (Throwable ex) { if (!(getProject().isKeepGoingMode())) { if (verbose && subdirPath != null) { log("Leaving directory: " + subdirPath + "\n", Project.MSG_INFO); } throw new BuildException(ex); } thrownException = ex; } if (thrownException != null) { if (thrownException instanceof BuildException) { log("File '" + file + "' failed with message '" + thrownException.getMessage() + "'.", Project.MSG_ERR); // only the first build exception is reported if (buildException == null) { buildException = (BuildException) thrownException; } } else { log("Target '" + file + "' failed with message '" + thrownException.getMessage() + "'.", Project.MSG_ERR); thrownException.printStackTrace(System.err); if (buildException == null) { buildException = new BuildException(thrownException); } } if (verbose && subdirPath != null) { log("Leaving directory: " + subdirPath + "\n", Project.MSG_INFO); } } } // check if one of the builds failed in keep going mode if (buildException != null) { throw buildException; } } /** * Runs the given target on the provided build file. * * @param file the build file to execute * @param directory the directory of the current iteration * @throws BuildException is the file cannot be found, read, is * a directory, or the target called failed, but only if * failOnError is true. Otherwise, * a warning log message is simply output. */ private void execute(File file, File directory) throws BuildException { if (!file.exists() || file.isDirectory() || !file.canRead()) { String msg = "Invalid file: " + file; if (failOnError) { throw new BuildException(msg); } log(msg, Project.MSG_WARN); return; } ant = createAntTask(directory); String antfilename = file.getAbsolutePath(); ant.setAntfile(antfilename); final int size = targets.size(); for (int i = 0; i < size; i++) { TargetElement targetElement = (TargetElement) targets.get(i); ant.addConfiguredTarget(targetElement); } try { ant.execute(); } catch (BuildException e) { if (failOnError || isHardError(e)) { throw e; } log("Failure for target '" + subTarget + "' of: " + antfilename + "\n" + e.getMessage(), Project.MSG_WARN); } catch (Throwable e) { if (failOnError || isHardError(e)) { throw new BuildException(e); } log("Failure for target '" + subTarget + "' of: " + antfilename + "\n" + e.toString(), Project.MSG_WARN); } finally { ant = null; } } /** whether we should even try to continue after this error */ private boolean isHardError(Throwable t) { if (t instanceof BuildException) { return isHardError(t.getCause()); } else if (t instanceof OutOfMemoryError) { return true; } else if (t instanceof ThreadDeath) { return true; } else { // incl. t == null return false; } } /** * This method builds the file name to use in conjunction with directories. * *

Defaults to "build.xml". * If genericantfile is set, this attribute is ignored.

* * @param antfile the short build file name. Defaults to "build.xml". */ public void setAntfile(String antfile) { this.antfile = antfile; } /** * This method builds a file path to use in conjunction with directories. * *

Use genericantfile, in order to run the same build file * with different basedirs.

* If this attribute is set, antfile is ignored. * * @param afile (path of the generic ant file, absolute or relative to * project base directory) * */ public void setGenericAntfile(File afile) { this.genericantfile = afile; } /** * Sets whether to fail with a build exception on error, or go on. * * @param failOnError the new value for this boolean flag. */ public void setFailonerror(boolean failOnError) { this.failOnError = failOnError; } /** * The target to call on the different sub-builds. Set to "" to execute * the default target. * @param target the target *

*/ // REVISIT: Defaults to the target name that contains this task if not specified. public void setTarget(String target) { this.subTarget = target; } /** * Add a target to this Ant invocation. * @param t the TargetElement to add. * @since Ant 1.7 */ public void addConfiguredTarget(TargetElement t) { String name = t.getName(); if ("".equals(name)) { throw new BuildException("target name must not be empty"); } targets.add(t); } /** * Enable/ disable verbose log messages showing when each sub-build path is entered/ exited. * The default value is "false". * @param on true to enable verbose mode, false otherwise (default). */ public void setVerbose(boolean on) { this.verbose = on; } /** * Corresponds to <ant>'s * output attribute. * * @param s the filename to write the output to. */ public void setOutput(String s) { this.output = s; } /** * Corresponds to <ant>'s * inheritall attribute. * * @param b the new value for this boolean flag. */ public void setInheritall(boolean b) { this.inheritAll = b; } /** * Corresponds to <ant>'s * inheritrefs attribute. * * @param b the new value for this boolean flag. */ public void setInheritrefs(boolean b) { this.inheritRefs = b; } /** * Corresponds to <ant>'s * nested <property> element. * * @param p the property to pass on explicitly to the sub-build. */ public void addProperty(Property p) { properties.addElement(p); } /** * Corresponds to <ant>'s * nested <reference> element. * * @param r the reference to pass on explicitly to the sub-build. */ public void addReference(Ant.Reference r) { references.addElement(r); } /** * Corresponds to <ant>'s * nested <propertyset> element. * @param ps the propertset */ public void addPropertyset(PropertySet ps) { propertySets.addElement(ps); } /** * Adds a directory set to the implicit build path. *

* Note that the directories will be added to the build path * in no particular order, so if order is significant, one should * use a file list instead! * * @param set the directory set to add. */ public void addDirset(DirSet set) { add(set); } /** * Adds a file set to the implicit build path. *

* Note that the directories will be added to the build path * in no particular order, so if order is significant, one should * use a file list instead! * * @param set the file set to add. */ public void addFileset(FileSet set) { add(set); } /** * Adds an ordered file list to the implicit build path. *

* Note that contrary to file and directory sets, file lists * can reference non-existent files or directories! * * @param list the file list to add. */ public void addFilelist(FileList list) { add(list); } /** * Adds a resource collection to the implicit build path. * * @param rc the resource collection to add. * @since Ant 1.7 */ public void add(ResourceCollection rc) { getBuildpath().add(rc); } /** * Set the buildpath to be used to find sub-projects. * * @param s an Ant Path object containing the buildpath. */ public void setBuildpath(Path s) { getBuildpath().append(s); } /** * Creates a nested build path, and add it to the implicit build path. * * @return the newly created nested build path. */ public Path createBuildpath() { return getBuildpath().createPath(); } /** * Creates a nested <buildpathelement>, * and add it to the implicit build path. * * @return the newly created nested build path element. */ public Path.PathElement createBuildpathElement() { return getBuildpath().createPathElement(); } /** * Gets the implicit build path, creating it if null. * * @return the implicit build path. */ private Path getBuildpath() { if (buildpath == null) { buildpath = new Path(getProject()); } return buildpath; } /** * Buildpath to use, by reference. * * @param r a reference to an Ant Path object containing the buildpath. */ public void setBuildpathRef(Reference r) { createBuildpath().setRefid(r); } /** * Creates the <ant> task configured to run a specific target. * * @param directory : if not null the directory where the build should run * * @return the ant task, configured with the explicit properties and * references necessary to run the sub-build. */ private Ant createAntTask(File directory) { Ant antTask = new Ant(this); antTask.init(); if (subTarget != null && subTarget.length() > 0) { antTask.setTarget(subTarget); } if (output != null) { antTask.setOutput(output); } if (directory != null) { antTask.setDir(directory); } else { antTask.setUseNativeBasedir(true); } antTask.setInheritAll(inheritAll); for (Enumeration i = properties.elements(); i.hasMoreElements();) { copyProperty(antTask.createProperty(), (Property) i.nextElement()); } for (Enumeration i = propertySets.elements(); i.hasMoreElements();) { antTask.addPropertyset((PropertySet) i.nextElement()); } antTask.setInheritRefs(inheritRefs); for (Enumeration i = references.elements(); i.hasMoreElements();) { antTask.addReference((Ant.Reference) i.nextElement()); } return antTask; } /** * Assigns an Ant property to another. * * @param to the destination property whose content is modified. * @param from the source property whose content is copied. */ private static void copyProperty(Property to, Property from) { to.setName(from.getName()); if (from.getValue() != null) { to.setValue(from.getValue()); } if (from.getFile() != null) { to.setFile(from.getFile()); } if (from.getResource() != null) { to.setResource(from.getResource()); } if (from.getPrefix() != null) { to.setPrefix(from.getPrefix()); } if (from.getRefid() != null) { to.setRefid(from.getRefid()); } if (from.getEnvironment() != null) { to.setEnvironment(from.getEnvironment()); } if (from.getClasspath() != null) { to.setClasspath(from.getClasspath()); } } } // END class SubAnt





© 2015 - 2025 Weber Informatics LLC | Privacy Policy