1. you can go <typedef uri="antlib:something" /> and the resource is worked out automatically from the URI. 2. if you use antlib://org/ex/resource.xml we load in the resource by its full path, so you dont need multiple packages to have multiple antlib files. I'm not sure about #2; I think it is convenient once you have antlib-only distros (i.e. inline declaration and script; nothing else), but am not sure about the syntax. Maybe antlib://org.ex/antlib.xml would be better, and more in keeping with WWW URLs; if /antlib.xml is omitted, we would add it by default. git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@278535 13f79535-47bb-0310-9956-ffa450edef68master
| @@ -1602,7 +1602,8 @@ | |||||
| value="${tests-classpath.value}"/> | value="${tests-classpath.value}"/> | ||||
| <sysproperty key="root" file="${basedir}"/> | <sysproperty key="root" file="${basedir}"/> | ||||
| <sysproperty key="build.compiler" value="${build.compiler}"/> | <sysproperty key="build.compiler" value="${build.compiler}"/> | ||||
| <sysproperty key="tests.and.ant.share.classloader" | |||||
| value="${tests.and.ant.share.classloader}"/> | |||||
| <formatter type="brief" usefile="false"/> | <formatter type="brief" usefile="false"/> | ||||
| <batchtest> | <batchtest> | ||||
| @@ -1775,6 +1776,8 @@ | |||||
| value="${tests-classpath.value}"/> | value="${tests-classpath.value}"/> | ||||
| <sysproperty key="root" file="${basedir}"/> | <sysproperty key="root" file="${basedir}"/> | ||||
| <sysproperty key="build.compiler" value="${build.compiler}"/> | <sysproperty key="build.compiler" value="${build.compiler}"/> | ||||
| <sysproperty key="tests.and.ant.share.classloader" | |||||
| value="${tests.and.ant.share.classloader}"/> | |||||
| <classpath refid="tests-classpath"/> | <classpath refid="tests-classpath"/> | ||||
| <formatter type="plain" usefile="false"/> | <formatter type="plain" usefile="false"/> | ||||
| <test name="${testcase}"/> | <test name="${testcase}"/> | ||||
| @@ -36,4 +36,21 @@ | |||||
| <x:preset.echo xmlns:x="abc" name="p"/> | <x:preset.echo xmlns:x="abc" name="p"/> | ||||
| <x:p xmlns:x="abc">Hello from x:p</x:p> | <x:p xmlns:x="abc">Hello from x:p</x:p> | ||||
| </target> | </target> | ||||
| <target name="antlib_uri" > | |||||
| <typedef uri="antlib:org.example.tasks" onerror="failall"/> | |||||
| </target> | |||||
| <target name="antlib_uri_auto" xmlns:ex="antlib:org.example.tasks"> | |||||
| <ex:simple> | |||||
| <echo message="inside simple" /> | |||||
| </ex:simple> | |||||
| </target> | |||||
| <target name="antlib_uri_auto2" xmlns:ex="antlib://org/example/tasks/antlib2.xml"> | |||||
| <ex:simple> | |||||
| <echo message="inside simple"/> | |||||
| </ex:simple> | |||||
| </target> | |||||
| </project> | </project> | ||||
| @@ -36,6 +36,7 @@ import java.lang.reflect.Modifier; | |||||
| import java.lang.reflect.InvocationTargetException; | import java.lang.reflect.InvocationTargetException; | ||||
| import org.apache.tools.ant.taskdefs.Typedef; | import org.apache.tools.ant.taskdefs.Typedef; | ||||
| import org.apache.tools.ant.taskdefs.Definer; | |||||
| import org.apache.tools.ant.launch.Launcher; | import org.apache.tools.ant.launch.Launcher; | ||||
| /** | /** | ||||
| @@ -91,7 +92,6 @@ public class ComponentHelper { | |||||
| private static final String ERROR_NO_TASK_LIST_LOAD = "Can't load default task list"; | private static final String ERROR_NO_TASK_LIST_LOAD = "Can't load default task list"; | ||||
| private static final String ERROR_NO_TYPE_LIST_LOAD = "Can't load default type list"; | private static final String ERROR_NO_TYPE_LIST_LOAD = "Can't load default type list"; | ||||
| public static final String COMPONENT_HELPER_REFERENCE = "ant.ComponentHelper"; | public static final String COMPONENT_HELPER_REFERENCE = "ant.ComponentHelper"; | ||||
| private static final String ANTLIB_PREFIX = "antlib:"; | |||||
| /** | /** | ||||
| * string used to control build.syspath policy {@value} | * string used to control build.syspath policy {@value} | ||||
| @@ -782,13 +782,15 @@ public class ComponentHelper { | |||||
| checkedNamespaces.add(uri); | checkedNamespaces.add(uri); | ||||
| Typedef definer = new Typedef(); | Typedef definer = new Typedef(); | ||||
| definer.setProject(project); | definer.setProject(project); | ||||
| definer.init(); | |||||
| definer.setURI(uri); | definer.setURI(uri); | ||||
| definer.setResource( | |||||
| uri.substring(ANTLIB_PREFIX.length()).replace('.', '/') | |||||
| + "/antlib.xml"); | |||||
| //there to stop error messages being "null" | |||||
| definer.setTaskName(uri); | |||||
| //if this is left out, bad things happen. like all build files break | |||||
| //on the first element encountered. | |||||
| definer.setResource(Definer.makeResourceFromURI(uri)); | |||||
| // a fishing expedition :- ignore errors if antlib not present | // a fishing expedition :- ignore errors if antlib not present | ||||
| definer.setOnError(new Typedef.OnError(Typedef.OnError.POLICY_IGNORE)); | definer.setOnError(new Typedef.OnError(Typedef.OnError.POLICY_IGNORE)); | ||||
| definer.init(); | |||||
| definer.execute(); | definer.execute(); | ||||
| } | } | ||||
| @@ -813,7 +815,7 @@ public class ComponentHelper { | |||||
| AntTypeDefinition def = getDefinition(componentName); | AntTypeDefinition def = getDefinition(componentName); | ||||
| if (def == null) { | if (def == null) { | ||||
| //not a known type | //not a known type | ||||
| boolean isAntlib = componentName.indexOf(ANTLIB_PREFIX) == 0; | |||||
| boolean isAntlib = componentName.indexOf(MagicNames.ANTLIB_PREFIX) == 0; | |||||
| out.println("Cause: The name is undefined."); | out.println("Cause: The name is undefined."); | ||||
| out.println("Action: Check the spelling."); | out.println("Action: Check the spelling."); | ||||
| out.println("Action: Check that any custom tasks/types have been declared."); | out.println("Action: Check that any custom tasks/types have been declared."); | ||||
| @@ -28,6 +28,12 @@ public final class MagicNames { | |||||
| private MagicNames() { | private MagicNames() { | ||||
| } | } | ||||
| /** | |||||
| * prefix for antlib URIs: | |||||
| * {@value} | |||||
| */ | |||||
| public static final String ANTLIB_PREFIX = "antlib:"; | |||||
| /** | /** | ||||
| * Ant version property. {@value} | * Ant version property. {@value} | ||||
| */ | */ | ||||
| @@ -33,6 +33,8 @@ import org.apache.tools.ant.ComponentHelper; | |||||
| import org.apache.tools.ant.BuildException; | import org.apache.tools.ant.BuildException; | ||||
| import org.apache.tools.ant.Project; | import org.apache.tools.ant.Project; | ||||
| import org.apache.tools.ant.ProjectHelper; | import org.apache.tools.ant.ProjectHelper; | ||||
| import org.apache.tools.ant.MagicNames; | |||||
| import org.apache.tools.ant.util.FileUtils; | |||||
| import org.apache.tools.ant.types.EnumeratedAttribute; | import org.apache.tools.ant.types.EnumeratedAttribute; | ||||
| /** | /** | ||||
| @@ -44,6 +46,13 @@ import org.apache.tools.ant.types.EnumeratedAttribute; | |||||
| * @noinspection ParameterHidesMemberVariable | * @noinspection ParameterHidesMemberVariable | ||||
| */ | */ | ||||
| public abstract class Definer extends DefBase { | public abstract class Definer extends DefBase { | ||||
| /** | |||||
| * the extension of an antlib file for autoloading. | |||||
| * {@value[ | |||||
| */ | |||||
| private static final String ANTLIB_XML = "/antlib.xml"; | |||||
| private static class ResourceStack extends ThreadLocal { | private static class ResourceStack extends ThreadLocal { | ||||
| public Object initialValue() { | public Object initialValue() { | ||||
| return new HashMap(); | return new HashMap(); | ||||
| @@ -188,10 +197,20 @@ public abstract class Definer extends DefBase { | |||||
| ClassLoader al = createLoader(); | ClassLoader al = createLoader(); | ||||
| if (!definerSet) { | if (!definerSet) { | ||||
| throw new BuildException( | |||||
| "name, file or resource attribute of " | |||||
| + getTaskName() + " is undefined", getLocation()); | |||||
| } | |||||
| //we arent fully defined yet. this is an error unless | |||||
| //we are in an antlib, in which case the resource name is determined | |||||
| //automatically. | |||||
| //NB: URIs in the ant core package will be "" at this point. | |||||
| if (getURI()!=null && getURI().startsWith(MagicNames.ANTLIB_PREFIX)) { | |||||
| //convert the URI to a resource | |||||
| String uri1 = getURI(); | |||||
| setResource(makeResourceFromURI(uri1)); | |||||
| } else { | |||||
| throw new BuildException( | |||||
| "name, file or resource attribute of " | |||||
| + getTaskName() + " is undefined", getLocation()); | |||||
| } | |||||
| } | |||||
| if (name != null) { | if (name != null) { | ||||
| if (classname == null) { | if (classname == null) { | ||||
| @@ -262,6 +281,38 @@ public abstract class Definer extends DefBase { | |||||
| } | } | ||||
| } | } | ||||
| /** | |||||
| * This is where the logic to map from a URI to an antlib resource | |||||
| * is kept. | |||||
| * @return the name of a resource. It may not exist | |||||
| */ | |||||
| public static String makeResourceFromURI(String uri) { | |||||
| String path = uri.substring(MagicNames.ANTLIB_PREFIX.length()); | |||||
| String resource; | |||||
| if (path.startsWith("//")) { | |||||
| //handle new style full paths to an antlib, in which | |||||
| //all but the forward slashes are allowed. | |||||
| resource = path.substring("//".length()); | |||||
| if (!resource.endsWith(".xml")) { | |||||
| //if we haven't already named an XML file, it gets antlib.xml | |||||
| resource = resource + ANTLIB_XML; | |||||
| } | |||||
| } else { | |||||
| //convert from a package to a path | |||||
| resource = path.replace('.', '/') + ANTLIB_XML; | |||||
| } | |||||
| return resource; | |||||
| } | |||||
| /** | |||||
| * Convert a file to a file: URL. | |||||
| * | |||||
| * @return the URL, or null if it isn't valid and the active error policy | |||||
| * is not to raise a fault | |||||
| * @throws BuildException if the file is missing/not a file and the | |||||
| * policy requires failure at this point. | |||||
| */ | |||||
| private URL fileToURL() { | private URL fileToURL() { | ||||
| String message = null; | String message = null; | ||||
| if (!(file.exists())) { | if (!(file.exists())) { | ||||
| @@ -330,7 +381,7 @@ public abstract class Definer extends DefBase { | |||||
| } | } | ||||
| /** | /** | ||||
| * Load type definitions as properties from a url. | |||||
| * Load type definitions as properties from a URL. | |||||
| * | * | ||||
| * @param al the classloader to use | * @param al the classloader to use | ||||
| * @param url the url to get the definitions from | * @param url the url to get the definitions from | ||||
| @@ -355,18 +406,12 @@ public abstract class Definer extends DefBase { | |||||
| } catch (IOException ex) { | } catch (IOException ex) { | ||||
| throw new BuildException(ex, getLocation()); | throw new BuildException(ex, getLocation()); | ||||
| } finally { | } finally { | ||||
| if (is != null) { | |||||
| try { | |||||
| is.close(); | |||||
| } catch (IOException e) { | |||||
| // ignore | |||||
| } | |||||
| } | |||||
| FileUtils.close(is); | |||||
| } | } | ||||
| } | } | ||||
| /** | /** | ||||
| * Load an antlib from a url. | |||||
| * Load an antlib from a URL. | |||||
| * | * | ||||
| * @param classLoader the classloader to use. | * @param classLoader the classloader to use. | ||||
| * @param url the url to load the definitions from. | * @param url the url to load the definitions from. | ||||
| @@ -551,9 +596,13 @@ public abstract class Definer extends DefBase { | |||||
| } | } | ||||
| } | } | ||||
| /** | |||||
| * handle too many definitions by raising an exception. | |||||
| * @throws BuildException always. | |||||
| */ | |||||
| private void tooManyDefinitions() { | private void tooManyDefinitions() { | ||||
| throw new BuildException( | throw new BuildException( | ||||
| "Only one of the attributes name,file,resource" | |||||
| "Only one of the attributes name, file and resource" | |||||
| + " can be set", getLocation()); | + " can be set", getLocation()); | ||||
| } | } | ||||
| } | } | ||||
| @@ -18,8 +18,8 @@ | |||||
| package org.apache.tools.ant.taskdefs; | package org.apache.tools.ant.taskdefs; | ||||
| import org.apache.tools.ant.BuildFileTest; | import org.apache.tools.ant.BuildFileTest; | ||||
| import org.apache.tools.ant.Project; | |||||
| import org.apache.tools.ant.Task; | import org.apache.tools.ant.Task; | ||||
| import org.apache.tools.ant.Project; | |||||
| /** | /** | ||||
| */ | */ | ||||
| @@ -32,6 +32,15 @@ public class AntlibTest extends BuildFileTest { | |||||
| configureProject("src/etc/testcases/taskdefs/antlib.xml"); | configureProject("src/etc/testcases/taskdefs/antlib.xml"); | ||||
| } | } | ||||
| /** | |||||
| * only do the antlib tests if we are in the same JVM as ant. | |||||
| * @return | |||||
| */ | |||||
| private boolean isSharedJVM() { | |||||
| String property = System.getProperty("tests.and.ant.share.classloader"); | |||||
| return property!=null && Project.toBoolean(property); | |||||
| } | |||||
| public void testAntlibFile() { | public void testAntlibFile() { | ||||
| expectLog("antlib.file", "MyTask called"); | expectLog("antlib.file", "MyTask called"); | ||||
| } | } | ||||
| @@ -49,6 +58,25 @@ public class AntlibTest extends BuildFileTest { | |||||
| expectLog("ns.current", "Echo2 inside a macroHello from x:p"); | expectLog("ns.current", "Echo2 inside a macroHello from x:p"); | ||||
| } | } | ||||
| public void testAntlib_uri() { | |||||
| if (isSharedJVM()) { | |||||
| executeTarget("antlib_uri"); | |||||
| } | |||||
| } | |||||
| public void testAntlib_uri_auto() { | |||||
| if (isSharedJVM()) { | |||||
| executeTarget("antlib_uri_auto"); | |||||
| } | |||||
| } | |||||
| public void testAntlib_uri_auto2() { | |||||
| if (isSharedJVM()) { | |||||
| executeTarget("antlib_uri_auto2"); | |||||
| } | |||||
| } | |||||
| public static class MyTask extends Task { | public static class MyTask extends Task { | ||||
| public void execute() { | public void execute() { | ||||
| log("MyTask called"); | log("MyTask called"); | ||||