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}"/> | |||
<sysproperty key="root" file="${basedir}"/> | |||
<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"/> | |||
<batchtest> | |||
@@ -1775,6 +1776,8 @@ | |||
value="${tests-classpath.value}"/> | |||
<sysproperty key="root" file="${basedir}"/> | |||
<sysproperty key="build.compiler" value="${build.compiler}"/> | |||
<sysproperty key="tests.and.ant.share.classloader" | |||
value="${tests.and.ant.share.classloader}"/> | |||
<classpath refid="tests-classpath"/> | |||
<formatter type="plain" usefile="false"/> | |||
<test name="${testcase}"/> | |||
@@ -36,4 +36,21 @@ | |||
<x:preset.echo xmlns:x="abc" name="p"/> | |||
<x:p xmlns:x="abc">Hello from x:p</x:p> | |||
</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> |
@@ -36,6 +36,7 @@ import java.lang.reflect.Modifier; | |||
import java.lang.reflect.InvocationTargetException; | |||
import org.apache.tools.ant.taskdefs.Typedef; | |||
import org.apache.tools.ant.taskdefs.Definer; | |||
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_TYPE_LIST_LOAD = "Can't load default type list"; | |||
public static final String COMPONENT_HELPER_REFERENCE = "ant.ComponentHelper"; | |||
private static final String ANTLIB_PREFIX = "antlib:"; | |||
/** | |||
* string used to control build.syspath policy {@value} | |||
@@ -782,13 +782,15 @@ public class ComponentHelper { | |||
checkedNamespaces.add(uri); | |||
Typedef definer = new Typedef(); | |||
definer.setProject(project); | |||
definer.init(); | |||
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 | |||
definer.setOnError(new Typedef.OnError(Typedef.OnError.POLICY_IGNORE)); | |||
definer.init(); | |||
definer.execute(); | |||
} | |||
@@ -813,7 +815,7 @@ public class ComponentHelper { | |||
AntTypeDefinition def = getDefinition(componentName); | |||
if (def == null) { | |||
//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("Action: Check the spelling."); | |||
out.println("Action: Check that any custom tasks/types have been declared."); | |||
@@ -28,6 +28,12 @@ public final class MagicNames { | |||
private MagicNames() { | |||
} | |||
/** | |||
* prefix for antlib URIs: | |||
* {@value} | |||
*/ | |||
public static final String ANTLIB_PREFIX = "antlib:"; | |||
/** | |||
* 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.Project; | |||
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; | |||
/** | |||
@@ -44,6 +46,13 @@ import org.apache.tools.ant.types.EnumeratedAttribute; | |||
* @noinspection ParameterHidesMemberVariable | |||
*/ | |||
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 { | |||
public Object initialValue() { | |||
return new HashMap(); | |||
@@ -188,10 +197,20 @@ public abstract class Definer extends DefBase { | |||
ClassLoader al = createLoader(); | |||
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 (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() { | |||
String message = null; | |||
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 url the url to get the definitions from | |||
@@ -355,18 +406,12 @@ public abstract class Definer extends DefBase { | |||
} catch (IOException ex) { | |||
throw new BuildException(ex, getLocation()); | |||
} 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 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() { | |||
throw new BuildException( | |||
"Only one of the attributes name,file,resource" | |||
"Only one of the attributes name, file and resource" | |||
+ " can be set", getLocation()); | |||
} | |||
} |
@@ -18,8 +18,8 @@ | |||
package org.apache.tools.ant.taskdefs; | |||
import org.apache.tools.ant.BuildFileTest; | |||
import org.apache.tools.ant.Project; | |||
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"); | |||
} | |||
/** | |||
* 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() { | |||
expectLog("antlib.file", "MyTask called"); | |||
} | |||
@@ -49,6 +58,25 @@ public class AntlibTest extends BuildFileTest { | |||
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 void execute() { | |||
log("MyTask called"); | |||