mbenson 2004/12/14 15:48:02
Modified: . Tag: ANT_16_BRANCH WHATSNEW
src/etc/testcases/taskdefs Tag: ANT_16_BRANCH ant.xml
calltarget.xml
src/main/org/apache/tools/ant Tag: ANT_16_BRANCH
Project.java Target.java
src/main/org/apache/tools/ant/taskdefs Tag: ANT_16_BRANCH
Ant.java CallTarget.java
src/testcases/org/apache/tools/ant/taskdefs Tag:
ANT_16_BRANCH AntTest.java CallTargetTest.java
docs/manual/CoreTasks Tag: ANT_16_BRANCH ant.html
antcall.html
Added: src/etc/testcases/taskdefs Tag: ANT_16_BRANCH bar.properties
foo.properties
src/main/org/apache/tools/ant Tag: ANT_16_BRANCH
Executor.java
src/main/org/apache/tools/ant/helper Tag: ANT_16_BRANCH
DefaultExecutor.java KeepGoingExecutor.java
SingleCheckExecutor.java
Log:
Merge Executor stuff and nested target elements for ant & antcall
Revision Changes Path
No revision
No revision
1.503.2.149 +7 -0 ant/WHATSNEW
Index: WHATSNEW
===================================================================
RCS file: /home/cvs/ant/WHATSNEW,v
retrieving revision 1.503.2.148
retrieving revision 1.503.2.149
diff -u -r1.503.2.148 -r1.503.2.149
--- WHATSNEW 13 Dec 2004 18:52:17 -0000 1.503.2.148
+++ WHATSNEW 14 Dec 2004 23:48:00 -0000 1.503.2.149
@@ -22,6 +22,13 @@
* New attribute alwayslog for <redirector> type.
+* Added <target> nested elements to <ant> and <antcall> to allow
+ specification of multiple sub-build targets, which are executed
+ with a single dependency analysis.
+
+* Refactored Target invocation into org.apache.tools.ant.Executor
+ implementations. Bugzilla Reports 21421, 29248.
+
Fixed bugs:
-----------
No revision
No revision
1.12.2.1 +36 -0 ant/src/etc/testcases/taskdefs/ant.xml
Index: ant.xml
===================================================================
RCS file: /home/cvs/ant/src/etc/testcases/taskdefs/ant.xml,v
retrieving revision 1.12
retrieving revision 1.12.2.1
diff -u -r1.12 -r1.12.2.1
--- ant.xml 10 Sep 2003 13:57:19 -0000 1.12
+++ ant.xml 14 Dec 2004 23:48:00 -0000 1.12.2.1
@@ -185,4 +185,40 @@
<ant antfile="ant.topleveltest.xml"/>
</target>
+ <target name="multiple-property-file-children">
+ <ant target="dummy" antfile="ant.xml">
+ <property file="foo.properties"/>
+ <property file="bar.properties"/>
+ </ant>
+ </target>
+
+ <target name="blank-target">
+ <ant antfile="ant.topleveltest.xml">
+ <target name="" />
+ </ant>
+ </target>
+
+ <target name="multiple-targets">
+ <ant antfile="ant.xml">
+ <target name="ta" />
+ <target name="tb" />
+ <target name="tc" />
+ </ant>
+ </target>
+
+ <target name="multiple-targets-2">
+ <ant antfile="ant.xml">
+ <target name="tb" />
+ <target name="da" />
+ </ant>
+ </target>
+
+ <target name="ta"><echo>ta</echo></target>
+ <target name="tb" depends="da,dc"><echo>tb</echo></target>
+ <target name="tc" depends="db,dc"><echo>tc</echo></target>
+
+ <target name="da"><echo>da</echo></target>
+ <target name="db"><echo>db</echo></target>
+ <target name="dc"><echo>dc</echo></target>
+
</project>
1.3.2.1 +31 -1 ant/src/etc/testcases/taskdefs/calltarget.xml
Index: calltarget.xml
===================================================================
RCS file: /home/cvs/ant/src/etc/testcases/taskdefs/calltarget.xml,v
retrieving revision 1.3
retrieving revision 1.3.2.1
diff -u -r1.3 -r1.3.2.1
--- calltarget.xml 5 Aug 2003 13:56:26 -0000 1.3
+++ calltarget.xml 14 Dec 2004 23:48:00 -0000 1.3.2.1
@@ -50,4 +50,34 @@
<param name="multi" value="SET"/>
</antcall>
</target>
-</project>
\ No newline at end of file
+
+ <target name="blank-target">
+ <antcall>
+ <target name="" />
+ </antcall>
+ </target>
+
+ <target name="multiple-targets">
+ <antcall>
+ <target name="ta" />
+ <target name="tb" />
+ <target name="tc" />
+ </antcall>
+ </target>
+
+ <target name="multiple-targets-2">
+ <ant antfile="ant.xml">
+ <target name="tb" />
+ <target name="da" />
+ </ant>
+ </target>
+
+ <target name="ta"><echo>ta</echo></target>
+ <target name="tb" depends="da,dc"><echo>tb</echo></target>
+ <target name="tc" depends="db,dc"><echo>tc</echo></target>
+
+ <target name="da"><echo>da</echo></target>
+ <target name="db"><echo>db</echo></target>
+ <target name="dc"><echo>dc</echo></target>
+
+</project>
No revision
Index: calltarget.xml
===================================================================
RCS file: /home/cvs/ant/src/etc/testcases/taskdefs/calltarget.xml,v
retrieving revision 1.3
retrieving revision 1.3.2.1
diff -u -r1.3 -r1.3.2.1
--- calltarget.xml 5 Aug 2003 13:56:26 -0000 1.3
+++ calltarget.xml 14 Dec 2004 23:48:00 -0000 1.3.2.1
@@ -50,4 +50,34 @@
<param name="multi" value="SET"/>
</antcall>
</target>
-</project>
\ No newline at end of file
+
+ <target name="blank-target">
+ <antcall>
+ <target name="" />
+ </antcall>
+ </target>
+
+ <target name="multiple-targets">
+ <antcall>
+ <target name="ta" />
+ <target name="tb" />
+ <target name="tc" />
+ </antcall>
+ </target>
+
+ <target name="multiple-targets-2">
+ <ant antfile="ant.xml">
+ <target name="tb" />
+ <target name="da" />
+ </ant>
+ </target>
+
+ <target name="ta"><echo>ta</echo></target>
+ <target name="tb" depends="da,dc"><echo>tb</echo></target>
+ <target name="tc" depends="db,dc"><echo>tc</echo></target>
+
+ <target name="da"><echo>da</echo></target>
+ <target name="db"><echo>db</echo></target>
+ <target name="dc"><echo>dc</echo></target>
+
+</project>
No revision
Index: calltarget.xml
===================================================================
RCS file: /home/cvs/ant/src/etc/testcases/taskdefs/calltarget.xml,v
retrieving revision 1.3
retrieving revision 1.3.2.1
diff -u -r1.3 -r1.3.2.1
--- calltarget.xml 5 Aug 2003 13:56:26 -0000 1.3
+++ calltarget.xml 14 Dec 2004 23:48:00 -0000 1.3.2.1
@@ -50,4 +50,34 @@
<param name="multi" value="SET"/>
</antcall>
</target>
-</project>
\ No newline at end of file
+
+ <target name="blank-target">
+ <antcall>
+ <target name="" />
+ </antcall>
+ </target>
+
+ <target name="multiple-targets">
+ <antcall>
+ <target name="ta" />
+ <target name="tb" />
+ <target name="tc" />
+ </antcall>
+ </target>
+
+ <target name="multiple-targets-2">
+ <ant antfile="ant.xml">
+ <target name="tb" />
+ <target name="da" />
+ </ant>
+ </target>
+
+ <target name="ta"><echo>ta</echo></target>
+ <target name="tb" depends="da,dc"><echo>tb</echo></target>
+ <target name="tc" depends="db,dc"><echo>tc</echo></target>
+
+ <target name="da"><echo>da</echo></target>
+ <target name="db"><echo>db</echo></target>
+ <target name="dc"><echo>dc</echo></target>
+
+</project>
1.1.2.1 +0 -0 ant/src/etc/testcases/taskdefs/bar.properties
Index: bar.properties
===================================================================
RCS file: /home/cvs/ant/src/etc/testcases/taskdefs/bar.properties,v
retrieving revision 1.1
retrieving revision 1.1.2.1
diff -u -r1.1 -r1.1.2.1
1.1.2.1 +0 -0 ant/src/etc/testcases/taskdefs/foo.properties
Index: foo.properties
===================================================================
RCS file: /home/cvs/ant/src/etc/testcases/taskdefs/foo.properties,v
retrieving revision 1.1
retrieving revision 1.1.2.1
diff -u -r1.1 -r1.1.2.1
No revision
No revision
1.154.2.14 +124 -42 ant/src/main/org/apache/tools/ant/Project.java
Index: Project.java
===================================================================
RCS file: /home/cvs/ant/src/main/org/apache/tools/ant/Project.java,v
retrieving revision 1.154.2.13
retrieving revision 1.154.2.14
diff -u -r1.154.2.13 -r1.154.2.14
--- Project.java 28 Oct 2004 09:17:48 -0000 1.154.2.13
+++ Project.java 14 Dec 2004 23:48:00 -0000 1.154.2.14
@@ -33,6 +33,8 @@
import java.util.HashSet;
import org.apache.tools.ant.input.DefaultInputHandler;
import org.apache.tools.ant.input.InputHandler;
+import org.apache.tools.ant.helper.DefaultExecutor;
+import org.apache.tools.ant.helper.KeepGoingExecutor;
import org.apache.tools.ant.types.FilterSet;
import org.apache.tools.ant.types.FilterSetCollection;
import org.apache.tools.ant.types.Description;
@@ -274,16 +276,14 @@
*/
private AntClassLoader createClassLoader() {
AntClassLoader loader = null;
- if (!JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_1)) {
- try {
- // 1.2+ - create advanced helper dynamically
- Class loaderClass
+ try {
+ // 1.2+ - create advanced helper dynamically
+ Class loaderClass
= Class.forName(ANTCLASSLOADER_JDK12);
- loader = (AntClassLoader) loaderClass.newInstance();
- } catch (Exception e) {
- log("Unable to create Class Loader: "
- + e.getMessage(), Project.MSG_DEBUG);
- }
+ loader = (AntClassLoader) loaderClass.newInstance();
+ } catch (Exception e) {
+ log("Unable to create Class Loader: "
+ + e.getMessage(), Project.MSG_DEBUG);
}
if (loader == null) {
@@ -766,7 +766,9 @@
/**
* Sets "keep-going" mode. In this mode ANT will try to execute
* as many targets as possible. All targets that do not depend
- * on failed target(s) will be executed.
+ * on failed target(s) will be executed. If the keepGoing settor/getter
+ * methods are used in conjunction with the
<code>ant.executor.class</code>
+ * property, they will have no effect.
* @param keepGoingMode "keep-going" mode
* @since Ant 1.6
*/
@@ -775,7 +777,9 @@
}
/**
- * Returns the keep-going mode.
+ * Returns the keep-going mode. If the keepGoing settor/getter
+ * methods are used in conjunction with the
<code>ant.executor.class</code>
+ * property, they will have no effect.
* @return "keep-going" mode
* @since Ant 1.6
*/
@@ -1056,19 +1060,38 @@
*/
public void executeTargets(Vector targetNames) throws BuildException {
- BuildException thrownException = null;
- for (int i = 0; i < targetNames.size(); i++) {
+ Object o = getReference("ant.executor");
+ if (o == null) {
+ String classname = getProperty("ant.executor.class");
+ if (classname == null) {
+ classname = (keepGoingMode)
+ ? KeepGoingExecutor.class.getName()
+ : DefaultExecutor.class.getName();
+ }
+ log("Attempting to create object of type " + classname,
MSG_DEBUG);
try {
- executeTarget((String) targetNames.elementAt(i));
- } catch (BuildException ex) {
- if (!(keepGoingMode)) {
- throw ex; // Throw further
+ o = Class.forName(classname, true, coreLoader).newInstance();
+ } catch (ClassNotFoundException seaEnEfEx) {
+ //try the current classloader
+ try {
+ o = Class.forName(classname).newInstance();
+ } catch (Exception ex) {
+ log(ex.toString(), MSG_ERR);
}
- thrownException = ex;
+ } catch (Exception ex) {
+ log(ex.toString(), MSG_ERR);
+ }
+ if (o != null) {
+ addReference("ant.executor", o);
}
}
- if (thrownException != null) {
- throw thrownException;
+
+ if (o == null) {
+ throw new BuildException("Unable to obtain a Target Executor
instance.");
+ } else {
+ String[] targetNameArray = (String[])(targetNames.toArray(
+ new String[targetNames.size()]));
+ ((Executor)o).executeTargets(this, targetNameArray);
}
}
@@ -1183,13 +1206,19 @@
throw new BuildException(msg);
}
- // Sort the dependency tree, and run everything from the
- // beginning until we hit our targetName.
+ // Sort and run the dependency tree.
// Sorting checks if all the targets (and dependencies)
// exist, and if there is any cycle in the dependency
// graph.
- Vector sortedTargets = topoSort(targetName, targets);
+ executeSortedTargets(topoSort(targetName, targets, false));
+ }
+ /**
+ * Executes a <code>Vector</code> of sorted targets.
+ * @param sortedTargets the aforementioned <code>Vector</code>.
+ */
+ public void executeSortedTargets(Vector sortedTargets)
+ throws BuildException {
Set succeededTargets = new HashSet();
BuildException buildException = null; // first build exception
for (Enumeration iter = sortedTargets.elements();
@@ -1247,9 +1276,6 @@
}
}
}
- if (curtarget.getName().equals(targetName)) { // old exit
condition
- break;
- }
}
if (buildException != null) {
throw buildException;
@@ -1531,11 +1557,6 @@
*/
public void setFileLastModified(File file, long time)
throws BuildException {
- if (JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_1)) {
- log("Cannot change the modification time of " + file
- + " in JDK 1.1", Project.MSG_WARN);
- return;
- }
fileUtils.setFileLastModified(file, time);
log("Setting modification time for " + file, MSG_VERBOSE);
}
@@ -1558,45 +1579,106 @@
}
/**
- * Topologically sorts a set of targets.
+ * Topologically sorts a set of targets. Equivalent to calling
+ * <code>topoSort(new String[] {root}, targets, true)</code>.
*
* @param root The name of the root target. The sort is created in such
* a way that the sequence of Targets up to the root
* target is the minimum possible such sequence.
* Must not be <code>null</code>.
- * @param targets A map of names to targets (String to Target).
+ * @param targets A Hashtable mapping names to Targets.
* Must not be <code>null</code>.
- * @return a vector of Target objects in sorted order.
+ * @return a Vector of ALL Target objects in sorted order.
* @exception BuildException if there is a cyclic dependency among the
* targets, or if a named target does not
exist.
*/
public final Vector topoSort(String root, Hashtable targets)
throws BuildException {
+ return topoSort(new String[] {root}, targets, true);
+ }
+
+ /**
+ * Topologically sorts a set of targets. Equivalent to calling
+ * <code>topoSort(new String[] {root}, targets, returnAll)</code>.
+ *
+ * @param root The name of the root target. The sort is created in such
+ * a way that the sequence of Targets up to the root
+ * target is the minimum possible such sequence.
+ * Must not be <code>null</code>.
+ * @param targets A Hashtable mapping names to Targets.
+ * Must not be <code>null</code>.
+ * @param returnAll <code>boolean</code> indicating whether to return all
+ * targets, or the execution sequence only.
+ * @return a Vector of Target objects in sorted order.
+ * @exception BuildException if there is a cyclic dependency among the
+ * targets, or if a named target does not
exist.
+ * @since Ant 1.6.3
+ */
+ public final Vector topoSort(String root, Hashtable targets,
+ boolean returnAll) throws BuildException {
+ return topoSort(new String[] {root}, targets, returnAll);
+ }
+
+ /**
+ * Topologically sorts a set of targets.
+ *
+ * @param root <code>String[]</code> containing the names of the root
targets.
+ * The sort is created in such a way that the ordered
sequence of
+ * Targets is the minimum possible such sequence to the
specified
+ * root targets.
+ * Must not be <code>null</code>.
+ * @param targets A map of names to targets (String to Target).
+ * Must not be <code>null</code>.
+ * @param returnAll <code>boolean</code> indicating whether to return all
+ * targets, or the execution sequence only.
+ * @return a Vector of Target objects in sorted order.
+ * @exception BuildException if there is a cyclic dependency among the
+ * targets, or if a named target does not
exist.
+ * @since Ant 1.6.3
+ */
+ public final Vector topoSort(String[] root, Hashtable targets,
+ boolean returnAll) throws BuildException {
Vector ret = new Vector();
Hashtable state = new Hashtable();
Stack visiting = new Stack();
- // We first run a DFS based sort using the root as the starting node.
- // This creates the minimum sequence of Targets to the root node.
+ // We first run a DFS based sort using each root as a starting node.
+ // This creates the minimum sequence of Targets to the root node(s).
// We then do a sort on any remaining unVISITED targets.
// This is unnecessary for doing our build, but it catches
// circular dependencies or missing Targets on the entire
// dependency tree, not just on the Targets that depend on the
// build Target.
- tsort(root, targets, state, visiting, ret);
- log("Build sequence for target `" + root + "' is " + ret,
MSG_VERBOSE);
+ for (int i = 0; i < root.length; i++) {
+ String st = (String)(state.get(root[i]));
+ if (st == null) {
+ tsort(root[i], targets, state, visiting, ret);
+ } else if (st == VISITING) {
+ throw new RuntimeException("Unexpected node in visiting
state: "
+ + root[i]);
+ }
+ }
+ StringBuffer buf = new StringBuffer("Build sequence for target(s)");
+
+ for (int j = 0; j < root.length; j++) {
+ buf.append((j == 0) ? " `" : ", `").append(root[j]).append('\'');
+ }
+ buf.append(" is " + ret);
+ log(buf.toString(), MSG_VERBOSE);
+
+ Vector complete = (returnAll) ? ret : new Vector(ret);
for (Enumeration en = targets.keys(); en.hasMoreElements();) {
String curTarget = (String) en.nextElement();
String st = (String) state.get(curTarget);
if (st == null) {
- tsort(curTarget, targets, state, visiting, ret);
+ tsort(curTarget, targets, state, visiting, complete);
} else if (st == VISITING) {
throw new RuntimeException("Unexpected node in visiting
state: "
+ curTarget);
}
}
- log("Complete build sequence is " + ret, MSG_VERBOSE);
+ log("Complete build sequence is " + complete, MSG_VERBOSE);
return ret;
}
@@ -2064,7 +2146,7 @@
*/
public Object get(Object key) {
//System.out.println("AntRefTable.get " + key);
- Object o = super.get(key);
+ Object o = getReal(key);
if (o instanceof UnknownElement) {
// Make sure that
UnknownElement ue = (UnknownElement) o;
1.46.2.9 +4 -8 ant/src/main/org/apache/tools/ant/Target.java
Index: Target.java
===================================================================
RCS file: /home/cvs/ant/src/main/org/apache/tools/ant/Target.java,v
retrieving revision 1.46.2.8
retrieving revision 1.46.2.9
diff -u -r1.46.2.8 -r1.46.2.9
--- Target.java 13 Aug 2004 09:28:47 -0000 1.46.2.8
+++ Target.java 14 Dec 2004 23:48:01 -0000 1.46.2.9
@@ -20,6 +20,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
+import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;
@@ -221,14 +222,9 @@
* @since Ant 1.6
*/
public boolean dependsOn(String other) {
- if (getProject() != null) {
- List l = getProject().topoSort(getName(),
- getProject().getTargets());
- int myIdx = l.indexOf(this);
- int otherIdx = l.indexOf(getProject().getTargets().get(other));
- return myIdx >= otherIdx;
- }
- return false;
+ Project p = getProject();
+ Hashtable t = (p == null) ? null : p.getTargets();
+ return (p != null && p.topoSort(getName(), t,
false).contains(t.get(other)));
}
/**
No revision
Index: Target.java
===================================================================
RCS file: /home/cvs/ant/src/main/org/apache/tools/ant/Target.java,v
retrieving revision 1.46.2.8
retrieving revision 1.46.2.9
diff -u -r1.46.2.8 -r1.46.2.9
--- Target.java 13 Aug 2004 09:28:47 -0000 1.46.2.8
+++ Target.java 14 Dec 2004 23:48:01 -0000 1.46.2.9
@@ -20,6 +20,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
+import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;
@@ -221,14 +222,9 @@
* @since Ant 1.6
*/
public boolean dependsOn(String other) {
- if (getProject() != null) {
- List l = getProject().topoSort(getName(),
- getProject().getTargets());
- int myIdx = l.indexOf(this);
- int otherIdx = l.indexOf(getProject().getTargets().get(other));
- return myIdx >= otherIdx;
- }
- return false;
+ Project p = getProject();
+ Hashtable t = (p == null) ? null : p.getTargets();
+ return (p != null && p.topoSort(getName(), t,
false).contains(t.get(other)));
}
/**
No revision
Index: Target.java
===================================================================
RCS file: /home/cvs/ant/src/main/org/apache/tools/ant/Target.java,v
retrieving revision 1.46.2.8
retrieving revision 1.46.2.9
diff -u -r1.46.2.8 -r1.46.2.9
--- Target.java 13 Aug 2004 09:28:47 -0000 1.46.2.8
+++ Target.java 14 Dec 2004 23:48:01 -0000 1.46.2.9
@@ -20,6 +20,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
+import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;
@@ -221,14 +222,9 @@
* @since Ant 1.6
*/
public boolean dependsOn(String other) {
- if (getProject() != null) {
- List l = getProject().topoSort(getName(),
- getProject().getTargets());
- int myIdx = l.indexOf(this);
- int otherIdx = l.indexOf(getProject().getTargets().get(other));
- return myIdx >= otherIdx;
- }
- return false;
+ Project p = getProject();
+ Hashtable t = (p == null) ? null : p.getTargets();
+ return (p != null && p.topoSort(getName(), t,
false).contains(t.get(other)));
}
/**
1.2.2.1 +0 -0 ant/src/main/org/apache/tools/ant/Executor.java
Index: Executor.java
===================================================================
RCS file: /home/cvs/ant/src/main/org/apache/tools/ant/Executor.java,v
retrieving revision 1.2
retrieving revision 1.2.2.1
diff -u -r1.2 -r1.2.2.1
No revision
No revision
1.3.2.1 +0 -0
ant/src/main/org/apache/tools/ant/helper/DefaultExecutor.java
Index: DefaultExecutor.java
===================================================================
RCS file:
/home/cvs/ant/src/main/org/apache/tools/ant/helper/DefaultExecutor.java,v
retrieving revision 1.3
retrieving revision 1.3.2.1
diff -u -r1.3 -r1.3.2.1
1.2.2.1 +0 -0
ant/src/main/org/apache/tools/ant/helper/KeepGoingExecutor.java
Index: KeepGoingExecutor.java
===================================================================
RCS file:
/home/cvs/ant/src/main/org/apache/tools/ant/helper/KeepGoingExecutor.java,v
retrieving revision 1.2
retrieving revision 1.2.2.1
diff -u -r1.2 -r1.2.2.1
1.2.2.1 +0 -0
ant/src/main/org/apache/tools/ant/helper/SingleCheckExecutor.java
Index: SingleCheckExecutor.java
===================================================================
RCS file:
/home/cvs/ant/src/main/org/apache/tools/ant/helper/SingleCheckExecutor.java,v
retrieving revision 1.2
retrieving revision 1.2.2.1
diff -u -r1.2 -r1.2.2.1
No revision
No revision
1.92.2.10 +114 -67 ant/src/main/org/apache/tools/ant/taskdefs/Ant.java
Index: Ant.java
===================================================================
RCS file: /home/cvs/ant/src/main/org/apache/tools/ant/taskdefs/Ant.java,v
retrieving revision 1.92.2.9
retrieving revision 1.92.2.10
diff -u -r1.92.2.9 -r1.92.2.10
--- Ant.java 12 Nov 2004 11:08:00 -0000 1.92.2.9
+++ Ant.java 14 Dec 2004 23:48:01 -0000 1.92.2.10
@@ -31,11 +31,13 @@
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.BuildListener;
import org.apache.tools.ant.DefaultLogger;
+import org.apache.tools.ant.Executor;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.ProjectComponent;
import org.apache.tools.ant.ProjectHelper;
import org.apache.tools.ant.Target;
import org.apache.tools.ant.Task;
+import org.apache.tools.ant.helper.SingleCheckExecutor;
import org.apache.tools.ant.types.PropertySet;
import org.apache.tools.ant.util.FileUtils;
@@ -47,8 +49,8 @@
* <ant antfile="build.xml" target="bar" >
* <property name="property1" value="aaaaa" />
* <property name="foo" value="baz" />
- * </ant></SPAN>
- * </target></SPAN>
+ * </ant></span>
+ * </target></span>
*
* <target name="bar" depends="init">
* <echo message="prop is ${property1} ${foo}" />
@@ -56,13 +58,15 @@
* </pre>
*
*
- *
* @since Ant 1.1
*
* @ant.task category="control"
*/
public class Ant extends Task {
+ /** Target Executor */
+ private static final Executor EXECUTOR = new SingleCheckExecutor();
+
/** the basedir where is executed the build file */
private File dir = null;
@@ -72,9 +76,6 @@
*/
private String antFile = null;
- /** the target to call if any */
- private String target = null;
-
/** the output */
private String output = null;
@@ -99,6 +100,12 @@
/** the sets of properties to pass to the new project */
private Vector propertySets = new Vector();
+ /** the targets to call on the new project */
+ private Vector targets = new Vector();
+
+ /** whether the target attribute was specified **/
+ private boolean targetAttributeSet = false;
+
/**
* If true, pass all properties to the new Ant project.
* Defaults to true.
@@ -196,9 +203,8 @@
}
/**
- * Pass output sent to System.out to the new project.
+ * @see Task#handleOutput(String)
*
- * @param output a line of output
* @since Ant 1.5
*/
public void handleOutput(String output) {
@@ -210,16 +216,6 @@
}
/**
- * 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
@@ -234,9 +230,7 @@
}
/**
- * Pass output sent to System.out to the new project.
- *
- * @param output The output to log. Should not be <code>null</code>.
+ * @see Task#handleFlush(String)
*
* @since Ant 1.5.2
*/
@@ -249,9 +243,7 @@
}
/**
- * Pass output sent to System.err to the new project.
- *
- * @param output The error output to log. Should not be
<code>null</code>.
+ * @see Task#handleErrorOutput(String)
*
* @since Ant 1.5
*/
@@ -264,9 +256,7 @@
}
/**
- * Pass output sent to System.err to the new project.
- *
- * @param output The error output to log. Should not be
<code>null</code>.
+ * @see Task#handleErrorFlush(String)
*
* @since Ant 1.5.2
*/
@@ -280,13 +270,13 @@
/**
* Do the execution.
- * @throws BuildException if a target tries to call itself
- * probably also if a BuildException is thrown by the new project
+ * @throws BuildException if a target tries to call itself;
+ * probably also if a BuildException is thrown by the new project.
*/
public void execute() throws BuildException {
File savedDir = dir;
String savedAntFile = antFile;
- String savedTarget = target;
+ Vector locals = new Vector(targets);
try {
if (newProject == null) {
reinit();
@@ -318,8 +308,9 @@
File file = FileUtils.newFileUtils().resolveFile(dir, antFile);
antFile = file.getAbsolutePath();
- log("calling target " + (target != null ? target : "[default]")
- + " in build file " + antFile, Project.MSG_VERBOSE);
+ log("calling target(s) "
+ + ((locals.size() == 0) ? locals.toString() : "[default]")
+ + " in build file " + antFile, Project.MSG_VERBOSE);
newProject.setUserProperty("ant.file" , antFile);
String thisAntFile = getProject().getProperty("ant.file");
@@ -349,8 +340,11 @@
ex, getLocation());
}
- if (target == null) {
- target = newProject.getDefaultTarget();
+ if (locals.size() == 0) {
+ String defaultTarget = newProject.getDefaultTarget();
+ if (defaultTarget != null) {
+ locals.add(defaultTarget);
+ }
}
if (newProject.getProperty("ant.file")
@@ -359,13 +353,18 @@
String owningTargetName = getOwningTarget().getName();
- if (owningTargetName.equals(target)) {
+ if (locals.contains(owningTargetName)) {
throw new BuildException(getTaskName() + " task calling "
+ "its own parent target.");
} else {
- Target other =
- (Target) getProject().getTargets().get(target);
- if (other != null && other.dependsOn(owningTargetName)) {
+ boolean circular = false;
+ for (Iterator it = locals.iterator(); !circular &&
it.hasNext();) {
+ Target other = (Target)
(getProject().getTargets().get(
+ (String) (it.next())));
+ circular |= (other != null
+ && other.dependsOn(owningTargetName));
+ }
+ if (circular) {
throw new BuildException(getTaskName()
+ " task calling a target"
+ " that depends on"
@@ -378,12 +377,14 @@
addReferences();
- if (target != null && !"".equals(target)) {
+ if (locals.size() > 0 && !(locals.size() == 1 && locals.get(0)
== "")) {
Throwable t = null;
try {
log("Entering " + antFile + "...", Project.MSG_VERBOSE);
newProject.fireSubBuildStarted();
- newProject.executeTarget(target);
+ EXECUTOR.executeTargets(newProject,
+ (String[]) (locals.toArray(new
String[locals.size()])));
+
} catch (BuildException ex) {
t = ProjectHelper
.addLocationToBuildException(ex, getLocation());
@@ -411,14 +412,13 @@
}
dir = savedDir;
antFile = savedAntFile;
- target = savedTarget;
}
}
/**
* Override the properties in the new project with the one
* explicitly defined as nested elements here.
- * @throws BuildException under unknown circumstances
+ * @throws BuildException under unknown circumstances.
*/
private void overrideProperties() throws BuildException {
// remove duplicate properties - last property wins
@@ -448,7 +448,7 @@
* new project. Also copy over all references that don't override
* existing references in the new project if inheritrefs has been
* requested.
- * @throws BuildException if a reference does not have a refid
+ * @throws BuildException if a reference does not have a refid.
*/
private void addReferences() throws BuildException {
Hashtable thisReferences
@@ -494,11 +494,12 @@
/**
* Try to clone and reconfigure the object referenced by oldkey in
- * the parent project and add it to the new project with the key
- * newkey.
+ * the parent project and add it to the new project with the key newkey.
*
* <p>If we cannot clone it, copy the referenced object itself and
* keep our fingers crossed.</p>
+ * @param oldKey the reference id in the current project.
+ * @param newKey the reference id in the new project.
*/
private void copyReference(String oldKey, String newKey) {
Object orig = getProject().getReference(oldKey);
@@ -547,7 +548,7 @@
* Copies all properties from the given table to the new project -
* omitting those that have already been set in the new project as
* well as properties named basedir or ant.file.
- * @param props properties to copy to the new project
+ * @param props properties <code>Hashtable</code> to copy to the new
project.
* @since Ant 1.6
*/
private void addAlmostAll(Hashtable props) {
@@ -573,17 +574,16 @@
* Defaults to the current project's basedir, unless inheritall
* has been set to false, in which case it doesn't have a default
* value. This will override the basedir setting of the called project.
- * @param d new directory
+ * @param d new directory as <code>File</code>.
*/
public void setDir(File d) {
this.dir = d;
}
/**
- * The build file to use.
- * Defaults to "build.xml". This file is expected to be a filename
relative
- * to the dir attribute given.
- * @param s build file to use
+ * The build file to use. Defaults to "build.xml". This file is expected
+ * to be a filename relative to the dir attribute given.
+ * @param s the <code>String</code> build file name.
*/
public void setAntfile(String s) {
// @note: it is a string and not a file to handle relative/absolute
@@ -595,22 +595,21 @@
/**
* The target of the new Ant project to execute.
* Defaults to the new project's default target.
- * @param s target to invoke
+ * @param s the name of the target to invoke.
*/
public void setTarget(String s) {
if (s.equals("")) {
throw new BuildException("target attribute must not be empty");
}
-
- this.target = s;
+ targets.add(s);
+ targetAttributeSet = true;
}
/**
- * Filename to write the output to.
- * This is relative to the value of the dir attribute
- * if it has been set or to the base directory of the
+ * Set the filename to write the output to. This is relative to the value
+ * of the dir attribute if it has been set or to the base directory of
the
* current project otherwise.
- * @param s file to which the output should go to
+ * @param s the name of the file to which the output should go.
*/
public void setOutput(String s) {
this.output = s;
@@ -618,8 +617,8 @@
/**
* Property to pass to the new project.
- * The property is passed as a 'user property'
- * @return new property created
+ * The property is passed as a 'user property'.
+ * @return the created <code>Property</code> object.
*/
public Property createProperty() {
if (newProject == null) {
@@ -633,18 +632,35 @@
}
/**
- * Reference element identifying a data type to carry
+ * Add a Reference element identifying a data type to carry
* over to the new project.
- * @param r reference to add
+ * @param r <code>Reference</code> to add.
*/
public void addReference(Reference r) {
references.addElement(r);
}
/**
- * Set of properties to pass to the new project.
+ * Add a target to this Ant invocation.
+ * @param t the <CODE>TargetElement</CODE> to add.
+ * @since Ant 1.6.3
+ */
+ public void addConfiguredTarget(TargetElement t) {
+ if (targetAttributeSet) {
+ throw new BuildException(
+ "nested target is incompatible with the target attribute");
+ }
+ String name = t.getName();
+ if (name.equals("")) {
+ throw new BuildException("target name must not be empty");
+ }
+ targets.add(name);
+ }
+
+ /**
+ * Add a set of properties to pass to the new project.
*
- * @param ps property set to add
+ * @param ps <code>PropertySet</code> to add.
* @since Ant 1.6
*/
public void addPropertyset(PropertySet ps) {
@@ -665,7 +681,7 @@
public static class Reference
extends org.apache.tools.ant.types.Reference {
- /** Creates a reference to be configured by Ant */
+ /** Creates a reference to be configured by Ant. */
public Reference() {
super();
}
@@ -677,19 +693,50 @@
* new project.
*
* @param targetid the id under which this reference will be passed
to
- * the new project */
+ * the new project. */
public void setToRefid(String targetid) {
this.targetid = targetid;
}
/**
* Get the id under which this reference will be stored in the new
- * project
+ * project.
*
* @return the id of the reference in the new project.
*/
public String getToRefid() {
return targetid;
+ }
+ }
+
+ /**
+ * Helper class that implements the nested <target>
+ * element of <ant> and <antcall>.
+ * @since Ant 1.6.3
+ */
+ public static class TargetElement {
+ private String name;
+
+ /**
+ * Default constructor.
+ */
+ public TargetElement() {
+ }
+
+ /**
+ * Set the name of this TargetElement.
+ * @param name the <CODE>String</CODE> target name.
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Get the name of this TargetElement.
+ * @return <CODE>String</CODE>.
+ */
+ public String getName() {
+ return name;
}
}
}
1.36.2.5 +46 -25
ant/src/main/org/apache/tools/ant/taskdefs/CallTarget.java
Index: CallTarget.java
===================================================================
RCS file:
/home/cvs/ant/src/main/org/apache/tools/ant/taskdefs/CallTarget.java,v
retrieving revision 1.36.2.4
retrieving revision 1.36.2.5
diff -u -r1.36.2.4 -r1.36.2.5
--- CallTarget.java 9 Mar 2004 17:01:33 -0000 1.36.2.4
+++ CallTarget.java 14 Dec 2004 23:48:01 -0000 1.36.2.5
@@ -17,10 +17,12 @@
package org.apache.tools.ant.taskdefs;
-import org.apache.tools.ant.BuildException;
-import org.apache.tools.ant.Task;
import java.io.IOException;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.PropertySet;
+
/**
* Call another target in the same project.
*
@@ -41,7 +43,6 @@
* defined in the project itself.
*
*
- *
* @since Ant 1.2
*
* @ant.task name="antcall" category="control"
@@ -49,15 +50,17 @@
public class CallTarget extends Task {
private Ant callee;
- private String subTarget;
// must match the default value of Ant#inheritAll
private boolean inheritAll = true;
// must match the default value of Ant#inheritRefs
private boolean inheritRefs = false;
+ private boolean targetSet = false;
+
/**
* If true, pass all properties to the new Ant project.
* Defaults to true.
+ * @param inherit <code>boolean</code> flag.
*/
public void setInheritAll(boolean inherit) {
inheritAll = inherit;
@@ -65,16 +68,16 @@
/**
* If true, pass all references to the new Ant project.
- * Defaults to false
- * @param inheritRefs new value
+ * Defaults to false.
+ * @param inheritRefs <code>boolean</code> flag.
*/
public void setInheritRefs(boolean inheritRefs) {
this.inheritRefs = inheritRefs;
}
/**
- * init this task by creating new instance of the ant task and
- * configuring it's by calling its own init method.
+ * Initialize this task by creating new instance of the ant task and
+ * configuring it by calling its own init method.
*/
public void init() {
callee = (Ant) getProject().createTask("ant");
@@ -85,29 +88,28 @@
}
/**
- * hand off the work to the ant task of ours, after setting it up
+ * Delegate the work to the ant task instance, after setting it up.
* @throws BuildException on validation failure or if the target didn't
- * execute
+ * execute.
*/
public void execute() throws BuildException {
if (callee == null) {
init();
}
-
- if (subTarget == null) {
- throw new BuildException("Attribute target is required.",
- getLocation());
+ if (!targetSet) {
+ throw new BuildException(
+ "Attribute target or at least one nested target is
required.",
+ getLocation());
}
-
callee.setAntfile(getProject().getProperty("ant.file"));
- callee.setTarget(subTarget);
callee.setInheritAll(inheritAll);
callee.setInheritRefs(inheritRefs);
callee.execute();
}
/**
- * Property to pass to the invoked target.
+ * Create a new Property to pass to the invoked target(s).
+ * @return a <code>Property</code> object.
*/
public Property createParam() {
if (callee == null) {
@@ -119,6 +121,7 @@
/**
* Reference element identifying a data type to carry
* over to the invoked target.
+ * @param r the specified <code>Ant.Reference</code>.
* @since Ant 1.5
*/
public void addReference(Ant.Reference r) {
@@ -130,10 +133,10 @@
/**
* Set of properties to pass to the new project.
- *
+ * @param ps the <code>PropertySet</code> to pass.
* @since Ant 1.6
*/
- public void addPropertyset(org.apache.tools.ant.types.PropertySet ps) {
+ public void addPropertyset(PropertySet ps) {
if (callee == null) {
init();
}
@@ -141,14 +144,32 @@
}
/**
- * Target to execute, required.
+ * Set target to execute.
+ * @param target the name of the target to execute.
*/
public void setTarget(String target) {
- subTarget = target;
+ if (callee == null) {
+ init();
+ }
+ callee.setTarget(target);
+ targetSet = true;
+ }
+
+ /**
+ * Add a target to the list of targets to invoke.
+ * @param t <code>Ant.TargetElement</code> representing the target.
+ * @since Ant 1.6.3
+ */
+ public void addConfiguredTarget(Ant.TargetElement t) {
+ if (callee == null) {
+ init();
+ }
+ callee.addConfiguredTarget(t);
+ targetSet = true;
}
/**
- * Pass output sent to System.out to the new project.
+ * @see Task#handleOutput(String)
*
* @since Ant 1.5
*/
@@ -175,7 +196,7 @@
}
/**
- * Pass output sent to System.out to the new project.
+ * @see Task#handleFlush(String)
*
* @since Ant 1.5.2
*/
@@ -188,7 +209,7 @@
}
/**
- * Pass output sent to System.err to the new project.
+ * @see Task#handleErrorOutput(String)
*
* @since Ant 1.5
*/
@@ -201,7 +222,7 @@
}
/**
- * Pass output sent to System.err to the new project and flush stream.
+ * @see Task#handleErrorFlush(String)
*
* @since Ant 1.5.2
*/
No revision
No revision
1.19.2.5 +39 -1
ant/src/testcases/org/apache/tools/ant/taskdefs/AntTest.java
Index: AntTest.java
===================================================================
RCS file:
/home/cvs/ant/src/testcases/org/apache/tools/ant/taskdefs/AntTest.java,v
retrieving revision 1.19.2.4
retrieving revision 1.19.2.5
diff -u -r1.19.2.4 -r1.19.2.5
--- AntTest.java 9 Mar 2004 17:02:01 -0000 1.19.2.4
+++ AntTest.java 14 Dec 2004 23:48:01 -0000 1.19.2.5
@@ -275,6 +275,38 @@
expectLog("topleveltarget", "Hello world");
}
+ public void testMultiplePropertyFileChildren() {
+ PropertyChecker pcBar = new PropertyChecker("bar",
+ new String[] {null,
"Bar"});
+ PropertyChecker pcFoo = new PropertyChecker("foo",
+ new String[] {null,
"Foo"});
+ project.addBuildListener(pcBar);
+ project.addBuildListener(pcFoo);
+ executeTarget("multiple-property-file-children");
+ AssertionFailedError aeBar = pcBar.getError();
+ if (aeBar != null) {
+ throw aeBar;
+ }
+ AssertionFailedError aeFoo = pcFoo.getError();
+ if (aeFoo != null) {
+ throw aeFoo;
+ }
+ project.removeBuildListener(pcBar);
+ project.removeBuildListener(pcFoo);
+ }
+
+ public void testBlankTarget() {
+ expectBuildException("blank-target", "target name must not be
empty");
+ }
+
+ public void testMultipleTargets() {
+ expectLog("multiple-targets", "tadadctbdbtc");
+ }
+
+ public void testMultipleTargets2() {
+ expectLog("multiple-targets-2", "dadctb");
+ }
+
private class BasedirChecker implements BuildListener {
private String[] expectedBasedirs;
private int calls = 0;
@@ -450,6 +482,12 @@
if (event.getTarget().getName().equals("")) {
return;
}
+ if (calls >= expectedValues.length) {
+ error = new AssertionFailedError("Unexpected invocation of"
+ + " target "
+ +
event.getTarget().getName());
+ }
+
if (error == null) {
try {
assertEquals(expectedValues[calls++],
1.3.2.5 +12 -0
ant/src/testcases/org/apache/tools/ant/taskdefs/CallTargetTest.java
Index: CallTargetTest.java
===================================================================
RCS file:
/home/cvs/ant/src/testcases/org/apache/tools/ant/taskdefs/CallTargetTest.java,v
retrieving revision 1.3.2.4
retrieving revision 1.3.2.5
diff -u -r1.3.2.4 -r1.3.2.5
--- CallTargetTest.java 9 Mar 2004 17:02:01 -0000 1.3.2.4
+++ CallTargetTest.java 14 Dec 2004 23:48:01 -0000 1.3.2.5
@@ -55,6 +55,18 @@
assertLogContaining("multi is SETmulti is SET");
}
+ public void testBlankTarget() {
+ expectBuildException("blank-target", "target name must not be
empty");
+ }
+
+ public void testMultipleTargets() {
+ expectLog("multiple-targets", "tadadctbdbtc");
+ }
+
+ public void testMultipleTargets2() {
+ expectLog("multiple-targets-2", "dadctb");
+ }
+
public void tearDown() {
project.executeTarget("cleanup");
}
No revision
No revision
1.18.2.8 +28 -3 ant/docs/manual/CoreTasks/ant.html
Index: ant.html
===================================================================
RCS file: /home/cvs/ant/docs/manual/CoreTasks/ant.html,v
retrieving revision 1.18.2.7
retrieving revision 1.18.2.8
diff -u -r1.18.2.7 -r1.18.2.8
--- ant.html 19 Nov 2004 09:10:00 -0000 1.18.2.7
+++ ant.html 14 Dec 2004 23:48:01 -0000 1.18.2.8
@@ -13,7 +13,7 @@
<p>Runs Ant on a supplied buildfile. This can be used to build
subprojects. <strong>This task must not be used outside of a
-<code>target</code> if it invoces the same build file it is part
+<code>target</code> if it invokes the same build file it is part
of.</strong></p>
<p>When the <i>antfile</i> attribute is omitted, the file
"build.xml"
@@ -34,7 +34,7 @@
to the new project and any project created in that project
regardless of the setting of <i>inheritAll</i>. This allows you to
parameterize your subprojects. Properties defined on the command line
-can not be overridden by nested <code><property></code> elements.</p>
+cannot be overridden by nested <code><property></code> elements.</p>
<p>References to data types can also be passed to the new project, but
by default they are not. If you set the inheritrefs attribute to
@@ -103,7 +103,12 @@
<h4>property</h4>
<p>See the description of the <a href="property.html">property
-task</a>. Note that the <code>refid</code> attribute points to a
+task</a>. <br/>
+These properties become equivalent to properties you define on
+the command line. These are special properties and they will always get
passed
+down, even through additional <code><*ant*></code> tasks with
inheritall set to
+false (see above). <br/>
+Note that the <code>refid</code> attribute points to a
reference in the calling project, not in the new one.</p>
<h4><a name="reference">reference</a></h4>
@@ -135,6 +140,26 @@
href="../CoreTypes/propertyset.html">propertyset</a>s.</p>
<p><em>since Ant 1.6</em>.</p>
+
+<h4>target</h4>
+
+<p>You can specify multiple targets using nested <code><target></code>
elements
+instead of using the target attribute. These will be executed as if
+Ant had been invoked with a single target whose dependencies are the
+targets so specified, in the order specified.</p>
+<table border="1" cellpadding="2" cellspacing="0">
+ <tr>
+ <td valign="top"><b>Attribute</b></td>
+ <td valign="top"><b>Description</b></td>
+ <td align="center" valign="top"><b>Required</b></td>
+ </tr>
+ <tr>
+ <td valign="top">name</td>
+ <td valign="top">The name of the called target.</td>
+ <td valign="top" align="center">Yes</td>
+ </tr>
+</table>
+<p><em>since Ant 1.6.3</em>.</p>
<h3>Basedir of the new project</h3>
1.18.2.8 +30 -5 ant/docs/manual/CoreTasks/antcall.html
Index: antcall.html
===================================================================
RCS file: /home/cvs/ant/docs/manual/CoreTasks/antcall.html,v
retrieving revision 1.18.2.7
retrieving revision 1.18.2.8
diff -u -r1.18.2.7 -r1.18.2.8
--- antcall.html 19 Nov 2004 09:10:00 -0000 1.18.2.7
+++ antcall.html 14 Dec 2004 23:48:01 -0000 1.18.2.8
@@ -11,9 +11,9 @@
<h2><a name="antcall">AntCall</a></h2>
<h3>Description</h3>
-<p>Call another target within the same build-file optionally
-specifying some properties (param's in this context). <strong>This
-task must no be used outside of a <code>target</code>.</strong></p>
+<p>Call another target within the same buildfile optionally
+specifying some properties (params in this context). <strong>This
+task must not be used outside of a <code>target</code>.</strong></p>
<p>By default, all of the properties of the current project will be
available in the new project. Alternatively, you can
@@ -42,7 +42,7 @@
the target "doSomethingElse" depended on the target
"init", then the
<i>antcall</i> of "doSomethingElse" will call "init"
during the call.
Of course, any properties defined in the antcall task or inherited from the
calling target
-will be fixed and not overridable in the init task -or indeed in the
"doSomethingElse" task.
+will be fixed and not overridable in the init task--or indeed in the
"doSomethingElse" task.
</p>
<p>If the build file changes after you've started the build, the
@@ -89,7 +89,12 @@
<h3>Parameters specified as nested elements</h3>
<h4>param</h4>
<p>Specifies the properties to set before running the specified target. See
<a
-href="property.html">property</a> for usage guidelines.</p>
+href="property.html">property</a> for usage guidelines.<br/>
+These properties become equivalent to properties you define on
+the command line. These are special properties and they will always get
passed
+down, even through additional <code><*ant*></code> tasks with
inheritall set to
+false (see above).
+</p>
<h4><a name="reference">reference</a></h4>
<p>Used to choose references that shall be copied into the new project,
@@ -120,6 +125,26 @@
href="../CoreTypes/propertyset.html">propertyset</a>s.</p>
<p><em>since Ant 1.6</em>.</p>
+
+<h4>target</h4>
+
+<p>You can specify multiple targets using nested <code><target></code>
elements
+instead of using the target attribute. These will be executed as if
+Ant had been invoked with a single target whose dependencies are the
+targets so specified, in the order specified.</p>
+<table border="1" cellpadding="2" cellspacing="0">
+ <tr>
+ <td valign="top"><b>Attribute</b></td>
+ <td valign="top"><b>Description</b></td>
+ <td align="center" valign="top"><b>Required</b></td>
+ </tr>
+ <tr>
+ <td valign="top">name</td>
+ <td valign="top">The name of the called target.</td>
+ <td valign="top" align="center">Yes</td>
+ </tr>
+</table>
+<p><em>since Ant 1.6.3</em>.</p>
<h3>Examples</h3>
<pre>
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]