git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@676095 13f79535-47bb-0310-9956-ffa450edef68master
| @@ -153,6 +153,8 @@ Other changes: | |||
| a build if a warning occurs. | |||
| Bugzilla Report 41836. | |||
| * Ant now supports local properties. Bugzilla report 23942. | |||
| Changes from Ant 1.7.0 TO Ant 1.7.1 | |||
| ============================================= | |||
| @@ -0,0 +1,53 @@ | |||
| <!-- | |||
| Licensed to the Apache Software Foundation (ASF) under one or more | |||
| contributor license agreements. See the NOTICE file distributed with | |||
| this work for additional information regarding copyright ownership. | |||
| The ASF licenses this file to You under the Apache License, Version 2.0 | |||
| (the "License"); you may not use this file except in compliance with | |||
| the License. You may obtain a copy of the License at | |||
| http://www.apache.org/licenses/LICENSE-2.0 | |||
| Unless required by applicable law or agreed to in writing, software | |||
| distributed under the License is distributed on an "AS IS" BASIS, | |||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| See the License for the specific language governing permissions and | |||
| limitations under the License. | |||
| --> | |||
| <html> | |||
| <head> | |||
| <meta http-equiv="Content-Language" content="en-us"> | |||
| <link rel="stylesheet" type="text/css" href="../stylesheets/style.css"> | |||
| <title>Local Task</title> | |||
| </head> | |||
| <body> | |||
| <h2>Local</h2> | |||
| <h3>Description</h3> | |||
| <p>Adds a local property to the current scope. Property scopes exist at Ant's | |||
| various "block" levels. These include targets as well as the | |||
| <a href="parallel.html">Parallel</a> and <a href="sequential.html">Sequential</a> | |||
| task containers (including <a href="macrodef.html">Macrodef</a> bodies). A local | |||
| property at a given scope "shadows" properties of the same name at higher scopes, | |||
| including the global scope (declaring a local property at the global level, i.e. | |||
| outside of any scope block, has no effect). <b>Since Ant 1.8</b></p> | |||
| <h3>Parameters</h3> | |||
| <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 property to declare in the current scope</td> | |||
| <td valign="top" align="center">Yes</td> | |||
| </tr> | |||
| </table> | |||
| </body> | |||
| </html> | |||
| @@ -86,6 +86,7 @@ | |||
| <a href="CoreTasks/loadfile.html">LoadFile</a><br/> | |||
| <a href="CoreTasks/loadproperties.html">LoadProperties</a><br/> | |||
| <a href="CoreTasks/loadresource.html">LoadResource</a><br/> | |||
| <a href="CoreTasks/local.html">Local</a><br/> | |||
| <a href="CoreTasks/makeurl.html">MakeURL</a><br/> | |||
| <a href="CoreTasks/mail.html">Mail</a><br/> | |||
| <a href="CoreTasks/macrodef.html">MacroDef</a><br/> | |||
| @@ -174,6 +174,12 @@ public final class MagicNames { | |||
| */ | |||
| public static final String REFID_PROPERTY_HELPER = "ant.PropertyHelper"; | |||
| /** | |||
| * Reference used to store the local properties. | |||
| * Value: {@value} | |||
| */ | |||
| public static final String REFID_LOCAL_PROPERTIES = "ant.LocalProperties"; | |||
| /** | |||
| * Name of JVM system property which provides the name of the ProjectHelper class to use. | |||
| * Value: {@value} | |||
| @@ -25,6 +25,8 @@ import java.util.Iterator; | |||
| import java.util.List; | |||
| import java.util.StringTokenizer; | |||
| import org.apache.tools.ant.property.LocalProperties; | |||
| /** | |||
| * Class to implement a target object with required parameters. | |||
| * | |||
| @@ -347,14 +349,22 @@ public class Target implements TaskContainer { | |||
| */ | |||
| public void execute() throws BuildException { | |||
| if (testIfCondition() && testUnlessCondition()) { | |||
| for (int taskPosition = 0; taskPosition < children.size(); ++taskPosition) { | |||
| Object o = children.get(taskPosition); | |||
| if (o instanceof Task) { | |||
| Task task = (Task) o; | |||
| task.perform(); | |||
| } else { | |||
| ((RuntimeConfigurable) o).maybeConfigure(project); | |||
| LocalProperties localProperties | |||
| = LocalProperties.get(getProject()); | |||
| localProperties.enterScope(); | |||
| try { | |||
| for (int taskPosition = 0; taskPosition < children.size(); | |||
| ++taskPosition) { | |||
| Object o = children.get(taskPosition); | |||
| if (o instanceof Task) { | |||
| Task task = (Task) o; | |||
| task.perform(); | |||
| } else { | |||
| ((RuntimeConfigurable) o).maybeConfigure(project); | |||
| } | |||
| } | |||
| } finally { | |||
| localProperties.exitScope(); | |||
| } | |||
| } else if (!testIfCondition()) { | |||
| project.log(this, "Skipped because property '" + project.replaceProperties(ifCondition) | |||
| @@ -0,0 +1,152 @@ | |||
| /* | |||
| * Licensed to the Apache Software Foundation (ASF) under one or more | |||
| * contributor license agreements. See the NOTICE file distributed with | |||
| * this work for additional information regarding copyright ownership. | |||
| * The ASF licenses this file to You under the Apache License, Version 2.0 | |||
| * (the "License"); you may not use this file except in compliance with | |||
| * the License. You may obtain a copy of the License at | |||
| * | |||
| * http://www.apache.org/licenses/LICENSE-2.0 | |||
| * | |||
| * Unless required by applicable law or agreed to in writing, software | |||
| * distributed under the License is distributed on an "AS IS" BASIS, | |||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| * See the License for the specific language governing permissions and | |||
| * limitations under the License. | |||
| * | |||
| */ | |||
| package org.apache.tools.ant.property; | |||
| import org.apache.tools.ant.PropertyHelper; | |||
| import org.apache.tools.ant.Project; | |||
| import org.apache.tools.ant.MagicNames; | |||
| /** | |||
| * Thread local class containing local properties. | |||
| * @since Ant 1.8.0 | |||
| */ | |||
| public class LocalProperties | |||
| extends InheritableThreadLocal | |||
| implements PropertyHelper.PropertyEvaluator, | |||
| PropertyHelper.PropertySetter { | |||
| /** | |||
| * Get a localproperties for the given project. | |||
| * @param project the project to retieve the localproperties for. | |||
| * @return the localproperties. | |||
| */ | |||
| public static synchronized LocalProperties get(Project project) { | |||
| LocalProperties l = (LocalProperties) project.getReference( | |||
| MagicNames.REFID_LOCAL_PROPERTIES); | |||
| if (l == null) { | |||
| l = new LocalProperties(); | |||
| project.addReference(MagicNames.REFID_LOCAL_PROPERTIES, l); | |||
| PropertyHelper.getPropertyHelper(project).add(l); | |||
| } | |||
| return l; | |||
| } | |||
| // -------------------------------------------------- | |||
| // | |||
| // Thread stuff | |||
| // | |||
| // -------------------------------------------------- | |||
| /** | |||
| * Construct a new LocalProperties object. | |||
| */ | |||
| private LocalProperties() { | |||
| } | |||
| /** | |||
| * Get the initial value. | |||
| * @return a new localproperties stack. | |||
| */ | |||
| protected synchronized Object initialValue() { | |||
| return new LocalPropertyStack(); | |||
| } | |||
| private LocalPropertyStack current() { | |||
| return (LocalPropertyStack) get(); | |||
| } | |||
| // -------------------------------------------------- | |||
| // | |||
| // Local property adding and scoping | |||
| // | |||
| // -------------------------------------------------- | |||
| /** | |||
| * Add a local property to the current scope. | |||
| * @param property the property name to add. | |||
| */ | |||
| public void addLocal(String property) { | |||
| current().addLocal(property); | |||
| } | |||
| /** enter the scope */ | |||
| public void enterScope() { | |||
| current().enterScope(); | |||
| } | |||
| /** exit the scope */ | |||
| public void exitScope() { | |||
| current().exitScope(); | |||
| } | |||
| // -------------------------------------------------- | |||
| // | |||
| // Copy - used in parallel to make a new stack | |||
| // | |||
| // -------------------------------------------------- | |||
| /** | |||
| * Copy the stack for a parallel thread. | |||
| * To be called from the parallel thread itself. | |||
| */ | |||
| public void copy() { | |||
| set(current().copy()); | |||
| } | |||
| // -------------------------------------------------- | |||
| // | |||
| // PropertyHelper delegate methods | |||
| // | |||
| // -------------------------------------------------- | |||
| /** | |||
| * Evaluate a property. | |||
| * @param property the property's String "identifier". | |||
| * @param helper the invoking PropertyHelper. | |||
| * @return Object value. | |||
| */ | |||
| public Object evaluate(String property, PropertyHelper helper) { | |||
| return current().evaluate(property, helper); | |||
| } | |||
| /** | |||
| * Set a *new" property. | |||
| * @param property the property's String "identifier". | |||
| * @param value the value to set. | |||
| * @param propertyHelper the invoking PropertyHelper. | |||
| * @return true if this entity 'owns' the property. | |||
| */ | |||
| public boolean setNew( | |||
| String property, Object value, PropertyHelper propertyHelper) { | |||
| return current().setNew(property, value, propertyHelper); | |||
| } | |||
| /** | |||
| * Set a property. | |||
| * @param property the property's String "identifier". | |||
| * @param value the value to set. | |||
| * @param propertyHelper the invoking PropertyHelper. | |||
| * @return true if this entity 'owns' the property. | |||
| */ | |||
| public boolean set( | |||
| String property, Object value, PropertyHelper propertyHelper) { | |||
| return current().set(property, value, propertyHelper); | |||
| } | |||
| } | |||
| @@ -0,0 +1,151 @@ | |||
| /* | |||
| * Licensed to the Apache Software Foundation (ASF) under one or more | |||
| * contributor license agreements. See the NOTICE file distributed with | |||
| * this work for additional information regarding copyright ownership. | |||
| * The ASF licenses this file to You under the Apache License, Version 2.0 | |||
| * (the "License"); you may not use this file except in compliance with | |||
| * the License. You may obtain a copy of the License at | |||
| * | |||
| * http://www.apache.org/licenses/LICENSE-2.0 | |||
| * | |||
| * Unless required by applicable law or agreed to in writing, software | |||
| * distributed under the License is distributed on an "AS IS" BASIS, | |||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| * See the License for the specific language governing permissions and | |||
| * limitations under the License. | |||
| * | |||
| */ | |||
| package org.apache.tools.ant.property; | |||
| import java.util.LinkedList; | |||
| import java.util.HashMap; | |||
| import java.util.Iterator; | |||
| import java.util.Map; | |||
| import org.apache.tools.ant.PropertyHelper; | |||
| /** | |||
| * A stack of local property maps. | |||
| * There is a map for each scope (target, sequential, macro). | |||
| * @since Ant 1.8.0 | |||
| */ | |||
| public class LocalPropertyStack { | |||
| private LinkedList stack = new LinkedList(); | |||
| // -------------------------------------------------- | |||
| // | |||
| // Local property adding and scoping | |||
| // | |||
| // -------------------------------------------------- | |||
| /** | |||
| * Add a local property. | |||
| * @param property the name of the local proeprty. | |||
| */ | |||
| public void addLocal(String property) { | |||
| if (!stack.isEmpty()) { | |||
| ((Map) stack.getFirst()).put(property, NullReturn.NULL); | |||
| } | |||
| } | |||
| /** | |||
| * Enter the local scope. | |||
| */ | |||
| public void enterScope() { | |||
| stack.addFirst(new HashMap()); | |||
| } | |||
| /** | |||
| * Exit the local scope. | |||
| */ | |||
| public void exitScope() { | |||
| ((HashMap) stack.removeFirst()).clear(); | |||
| } | |||
| // -------------------------------------------------- | |||
| // | |||
| // Copy - used in parallel to make a new stack | |||
| // | |||
| // -------------------------------------------------- | |||
| /** | |||
| * Copy the stack for a parallel thread. | |||
| * @return a copy. | |||
| */ | |||
| public LocalPropertyStack copy() { | |||
| LocalPropertyStack ret = new LocalPropertyStack(); | |||
| ret.stack.addAll(stack); | |||
| return ret; | |||
| } | |||
| // -------------------------------------------------- | |||
| // | |||
| // PropertyHelper delegate methods | |||
| // | |||
| // -------------------------------------------------- | |||
| /** | |||
| * Evaluate a property. | |||
| * @param property the property's String "identifier". | |||
| * @param helper the invoking PropertyHelper. | |||
| * @return Object value. | |||
| */ | |||
| public Object evaluate(String property, PropertyHelper helper) { | |||
| for (Iterator i = stack.iterator(); i.hasNext();) { | |||
| Map map = (Map) i.next(); | |||
| Object ret = map.get(property); | |||
| if (ret != null) { | |||
| return ret; | |||
| } | |||
| } | |||
| return null; | |||
| } | |||
| /** | |||
| * Set a *new" property. | |||
| * @param property the property's String "identifier". | |||
| * @param value the value to set. | |||
| * @param propertyHelper the invoking PropertyHelper. | |||
| * @return true if this entity 'owns' the property. | |||
| */ | |||
| public boolean setNew( | |||
| String property, Object value, PropertyHelper propertyHelper) { | |||
| Map map = getMapForProperty(property); | |||
| if (map == null) { | |||
| return false; | |||
| } | |||
| Object currValue = map.get(property); | |||
| if (currValue == NullReturn.NULL) { | |||
| map.put(property, value); | |||
| } | |||
| return true; | |||
| } | |||
| /** | |||
| * Set a property. | |||
| * @param property the property's String "identifier". | |||
| * @param value the value to set. | |||
| * @param propertyHelper the invoking PropertyHelper. | |||
| * @return true if this entity 'owns' the property. | |||
| */ | |||
| public boolean set(String property, Object value, PropertyHelper propertyHelper) { | |||
| Map map = getMapForProperty(property); | |||
| if (map == null) { | |||
| return false; | |||
| } | |||
| map.put(property, value); | |||
| return true; | |||
| } | |||
| private Map getMapForProperty(String property) { | |||
| for (Iterator i = stack.iterator(); i.hasNext();) { | |||
| Map map = (Map) i.next(); | |||
| if (map.get(property) != null) { | |||
| return map; | |||
| } | |||
| } | |||
| return null; | |||
| } | |||
| } | |||
| @@ -0,0 +1,47 @@ | |||
| /* | |||
| * Licensed to the Apache Software Foundation (ASF) under one or more | |||
| * contributor license agreements. See the NOTICE file distributed with | |||
| * this work for additional information regarding copyright ownership. | |||
| * The ASF licenses this file to You under the Apache License, Version 2.0 | |||
| * (the "License"); you may not use this file except in compliance with | |||
| * the License. You may obtain a copy of the License at | |||
| * | |||
| * http://www.apache.org/licenses/LICENSE-2.0 | |||
| * | |||
| * Unless required by applicable law or agreed to in writing, software | |||
| * distributed under the License is distributed on an "AS IS" BASIS, | |||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| * See the License for the specific language governing permissions and | |||
| * limitations under the License. | |||
| * | |||
| */ | |||
| package org.apache.tools.ant.taskdefs; | |||
| import org.apache.tools.ant.BuildException; | |||
| import org.apache.tools.ant.Task; | |||
| import org.apache.tools.ant.property.LocalProperties; | |||
| /** | |||
| * Task to create a local property in the current scope. | |||
| */ | |||
| public class Local extends Task { | |||
| private String name; | |||
| /** | |||
| * Set the name attribute. | |||
| * @param name the name of the local property. | |||
| */ | |||
| public void setName(String name) { | |||
| this.name = name; | |||
| } | |||
| /** | |||
| * Run the task. | |||
| */ | |||
| public void execute() { | |||
| if (name == null) { | |||
| throw new BuildException("Missing attribute name"); | |||
| } | |||
| LocalProperties.get(getProject()).addLocal(name); | |||
| } | |||
| } | |||
| @@ -37,6 +37,7 @@ import org.apache.tools.ant.Target; | |||
| import org.apache.tools.ant.Task; | |||
| import org.apache.tools.ant.TaskContainer; | |||
| import org.apache.tools.ant.UnknownElement; | |||
| import org.apache.tools.ant.property.LocalProperties; | |||
| /** | |||
| * The class to be placed in the ant type definition. | |||
| @@ -390,6 +391,9 @@ public class MacroInstance extends Task implements DynamicAttribute, TaskContain | |||
| // need to set the project on unknown element | |||
| UnknownElement c = copy(macroDef.getNestedTask(), false); | |||
| c.init(); | |||
| LocalProperties localProperties | |||
| = LocalProperties.get(getProject()); | |||
| localProperties.enterScope(); | |||
| try { | |||
| c.perform(); | |||
| } catch (BuildException ex) { | |||
| @@ -403,6 +407,7 @@ public class MacroInstance extends Task implements DynamicAttribute, TaskContain | |||
| } finally { | |||
| presentElements = null; | |||
| localAttributes = null; | |||
| localProperties.exitScope(); | |||
| } | |||
| } | |||
| } | |||
| @@ -26,6 +26,7 @@ import org.apache.tools.ant.BuildException; | |||
| import org.apache.tools.ant.Location; | |||
| import org.apache.tools.ant.Task; | |||
| import org.apache.tools.ant.TaskContainer; | |||
| import org.apache.tools.ant.property.LocalProperties; | |||
| import org.apache.tools.ant.util.StringUtils; | |||
| /** | |||
| @@ -451,6 +452,7 @@ public class Parallel extends Task | |||
| */ | |||
| public void run() { | |||
| try { | |||
| LocalProperties.get(getProject()).copy(); | |||
| thread = Thread.currentThread(); | |||
| task.perform(); | |||
| } catch (Throwable t) { | |||
| @@ -23,6 +23,8 @@ import org.apache.tools.ant.BuildException; | |||
| import org.apache.tools.ant.Task; | |||
| import org.apache.tools.ant.TaskContainer; | |||
| import org.apache.tools.ant.property.LocalProperties; | |||
| /** | |||
| * Sequential is a container task - it can contain other Ant tasks. The nested | |||
| * tasks are simply executed in sequence. Sequential's primary use is to support | |||
| @@ -57,9 +59,16 @@ public class Sequential extends Task implements TaskContainer { | |||
| * @throws BuildException if one of the nested tasks fails. | |||
| */ | |||
| public void execute() throws BuildException { | |||
| for (Iterator i = nestedTasks.iterator(); i.hasNext();) { | |||
| Task nestedTask = (Task) i.next(); | |||
| nestedTask.perform(); | |||
| LocalProperties localProperties | |||
| = LocalProperties.get(getProject()); | |||
| localProperties.enterScope(); | |||
| try { | |||
| for (Iterator i = nestedTasks.iterator(); i.hasNext();) { | |||
| Task nestedTask = (Task) i.next(); | |||
| nestedTask.perform(); | |||
| } | |||
| } finally { | |||
| localProperties.exitScope(); | |||
| } | |||
| } | |||
| } | |||
| @@ -61,6 +61,7 @@ length=org.apache.tools.ant.taskdefs.Length | |||
| loadfile=org.apache.tools.ant.taskdefs.LoadFile | |||
| loadproperties=org.apache.tools.ant.taskdefs.LoadProperties | |||
| loadresource=org.apache.tools.ant.taskdefs.LoadResource | |||
| local=org.apache.tools.ant.taskdefs.Local | |||
| macrodef=org.apache.tools.ant.taskdefs.MacroDef | |||
| mail=org.apache.tools.ant.taskdefs.email.EmailTask | |||
| manifest=org.apache.tools.ant.taskdefs.ManifestTask | |||
| @@ -0,0 +1,86 @@ | |||
| <?xml version="1.0"?> | |||
| <!-- | |||
| Licensed to the Apache Software Foundation (ASF) under one or more | |||
| contributor license agreements. See the NOTICE file distributed with | |||
| this work for additional information regarding copyright ownership. | |||
| The ASF licenses this file to You under the Apache License, Version 2.0 | |||
| (the "License"); you may not use this file except in compliance with | |||
| the License. You may obtain a copy of the License at | |||
| http://www.apache.org/licenses/LICENSE-2.0 | |||
| Unless required by applicable law or agreed to in writing, software | |||
| distributed under the License is distributed on an "AS IS" BASIS, | |||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| See the License for the specific language governing permissions and | |||
| limitations under the License. | |||
| --> | |||
| <project default="antunit" xmlns:au="antlib:org.apache.ant.antunit"> | |||
| <import file="../antunit-base.xml" /> | |||
| <property name="foo" value="foo" /> | |||
| <target name="testBaseline"> | |||
| <au:assertPropertyEquals name="foo" value="foo" /> | |||
| </target> | |||
| <target name="testTarget"> | |||
| <au:assertPropertyEquals name="foo" value="foo" /> | |||
| <local name="foo" /> | |||
| <property name="foo" value="foo.target" /> | |||
| <au:assertPropertyEquals name="foo" value="foo.target" /> | |||
| </target> | |||
| <target name="testSequential"> | |||
| <sequential> | |||
| <local name="foo" /> | |||
| <property name="foo" value="foo.1" /> | |||
| <sequential> | |||
| <local name="foo" /> | |||
| <property name="foo" value="foo.2" /> | |||
| <au:assertPropertyEquals name="foo" value="foo.2" /> | |||
| </sequential> | |||
| <au:assertPropertyEquals name="foo" value="foo.1" /> | |||
| </sequential> | |||
| <au:assertPropertyEquals name="foo" value="foo" /> | |||
| </target> | |||
| <target name="testParallel"> | |||
| <macrodef name="p"> | |||
| <attribute name="value" /> | |||
| <attribute name="sleep" default="0" /> | |||
| <sequential> | |||
| <local name="foo" /> | |||
| <sleep seconds="@{sleep}" /> | |||
| <property name="foo" value="@{value}" /> | |||
| <au:assertPropertyEquals name="foo" value="@{value}" /> | |||
| </sequential> | |||
| </macrodef> | |||
| <parallel> | |||
| <p sleep="2" value="foo.a" /> | |||
| <au:assertPropertyEquals name="foo" value="foo" /> | |||
| <p sleep="1" value="foo.b" /> | |||
| <au:assertPropertyEquals name="foo" value="foo" /> | |||
| <p sleep="0" value="foo.c" /> | |||
| <au:assertPropertyEquals name="foo" value="foo" /> | |||
| </parallel> | |||
| <au:assertPropertyEquals name="foo" value="foo" /> | |||
| </target> | |||
| <target name="testMacrodef"> | |||
| <macrodef name="m"> | |||
| <sequential> | |||
| <local name="foo" /> | |||
| <property name="foo" value="foo.x" /> | |||
| <au:assertPropertyEquals name="foo" value="foo.x" /> | |||
| </sequential> | |||
| </macrodef> | |||
| <m /> | |||
| <au:assertPropertyEquals name="foo" value="foo" /> | |||
| <m /> | |||
| <au:assertPropertyEquals name="foo" value="foo" /> | |||
| <m /> | |||
| <au:assertPropertyEquals name="foo" value="foo" /> | |||
| </target> | |||
| </project> | |||