From 32c6ef39e4527cdcaeb6a702be90203ca8ced940 Mon Sep 17 00:00:00 2001 From: Stefan Bodewig Date: Tue, 11 Nov 2008 11:03:45 +0000 Subject: [PATCH] add an variant named that matches EasyAnt's git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@713016 13f79535-47bb-0310-9956-ffa450edef68 --- WHATSNEW | 3 + docs/manual/CoreTasks/import.html | 93 +++++- docs/manual/CoreTasks/include.html | 274 ++++++++++++++++++ docs/manual/coretasklist.html | 1 + docs/manual/tasksoverview.html | 5 + .../org/apache/tools/ant/ProjectHelper.java | 39 ++- src/main/org/apache/tools/ant/Target.java | 29 +- .../tools/ant/helper/ProjectHelper2.java | 34 ++- .../apache/tools/ant/taskdefs/ImportTask.java | 24 +- .../tools/ant/taskdefs/defaults.properties | 1 + src/tests/antunit/taskdefs/import-test.xml | 18 ++ .../antunit/taskdefs/importtests/override.xml | 25 ++ src/tests/antunit/taskdefs/include-test.xml | 41 +++ 13 files changed, 571 insertions(+), 16 deletions(-) create mode 100644 docs/manual/CoreTasks/include.html create mode 100644 src/tests/antunit/taskdefs/importtests/override.xml create mode 100644 src/tests/antunit/taskdefs/include-test.xml diff --git a/WHATSNEW b/WHATSNEW index da7536ee6..32de7e391 100644 --- a/WHATSNEW +++ b/WHATSNEW @@ -519,6 +519,9 @@ Other changes: * has a new attribute "as" that can be used to control the prefix prepended to the imported target's names. + * a new task provides an alternative to that + should be preferred when you don't want to override any targets. + Changes from Ant 1.7.0 TO Ant 1.7.1 ============================================= diff --git a/docs/manual/CoreTasks/import.html b/docs/manual/CoreTasks/import.html index 1a3adf9c7..3b2594333 100644 --- a/docs/manual/CoreTasks/import.html +++ b/docs/manual/CoreTasks/import.html @@ -27,6 +27,14 @@

Imports another build file into the current project.

+ +

+ Note this task heavily relies on the ProjectHelper + implementation and doesn't really perform any work of its own. If + you have configured Ant to use a ProjectHelper other than Ant's + default, this task may or may not work. +

+

On execution it will read another Ant file into the same Project. This means that it basically works like the @@ -83,7 +91,7 @@ property of the main buildfile.

Note that "builddocs" is not the filename, but the name attribute present in the imported project tag.

- If import file does not have a name attribute, the ant.file.projectname + If the imported file does not have a name attribute, the ant.file.projectname property will not be set.

@@ -182,6 +190,89 @@ directory.

Imports the project defined by the property deploy-platform

+

How is <import> different + from <include>?

+ +

When using import the imported targets are available by up to two + names. Their "normal" name without any prefix and potentially with + a prefixed name (the value of the as attribute or the imported + project's name attribute, if any).

+ +

When using include the included targets are only available in the + prefixed form.

+ +

When using import, the imported target's depends attribute + remains unchanged, i.e. it uses "normal" names and allows you to + override targets in the dependency list.

+ +

When using include, the included target's depends attribute is + rewritten so that prefixed names are used. This allows writers of + the included file to control which target is invoked as part of the + dependencies.

+ +

It is possible to include the same file more than once by using + different prefixes, it is not possible to import the same file more + than once.

+ +

Use import if you intend to override a target, otherwise use include.

+ +

nested.xml shall be:

+ +
+<project>
+  <target name="setUp">
+    <property name="prop" value="in nested"/>
+  </target>
+
+  <target name="echo" depends="setUp">
+    <echo>prop has the value ${prop}</echo>
+  </target>
+</project>
+
+ +

When using import like in

+ +
+<project>
+  <target name="setUp">
+    <property name="prop" value="in importing"/>
+  </target>
+
+  <import file="nested.xml" as="nested"/>
+</project>
+
+ +

Running the target nested.echo will emit: + +

+setUp:
+
+nested.echo:
+     [echo] prop has the value in importing
+
+ +

When using include like in

+ +
+<project>
+  <target name="setUp">
+    <property name="prop" value="in importing"/>
+  </target>
+
+  <include file="nested.xml" as="nested"/>
+</project>
+
+ +

Running the target nested.echo will emit: + +

+nested.setUp:
+
+nested.echo:
+     [echo] prop has the value in nested
+
+ +

and there won't be any target named "echo" on the including build file.

diff --git a/docs/manual/CoreTasks/include.html b/docs/manual/CoreTasks/include.html new file mode 100644 index 000000000..8a5e900a9 --- /dev/null +++ b/docs/manual/CoreTasks/include.html @@ -0,0 +1,274 @@ + + + + + + + Include Task + + +

Include

+

Description

+

+ Include another build file into the current project. +

+ +

+ Note this task heavily relies on the ProjectHelper + implementation and doesn't really perform any work of its own. If + you have configured Ant to use a ProjectHelper other than Ant's + default, this task may or may not work. +

+ +

+ On execution it will read another Ant file into the same Project + rewriting the included target names and depends lists. This is + different + from Entity + Includes as explained in the Ant FAQ in so far as the target + names get prefixed by the included project's name or the as + attribute and do not appear as if the file was contained in the + including file. +

+

+ The include task may only be used as a top-level task. This means that + it may not be used in a target. +

+

+There are two further functional aspects that pertain to this task and +that are not possible with entity includes: +

    +
  • target rewriting
  • +
  • special properties
  • +
+

+

Target rewriting

+ +

Any target in the included file will be renamed + to prefix.name where name is the original target's + name and prefix is either the value of the as + attribute or the name attribute of the project tag of + the included file.

+ +

The depends attribute of all included targets is rewritten so that + all target names are prefixed as well. This makes the included file + self-contained.

+ +

Special Properties

+ +

Included files are treated as they are present in the main +buildfile. This makes it easy to understand, but it makes it impossible +for them to reference files and resources relative to their path. +Because of this, for every included file, Ant adds a property that +contains the path to the included buildfile. With this path, the +included buildfile can keep resources and be able to reference them +relative to its position.

+ +

So if I include for example a docsbuild.xml file named builddocs, +I can get its path as ant.file.builddocs, similarly to the ant.file +property of the main buildfile.

+ +

Note that "builddocs" is not the filename, but the name attribute +present in the included project tag.

+

+ If the included file does not have a name attribute, the ant.file.projectname + property will not be set. +

+ +

Resolving files against the included file

+ +

Suppose your main build file called including.xml +includes a build file included.xml, located anywhere on +the file system, and included.xml reads a set of +properties from included.properties:

+ +
<!-- including.xml -->
+<project name="including" basedir="." default="...">
+  <include file="${path_to_included}/included.xml"/>
+</project>
+
+<!-- included.xml -->
+<project name="included" basedir="." default="...">
+  <property file="included.properties"/>
+</project>
+
+ +

This snippet however will resolve included.properties +against the basedir of including.xml, because the basedir +of included.xml is ignored by Ant. The right way to use +included.properties is:

+ +
+<!-- included.xml -->
+<project name="included" basedir="." default="...">
+  <dirname property="included.basedir" file="${ant.file.included}"/>
+  <property file="${included.basedir}/included.properties"/>
+</project>
+
+ +

As explained above ${ant.file.included} stores the +path of the build script, that defines the project called +included, (in short it stores the path to +included.xml) and <dirname> takes its +directory. This technique also allows included.xml to be +used as a standalone file (without being included in other +project).

+ +

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + +
AttributeDescriptionRequired
+ file + + The file to include. If this is a relative file name, the file name will be resolved + relative to the including file. Note, this is unlike most other + ant file attributes, where relative files are resolved relative to ${basedir}. + Yes
+ optional + + If true, do not stop the build if the file does not exist, + default is false. + No
+ as + + Specifies the prefix prepended to the target names. If + ommitted, the name attribute of the project tag of the + imported file will be used. + Yes, if the included file's + project tag doesn't specify a name attribute.
+ +

Examples

+
  <include file="../common-targets.xml"/>
+
+ +

Includes targets from the common-targets.xml file that is in a parent +directory.

+ +
  <include file="${deploy-platform}.xml"/>
+
+ +

Includes the project defined by the property deploy-platform

+ +

How is <import> different + from <include>?

+ +

When using import the imported targets are available by up to two + names. Their "normal" name without any prefix and potentially with + a prefixed name (the value of the as attribute or the imported + project's name attribute, if any).

+ +

When using include the included targets are only available in the + prefixed form.

+ +

When using import, the imported target's depends attribute + remains unchanged, i.e. it uses "normal" names and allows you to + override targets in the dependency list.

+ +

When using include, the included target's depends attribute is + rewritten so that prefixed names are used. This allows writers of + the included file to control which target is invoked as part of the + dependencies.

+ +

It is possible to include the same file more than once by using + different prefixes, it is not possible to import the same file more + than once.

+ +

Use import if you intend to override a target, otherwise use include.

+ +

nested.xml shall be:

+ +
+<project>
+  <target name="setUp">
+    <property name="prop" value="in nested"/>
+  </target>
+
+  <target name="echo" depends="setUp">
+    <echo>prop has the value ${prop}</echo>
+  </target>
+</project>
+
+ +

When using import like in

+ +
+<project>
+  <target name="setUp">
+    <property name="prop" value="in importing"/>
+  </target>
+
+  <import file="nested.xml" as="nested"/>
+</project>
+
+ +

Running the target nested.echo will emit: + +

+setUp:
+
+nested.echo:
+     [echo] prop has the value in importing
+
+ +

When using include like in

+ +
+<project>
+  <target name="setUp">
+    <property name="prop" value="in importing"/>
+  </target>
+
+  <include file="nested.xml" as="nested"/>
+</project>
+
+ +

Running the target nested.echo will emit: + +

+nested.setUp:
+
+nested.echo:
+     [echo] prop has the value in nested
+
+ +

and there won't be any target named "echo" on the including build file.

+ + + diff --git a/docs/manual/coretasklist.html b/docs/manual/coretasklist.html index aa47dd623..a3951c7a3 100644 --- a/docs/manual/coretasklist.html +++ b/docs/manual/coretasklist.html @@ -77,6 +77,7 @@ GUnzip
GZip
Import
+Include
Input
Jar
Java
diff --git a/docs/manual/tasksoverview.html b/docs/manual/tasksoverview.html index 17da10b7f..07d269d88 100644 --- a/docs/manual/tasksoverview.html +++ b/docs/manual/tasksoverview.html @@ -843,6 +843,11 @@ documentation.

in it with targets of your own.

+ + Include +

Include another build file.

+ + JavaCC

Invokes the diff --git a/src/main/org/apache/tools/ant/ProjectHelper.java b/src/main/org/apache/tools/ant/ProjectHelper.java index db4654a2b..99a7d292b 100644 --- a/src/main/org/apache/tools/ant/ProjectHelper.java +++ b/src/main/org/apache/tools/ant/ProjectHelper.java @@ -128,7 +128,7 @@ public class ProjectHelper { * * @return the configured prefix or null * - * @since ant 1.8.0 + * @since Ant 1.8.0 */ public static String getCurrentTargetPrefix() { return (String) targetPrefix.get(); @@ -137,12 +137,47 @@ public class ProjectHelper { /** * Sets the prefix to prepend to imported target names. * - * @since ant 1.8.0 + * @since Ant 1.8.0 */ public static void setCurrentTargetPrefix(String prefix) { targetPrefix.set(prefix); } + private final static ThreadLocal inIncludeMode = new ThreadLocal() { + protected Object initialValue() { + return Boolean.FALSE; + } + }; + + /** + * Whether the current file should be read in include as opposed + * to import mode. + * + *

In include mode included targets are only known by their + * prefixed names and their depends lists get rewritten so that + * all dependencies get the prefix as well.

+ * + *

In import mode imported targets are known by an adorned as + * well as a prefixed name and the unadorned target may be + * overwritten in the importing build file. The depends list of + * the imported targets is not modified at all.

+ * + * @since Ant 1.8.0 + */ + public static boolean isInIncludeMode() { + return inIncludeMode.get() == Boolean.TRUE; + } + + /** + * Sets whether the current file should be read in include as + * opposed to import mode. + * + * @since Ant 1.8.0 + */ + public static void setInIncludeMode(boolean includeMode) { + inIncludeMode.set(includeMode ? Boolean.TRUE : Boolean.FALSE); + } + // -------------------- Parse method -------------------- /** * Parses the project file, configuring the project as it goes. diff --git a/src/main/org/apache/tools/ant/Target.java b/src/main/org/apache/tools/ant/Target.java index 19fda5767..27abf5759 100644 --- a/src/main/org/apache/tools/ant/Target.java +++ b/src/main/org/apache/tools/ant/Target.java @@ -125,19 +125,31 @@ public class Target implements TaskContainer { * depends on. Must not be null. */ public void setDepends(String depS) { - if (depS.length() > 0) { + for (Iterator iter = parseDepends(depS, getName()).iterator(); + iter.hasNext(); ) { + addDependency((String) iter.next()); + } + } + + public static List/**/ parseDepends(String depends, + String targetName) { + ArrayList list = new ArrayList(); + if (depends.length() > 0) { StringTokenizer tok = - new StringTokenizer(depS, ",", true); + new StringTokenizer(depends, ",", true); while (tok.hasMoreTokens()) { String token = tok.nextToken().trim(); // Make sure the dependency is not empty string if ("".equals(token) || ",".equals(token)) { - throw new BuildException("Syntax Error: depends " + "attribute of target \"" - + getName() + "\" has an empty string as dependency."); + throw new BuildException("Syntax Error: depends " + + "attribute of target \"" + + targetName + + "\" has an empty string as " + + "dependency."); } - addDependency(token); + list.add(token); // Make sure that depends attribute does not // end in a , @@ -145,12 +157,15 @@ public class Target implements TaskContainer { token = tok.nextToken(); if (!tok.hasMoreTokens() || !",".equals(token)) { throw new BuildException("Syntax Error: Depend " - + "attribute for target \"" + getName() - + "\" ends with a , character"); + + "attribute for target \"" + + targetName + + "\" ends with a \",\" " + + "character"); } } } } + return list; } /** diff --git a/src/main/org/apache/tools/ant/helper/ProjectHelper2.java b/src/main/org/apache/tools/ant/helper/ProjectHelper2.java index 14e8910ba..e2706c306 100644 --- a/src/main/org/apache/tools/ant/helper/ProjectHelper2.java +++ b/src/main/org/apache/tools/ant/helper/ProjectHelper2.java @@ -45,6 +45,7 @@ import java.io.UnsupportedEncodingException; import java.net.URL; import java.util.HashMap; import java.util.Hashtable; +import java.util.Iterator; import java.util.Map; import java.util.Stack; @@ -845,9 +846,25 @@ public class ProjectHelper2 extends ProjectHelper { context.getLocator()); } + String prefix = null; + boolean isInIncludeMode = + context.isIgnoringProjectTag() && isInIncludeMode(); + if (isInIncludeMode) { + prefix = getTargetPrefix(context); + if (prefix == null) { + throw new BuildException("can't include build file " + + context.getBuildFile() + + ", no as attribute has been given" + + " and the project tag doesn't" + + " specify a name attribute"); + } + name = prefix + "." + name; + } + // Check if this target is in the current build file if (context.getCurrentTargets().get(name) != null) { - throw new BuildException("Duplicate target '" + name + "'", target.getLocation()); + throw new BuildException("Duplicate target '" + name + "'", + target.getLocation()); } Hashtable projectTargets = project.getTargets(); boolean usedTarget = false; @@ -862,12 +879,19 @@ public class ProjectHelper2 extends ProjectHelper { usedTarget = true; } if (depends.length() > 0) { - target.setDepends(depends); + if (!isInIncludeMode) { + target.setDepends(depends); + } else { + for (Iterator iter = + Target.parseDepends(depends, name).iterator(); + iter.hasNext(); ) { + target.addDependency(prefix + "." + iter.next()); + } + } } - String prefix = null; - if (context.isIgnoringProjectTag() + if (!isInIncludeMode && context.isIgnoringProjectTag() && (prefix = getTargetPrefix(context)) != null) { - // In an impored file (and not completely + // In an imported file (and not completely // ignoring the project tag or having a preconfigured prefix) String newName = prefix + "." + name; Target newTarget = usedTarget ? new Target(target) : target; diff --git a/src/main/org/apache/tools/ant/taskdefs/ImportTask.java b/src/main/org/apache/tools/ant/taskdefs/ImportTask.java index d5d598acf..897f189ce 100644 --- a/src/main/org/apache/tools/ant/taskdefs/ImportTask.java +++ b/src/main/org/apache/tools/ant/taskdefs/ImportTask.java @@ -145,7 +145,7 @@ public class ImportTask extends Task { } } - if (importStack.contains(importedFile)) { + if (!isInIncludeMode() && importStack.contains(importedFile)) { getProject().log( "Skipped already imported file:\n " + importedFile + "\n", Project.MSG_VERBOSE); @@ -155,14 +155,36 @@ public class ImportTask extends Task { // nested invokations are possible like an imported file // importing another one String oldPrefix = ProjectHelper.getCurrentTargetPrefix(); + boolean oldIncludeMode = ProjectHelper.isInIncludeMode(); try { ProjectHelper.setCurrentTargetPrefix(targetPrefix); + ProjectHelper.setInIncludeMode(isInIncludeMode()); helper.parse(getProject(), importedFile); } catch (BuildException ex) { throw ProjectHelper.addLocationToBuildException( ex, getLocation()); } finally { ProjectHelper.setCurrentTargetPrefix(oldPrefix); + ProjectHelper.setInIncludeMode(oldIncludeMode); } } + + /** + * Whether the task is in include (as opposed to import) mode. + * + *

In include mode included targets are only known by their + * prefixed names and their depends lists get rewritten so that + * all dependencies get the prefix as well.

+ * + *

In import mode imported targets are known by an adorned as + * well as a prefixed name and the unadorned target may be + * overwritten in the importing build file. The depends list of + * the imported targets is not modified at all.

+ * + * @since Ant 1.8.0 + */ + protected final boolean isInIncludeMode() { + return "include".equals(getTaskType()); + } + } diff --git a/src/main/org/apache/tools/ant/taskdefs/defaults.properties b/src/main/org/apache/tools/ant/taskdefs/defaults.properties index 5831273d5..122fe5cf7 100644 --- a/src/main/org/apache/tools/ant/taskdefs/defaults.properties +++ b/src/main/org/apache/tools/ant/taskdefs/defaults.properties @@ -56,6 +56,7 @@ get=org.apache.tools.ant.taskdefs.Get gunzip=org.apache.tools.ant.taskdefs.GUnzip gzip=org.apache.tools.ant.taskdefs.GZip import=org.apache.tools.ant.taskdefs.ImportTask +include=org.apache.tools.ant.taskdefs.ImportTask input=org.apache.tools.ant.taskdefs.Input jar=org.apache.tools.ant.taskdefs.Jar java=org.apache.tools.ant.taskdefs.Java diff --git a/src/tests/antunit/taskdefs/import-test.xml b/src/tests/antunit/taskdefs/import-test.xml index 5f9650294..2bca71872 100644 --- a/src/tests/antunit/taskdefs/import-test.xml +++ b/src/tests/antunit/taskdefs/import-test.xml @@ -28,4 +28,22 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/tests/antunit/taskdefs/importtests/override.xml b/src/tests/antunit/taskdefs/importtests/override.xml new file mode 100644 index 000000000..97fd320bc --- /dev/null +++ b/src/tests/antunit/taskdefs/importtests/override.xml @@ -0,0 +1,25 @@ + + + + + + + + + + diff --git a/src/tests/antunit/taskdefs/include-test.xml b/src/tests/antunit/taskdefs/include-test.xml new file mode 100644 index 000000000..83c8a3f9e --- /dev/null +++ b/src/tests/antunit/taskdefs/include-test.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + +