From c672e8778ed4d0f929d9bb413532a4b2e421fa16 Mon Sep 17 00:00:00 2001
From: Matthew Jason Benson
since Ant 1.6.
+You can specify multiple targets using nested <target> 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.
+| Attribute | +Description | +Required | +
| name | +The name of the called target. | +Yes | +
since Ant 1.6.2.
+The basedir value of the new project is affected by the two diff --git a/docs/manual/CoreTasks/antcall.html b/docs/manual/CoreTasks/antcall.html index c8f8657b2..314759256 100644 --- a/docs/manual/CoreTasks/antcall.html +++ b/docs/manual/CoreTasks/antcall.html @@ -120,6 +120,26 @@ href="../CoreTypes/propertyset.html">propertysets.
since Ant 1.6.
+You can specify multiple targets using nested <target> 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.
+| Attribute | +Description | +Required | +
| name | +The name of the called target. | +Yes | +
since Ant 1.6.2.
+
<target name="default">
diff --git a/src/etc/testcases/taskdefs/ant.xml b/src/etc/testcases/taskdefs/ant.xml
index bf4357b64..aa69d6488 100644
--- a/src/etc/testcases/taskdefs/ant.xml
+++ b/src/etc/testcases/taskdefs/ant.xml
@@ -192,4 +192,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ta
+ tb
+ tc
+
+ da
+ db
+ dc
+
diff --git a/src/etc/testcases/taskdefs/calltarget.xml b/src/etc/testcases/taskdefs/calltarget.xml
index 380432ee1..17c9f0574 100644
--- a/src/etc/testcases/taskdefs/calltarget.xml
+++ b/src/etc/testcases/taskdefs/calltarget.xml
@@ -50,4 +50,27 @@
-
\ No newline at end of file
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ta
+ tb
+ tc
+
+ da
+ db
+ dc
+
+
diff --git a/src/main/org/apache/tools/ant/Project.java b/src/main/org/apache/tools/ant/Project.java
index 7e66c7608..82b3a9f03 100644
--- a/src/main/org/apache/tools/ant/Project.java
+++ b/src/main/org/apache/tools/ant/Project.java
@@ -1187,7 +1187,16 @@ public class Project {
// exist, and if there is any cycle in the dependency
// graph.
Vector sortedTargets = topoSort(targetName, targets);
+ sortedTargets.setSize(sortedTargets.indexOf(targets.get(targetName)) + 1);
+ executeSortedTargets(sortedTargets);
+ }
+ /**
+ * Executes a Vector of sorted targets.
+ * @param sortedTargets the aforementioned Vector.
+ */
+ public void executeSortedTargets(Vector sortedTargets)
+ throws BuildException {
Set succeededTargets = new HashSet();
BuildException buildException = null; // first build exception
for (Enumeration iter = sortedTargets.elements();
@@ -1245,9 +1254,6 @@ public class Project {
}
}
}
- if (curtarget.getName().equals(targetName)) { // old exit condition
- break;
- }
}
if (buildException != null) {
throw buildException;
@@ -1564,21 +1570,48 @@ public class Project {
* 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);
+ }
+
+ /**
+ * Topologically sorts a set of targets.
+ *
+ * @param root String[] containing the names of the root targets.
+ * 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 null.
+ * @param targets A map of names to targets (String to Target).
+ * Must not be null.
+ * @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.
+ */
+ public final Vector topoSort(String[] root, Hashtable targets)
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++) {
+ tsort(root[i], targets, state, visiting, ret);
+ }
+ 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);
+
for (Enumeration en = targets.keys(); en.hasMoreElements();) {
String curTarget = (String) en.nextElement();
String st = (String) state.get(curTarget);
diff --git a/src/main/org/apache/tools/ant/taskdefs/Ant.java b/src/main/org/apache/tools/ant/taskdefs/Ant.java
index 4d9131386..3a06830a6 100644
--- a/src/main/org/apache/tools/ant/taskdefs/Ant.java
+++ b/src/main/org/apache/tools/ant/taskdefs/Ant.java
@@ -71,9 +71,6 @@ public class Ant extends Task {
*/
private String antFile = null;
- /** the target to call if any */
- private String target = null;
-
/** the output */
private String output = null;
@@ -98,6 +95,12 @@ public class Ant extends Task {
/** 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.
@@ -285,7 +288,7 @@ public class Ant extends Task {
public void execute() throws BuildException {
File savedDir = dir;
String savedAntFile = antFile;
- String savedTarget = target;
+ Vector locals = new Vector(targets);
try {
if (newProject == null) {
reinit();
@@ -317,8 +320,9 @@ public class Ant extends Task {
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");
@@ -348,8 +352,11 @@ public class Ant extends Task {
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")
@@ -358,13 +365,18 @@ public class Ant extends Task {
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"
@@ -377,12 +389,20 @@ public class Ant extends Task {
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);
+ String[] nameArray =
+ (String[])(locals.toArray(new String[locals.size()]));
+
+ Hashtable targets = newProject.getTargets();
+ Vector sortedTargets = newProject.topoSort(nameArray, targets);
+
+ sortedTargets.setSize(sortedTargets.indexOf(targets.get(
+ locals.lastElement())) + 1);
+ newProject.executeSortedTargets(sortedTargets);
} catch (BuildException ex) {
t = ProjectHelper
.addLocationToBuildException(ex, getLocation());
@@ -410,7 +430,6 @@ public class Ant extends Task {
}
dir = savedDir;
antFile = savedAntFile;
- target = savedTarget;
}
}
@@ -601,7 +620,8 @@ public class Ant extends Task {
throw new BuildException("target attribute must not be empty");
}
- this.target = s;
+ targets.add(s);
+ targetAttributeSet = true;
}
/**
@@ -640,6 +660,23 @@ public class Ant extends Task {
references.addElement(r);
}
+ /**
+ * Add a target to this Ant invocation.
+ * @param target the TargetElement to add.
+ * @since Ant 1.7
+ */
+ 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);
+ }
+
/**
* Set of properties to pass to the new project.
*
@@ -691,4 +728,34 @@ public class Ant extends Task {
return targetid;
}
}
+
+ /**
+ * Helper class that implements the nested <target>
+ * element of <ant> and <antcall>.
+ * @since Ant 1.7
+ */
+ public static class TargetElement {
+ private String name;
+
+ /**
+ * Default constructor.
+ */
+ public TargetElement() {}
+
+ /**
+ * Set the name of this TargetElement.
+ * @param name the String target name.
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Get the name of this TargetElement.
+ * @return String.
+ */
+ public String getName() {
+ return name;
+ }
+ }
}
diff --git a/src/main/org/apache/tools/ant/taskdefs/CallTarget.java b/src/main/org/apache/tools/ant/taskdefs/CallTarget.java
index abf108063..a0e17f971 100644
--- a/src/main/org/apache/tools/ant/taskdefs/CallTarget.java
+++ b/src/main/org/apache/tools/ant/taskdefs/CallTarget.java
@@ -48,12 +48,13 @@ import java.io.IOException;
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.
@@ -93,13 +94,13 @@ public class CallTarget extends Task {
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();
@@ -143,7 +144,24 @@ public class CallTarget extends Task {
* Target to execute, required.
*/
public void setTarget(String target) {
- subTarget = target;
+ if (callee == null) {
+ init();
+ }
+ callee.setTarget(target);
+ targetSet = true;
+ }
+
+ /**
+ * Target element identifying a data type to carry
+ * over to the invoked target.
+ * @since Ant 1.6.2
+ */
+ public void addConfiguredTarget(Ant.TargetElement t) {
+ if (callee == null) {
+ init();
+ }
+ callee.addConfiguredTarget(t);
+ targetSet = true;
}
/**
diff --git a/src/testcases/org/apache/tools/ant/taskdefs/AntTest.java b/src/testcases/org/apache/tools/ant/taskdefs/AntTest.java
index c144d311a..23c67f98e 100644
--- a/src/testcases/org/apache/tools/ant/taskdefs/AntTest.java
+++ b/src/testcases/org/apache/tools/ant/taskdefs/AntTest.java
@@ -295,6 +295,14 @@ public class AntTest extends BuildFileTest {
project.removeBuildListener(pcFoo);
}
+ public void testBlankTarget() {
+ expectBuildException("blank-target", "target name must not be empty");
+ }
+
+ public void testMultipleTargets() {
+ expectLog("multiple-targets", "tadadctbdbtc");
+ }
+
private class BasedirChecker implements BuildListener {
private String[] expectedBasedirs;
private int calls = 0;
diff --git a/src/testcases/org/apache/tools/ant/taskdefs/CallTargetTest.java b/src/testcases/org/apache/tools/ant/taskdefs/CallTargetTest.java
index 433c33e6c..bd2d07a99 100644
--- a/src/testcases/org/apache/tools/ant/taskdefs/CallTargetTest.java
+++ b/src/testcases/org/apache/tools/ant/taskdefs/CallTargetTest.java
@@ -55,6 +55,14 @@ public class CallTargetTest extends BuildFileTest {
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 tearDown() {
project.executeTarget("cleanup");
}