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 + 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 + + dc + + ++ ++ + + ++ ++ + + + ta + tb + + tc + da + db + + 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 dc Vector
of sorted targets. + * @param sortedTargets the aforementionedVector
. + */ + 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 rootString[]
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 benull
. + * @param targets A map of names to targets (String to Target). + * Must not benull
. + * @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 theTargetElement
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 theString
target name. + */ + public void setName(String name) { + this.name = name; + } + + /** + * Get the name of this TargetElement. + * @returnString
. + */ + 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"); }