From c672e8778ed4d0f929d9bb413532a4b2e421fa16 Mon Sep 17 00:00:00 2001 From: Matthew Jason Benson Date: Thu, 24 Jun 2004 19:30:03 +0000 Subject: [PATCH] Added multiple targets to and using nested 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 --- WHATSNEW | 4 + docs/manual/CoreTasks/ant.html | 20 ++++ docs/manual/CoreTasks/antcall.html | 20 ++++ src/etc/testcases/taskdefs/ant.xml | 22 +++++ src/etc/testcases/taskdefs/calltarget.xml | 25 ++++- src/main/org/apache/tools/ant/Project.java | 47 +++++++-- .../org/apache/tools/ant/taskdefs/Ant.java | 99 ++++++++++++++++--- .../apache/tools/ant/taskdefs/CallTarget.java | 30 ++++-- .../apache/tools/ant/taskdefs/AntTest.java | 8 ++ .../tools/ant/taskdefs/CallTargetTest.java | 8 ++ 10 files changed, 253 insertions(+), 30 deletions(-) diff --git a/WHATSNEW b/WHATSNEW index 414441ada..088cec43c 100644 --- a/WHATSNEW +++ b/WHATSNEW @@ -21,6 +21,10 @@ Other changes: * A new base class DispatchTask has been added to facilitate elegant creation of tasks with multiple actions. +* Added nested elements to and 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 ===================================================== diff --git a/docs/manual/CoreTasks/ant.html b/docs/manual/CoreTasks/ant.html index 2924453a3..13e404c0c 100644 --- a/docs/manual/CoreTasks/ant.html +++ b/docs/manual/CoreTasks/ant.html @@ -135,6 +135,26 @@ href="../CoreTypes/propertyset.html">propertysets.

since Ant 1.6.

+

target

+ +

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.

+ + + + + + + + + + + +
AttributeDescriptionRequired
nameThe name of the called target.Yes
+

since Ant 1.6.2.

+

Basedir of the new project

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.

+

target

+ +

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.

+ + + + + + + + + + + +
AttributeDescriptionRequired
nameThe name of the called target.Yes
+

since Ant 1.6.2.

+

Examples

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