Browse Source

Added multiple targets to <ant> and <antcall> using nested <target> elements.

PR: 5270, 8148, 17071, 6368, 29623


git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@276629 13f79535-47bb-0310-9956-ffa450edef68
master
Matthew Jason Benson 21 years ago
parent
commit
c672e8778e
10 changed files with 253 additions and 30 deletions
  1. +4
    -0
      WHATSNEW
  2. +20
    -0
      docs/manual/CoreTasks/ant.html
  3. +20
    -0
      docs/manual/CoreTasks/antcall.html
  4. +22
    -0
      src/etc/testcases/taskdefs/ant.xml
  5. +24
    -1
      src/etc/testcases/taskdefs/calltarget.xml
  6. +40
    -7
      src/main/org/apache/tools/ant/Project.java
  7. +83
    -16
      src/main/org/apache/tools/ant/taskdefs/Ant.java
  8. +24
    -6
      src/main/org/apache/tools/ant/taskdefs/CallTarget.java
  9. +8
    -0
      src/testcases/org/apache/tools/ant/taskdefs/AntTest.java
  10. +8
    -0
      src/testcases/org/apache/tools/ant/taskdefs/CallTargetTest.java

+ 4
- 0
WHATSNEW View File

@@ -21,6 +21,10 @@ Other changes:
* A new base class DispatchTask has been added to facilitate elegant
creation of tasks with multiple actions.

* Added <target> nested elements to <ant> and <antcall> to allow
specification of multiple sub-build targets, which are executed
with a single dependency analysis.

Changes from Ant 1.6.1 to current Ant 1.6 CVS version
=====================================================



+ 20
- 0
docs/manual/CoreTasks/ant.html View File

@@ -135,6 +135,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 &lt;target&gt; 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.2</em>.</p>

<h3>Basedir of the new project</h3>

<p>The basedir value of the new project is affected by the two


+ 20
- 0
docs/manual/CoreTasks/antcall.html View File

@@ -120,6 +120,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 &lt;target&gt; 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.2</em>.</p>

<h3>Examples</h3>
<pre>
&lt;target name=&quot;default&quot;&gt;


+ 22
- 0
src/etc/testcases/taskdefs/ant.xml View File

@@ -192,4 +192,26 @@
</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="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>

+ 24
- 1
src/etc/testcases/taskdefs/calltarget.xml View File

@@ -50,4 +50,27 @@
<param name="multi" value="SET"/>
</antcall>
</target>
</project>

<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="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>

+ 40
- 7
src/main/org/apache/tools/ant/Project.java View File

@@ -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 <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();
@@ -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 <CODE>String[]</CODE> 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 <code>null</code>.
* @param targets A map of names to targets (String to Target).
* Must not be <code>null</code>.
* @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);


+ 83
- 16
src/main/org/apache/tools/ant/taskdefs/Ant.java View File

@@ -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 <CODE>TargetElement</CODE> 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 &lt;target&gt;
* element of &lt;ant&gt; and &lt;antcall&gt;.
* @since Ant 1.7
*/
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;
}
}
}

+ 24
- 6
src/main/org/apache/tools/ant/taskdefs/CallTarget.java View File

@@ -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;
}

/**


+ 8
- 0
src/testcases/org/apache/tools/ant/taskdefs/AntTest.java View File

@@ -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;


+ 8
- 0
src/testcases/org/apache/tools/ant/taskdefs/CallTargetTest.java View File

@@ -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");
}


Loading…
Cancel
Save