Browse Source

Add the possibility to register a custom command line argument processor

git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@1385262 13f79535-47bb-0310-9956-ffa450edef68
master
Nicolas Lalevee 12 years ago
parent
commit
eb08ea9fd0
7 changed files with 430 additions and 50 deletions
  1. +3
    -0
      WHATSNEW
  2. +76
    -0
      manual/argumentprocessor.html
  3. +1
    -0
      manual/developlist.html
  4. +15
    -1
      manual/running.html
  5. +72
    -0
      src/main/org/apache/tools/ant/ArgumentProcessor.java
  6. +168
    -0
      src/main/org/apache/tools/ant/ArgumentProcessorRegistry.java
  7. +95
    -49
      src/main/org/apache/tools/ant/Main.java

+ 3
- 0
WHATSNEW View File

@@ -93,6 +93,9 @@ Other changes:
* Make extension point bindable to imported prefixed targets
Bugzilla Report 53550.

* Add the possibility to register a custom command line argument processor.
See org.apache.tools.ant.ArgumentProcessor and manual/argumentprocessor.html

Changes from Ant 1.8.3 TO Ant 1.8.4
===================================



+ 76
- 0
manual/argumentprocessor.html View File

@@ -0,0 +1,76 @@
<!--
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>The Command Line Processor Plugin: ArgumentProcessor</title>
</head>

<body>
<h1>The Command Line Processor Plugin: ArgumentProcessor</h1>

<h2><a name="definition">What is an ArgumentProcessor?</a></h2>

<p>
An <code>ArgumentProcessor</code> is a parser of command line argument which is
then call before and after the build file is being parsed. Third party
libraries may then be able to have custom argument line argument which modify
Ant behaviour.
</p>

<p>
An <code>ArgumentProcessor</code> is called each time Ant parse an unknown
argument, an <code>ArgumentProcessor</code> doesn't take precedence over Ant to
parse already suported options. It is then recommended to thrid party
<code>ArgumentProcessor</code> implementation to chose specific 'enough'
argument name, avoiding for instance one letter arguments.
</p>

<p>
It is also called at the different phases so different behaviour can be
implemented. It is called just after every arguments are parsed, just
before the project is being configured (the build file being parsed),
and just after. Some of the methods to be implemented return a boolean:
if <code>true</code> is returned, Ant will terminate immediatly, without
error.
</p>

<p>
Being called during all these phases, an <code>ArgumentProcessor</code>
can just print some specific system properties and quit (like
<code>-diagnose</code>), or print some specific properties of a project after
being parsed and quit (like <code>-projectHelp</code>), or just set some
custom properties on the project and let it run.
</p>

<h2><a name="repository">How to register it's own ArgumentProcessor</a></h2>

<p>First, the <code>ArgumentProcessor</code> must be an implementation of
<code>org.apache.tools.ant.ArgumentProcessor</code>.
</p>

<p>Then to decare it: create a file
<code>META-INF/services/org.apache.tools.ant.ArgumentProcessor</code> which
contains only one line the fully qualified name of the class of the
implementation. This file together with the implementation class need then to
be found in Ant's classpath.
</p>
</body>
</html>


+ 1
- 0
manual/developlist.html View File

@@ -39,6 +39,7 @@
<li><a href="inputhandler.html">InputHandler</a></li>
<li><a href="antexternal.html">Using Ant Tasks Outside of Ant</a></li>
<li><a href="projecthelper.html">The Ant frontend: ProjectHelper</a></li>
<li><a href="argumentprocessor.html">The Command Line Processor Plugin:ArgumentProcessor</a></li>
</ul>

<h3>Tutorials</h3>


+ 15
- 1
manual/running.html View File

@@ -445,11 +445,18 @@ org.apache.tools.ant.Executor implementation specified here.
<tr>
<td><code>org.apache.tools.ant.ProjectHelper</code></td>
<!-- add the blank after the slash, so the browser can do a line break -->
<td>classname (optional, default 'org.apache.tools.ant.ProjectHelper')</td>
<td>classname (optional, default 'org.apache.tools.ant.ProjectHelper2')</td>
<td>specifies the classname to use as ProjectHelper. The class must extend
org.apache.tools.ant.ProjectHelper.
</td>
</tr>
<tr>
<td><code>org.apache.tools.ant.ArgumentProcessor</code></td>
<td>classname (optional)</td>
<td>specifies the classname to use as ArgumentProcessor. The class must extend
org.apache.tools.ant.ArgumentProcessor.
</td>
</tr>
<tr>
<td><code>p4.port, p4.client, p4.user</code></td>
<td>several formats</td>
@@ -477,6 +484,13 @@ org.apache.tools.ant.Executor implementation specified here.
<a href="projecthelper.html#repository">ProjectHelper internal repository</a>.
</td>
</tr>
<tr>
<td><code>ant.argument-processor-repo.debug</code></td>
<td>boolean (default 'false')</td>
<td>Set it to true to enable debugging with Ant's
<a href="argumentprocessor.html#repository">ArgumentProcessor internal repository</a>.
</td>
</tr>
</table>

<p>


+ 72
- 0
src/main/org/apache/tools/ant/ArgumentProcessor.java View File

@@ -0,0 +1,72 @@
/*
* 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;

import java.io.PrintStream;
import java.util.List;

/**
* Processor of arguments of the command line.
* <p>
* Arguments supported by third party code should not conflict with Ant core
* ones. It is then recommended to chose specific 'enough' argument name,
* avoiding for instance one letter arguments. By the way, if there any
* conflict, Ant will take precedence.
*
* @since 1.9
*/
public interface ArgumentProcessor {

/**
* Read the arguments from the command line at the specified position
* <p>
* If the argument is not supported, returns -1. Else, the position of the
* first argument not supported.
*/
int readArguments(String[] args, int pos);

/**
* If some arguments matched with {@link #readArguments(String[], int)},
* this method is called after all arguments were parsed. Returns
* <code>true</code> if Ant should stop there, ie the build file not parsed
* and the project should not be executed.
*/
boolean handleArg(List<String> args);

/**
* If some arguments matched with {@link #readArguments(String[], int)},
* this method is called just before the project being configured
*/
void prepareConfigure(Project project, List<String> args);

/**
* Handle the arguments with {@link #readArguments(String[], int)}, just
* after the project being configured. Returns <code>true</code> if Ant
* should stop there, ie the build file not parsed and the project should
* not be executed.
*/
boolean handleArg(Project project, List<String> arg);

/**
* Print the usage of the supported arguments
*
* @see org.apache.tools.ant.Main.printUsage()
*/
void printUsage(PrintStream writer);

}

+ 168
- 0
src/main/org/apache/tools/ant/ArgumentProcessorRegistry.java View File

@@ -0,0 +1,168 @@
/*
* 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;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

import org.apache.tools.ant.util.LoaderUtils;

/**
* The global registry for {@link ArgumentProcessor}s.
* <p>
* An {@link ArgumentProcessor} implementation can be registered via the system
* property <code>org.apache.tools.ant.ArgumentProcessor</code>, or via a JDK1.3
* 'service', by putting the fully qualified name of the implementation into the
* file <code>META-INF/services/org.apache.tools.ant.ArgumentProcessor</code>
* <p>
* Use the system property <code>ant.argument-processor.debug</code> to enable
* the print of debug log.
*
* @since 1.9
*/
public class ArgumentProcessorRegistry {

private static final String DEBUG_ARGUMENT_PROCESSOR_REPOSITORY = "ant.argument-processor-repo.debug";

// The message log level is not accessible here because everything
// is instanciated statically
private static final boolean DEBUG = "true".equals(System.getProperty(DEBUG_ARGUMENT_PROCESSOR_REPOSITORY));

private static final String SERVICE_ID = "META-INF/services/org.apache.tools.ant.ArgumentProcessor";

private static ArgumentProcessorRegistry instance = new ArgumentProcessorRegistry();

private List<ArgumentProcessor> processors = new ArrayList<ArgumentProcessor>();

public static ArgumentProcessorRegistry getInstance() {
return instance;
}

private ArgumentProcessorRegistry() {
collectArgumentProcessors();
}

public List<ArgumentProcessor> getProcessors() {
return processors;
}

private void collectArgumentProcessors() {
try {
ClassLoader classLoader = LoaderUtils.getContextClassLoader();
if (classLoader != null) {
Enumeration<URL> resources = classLoader.getResources(SERVICE_ID);
while (resources.hasMoreElements()) {
URL resource = resources.nextElement();
ArgumentProcessor processor = getProcessorByService(resource.openStream());
registerArgumentProcessor(processor);
}
}

InputStream systemResource = ClassLoader.getSystemResourceAsStream(SERVICE_ID);
if (systemResource != null) {
ArgumentProcessor processor = getProcessorByService(systemResource);
registerArgumentProcessor(processor);
}
} catch (Exception e) {
System.err.println("Unable to load ArgumentProcessor from service "
+ SERVICE_ID + " (" + e.getClass().getName() + ": "
+ e.getMessage() + ")");
if (DEBUG) {
e.printStackTrace(System.err);
}
}
}

public void registerArgumentProcessor(String helperClassName)
throws BuildException {
registerArgumentProcessor(getProcessor(helperClassName));
}

public void registerArgumentProcessor(
Class< ? extends ArgumentProcessor> helperClass)
throws BuildException {
registerArgumentProcessor(getProcessor(helperClass));
}

private ArgumentProcessor getProcessor(String helperClassName) {
try {
@SuppressWarnings("unchecked")
Class< ? extends ArgumentProcessor> cl = (Class< ? extends ArgumentProcessor>) Class.forName(helperClassName);
return getProcessor(cl);
} catch (ClassNotFoundException e) {
throw new BuildException("Argument processor class "
+ helperClassName + " was not found", e);
}
}

private ArgumentProcessor getProcessor(
Class< ? extends ArgumentProcessor> processorClass) {
ArgumentProcessor processor;
try {
processor = processorClass.getConstructor().newInstance();
} catch (Exception e) {
throw new BuildException("The argument processor class"
+ processorClass.getClass().getName()
+ " could not be instanciated with a default constructor",
e);
}
return processor;
}

public void registerArgumentProcessor(ArgumentProcessor processor) {
if (processor == null) {
return;
}
processors.add(processor);
if (DEBUG) {
System.out.println("Argument processor "
+ processor.getClass().getName() + " registered.");
}
}

private ArgumentProcessor getProcessorByService(InputStream is)
throws IOException {
InputStreamReader isr = null;
try {
try {
isr = new InputStreamReader(is, "UTF-8");
} catch (java.io.UnsupportedEncodingException e) {
isr = new InputStreamReader(is);
}
BufferedReader rd = new BufferedReader(isr);
String processorClassName = rd.readLine();
if (processorClassName != null && !"".equals(processorClassName)) {
return getProcessor(processorClassName);
}
} finally {
try {
isr.close();
} catch (IOException e) {
// ignore
}
}
return null;
}

}

+ 95
- 49
src/main/org/apache/tools/ant/Main.java View File

@@ -24,12 +24,15 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
@@ -152,6 +155,7 @@ public class Main implements AntMain {
*/
private boolean proxy = false;

private Map<Class<?>, List<String>> extraArguments = new HashMap<Class<?>, List<String>>();

private static final GetProperty NOPROPERTIES = new GetProperty(){
public Object getProperty(String aName) {
@@ -324,6 +328,8 @@ public class Main implements AntMain {
boolean justPrintVersion = false;
boolean justPrintDiagnostics = false;

ArgumentProcessorRegistry processorRegistry = ArgumentProcessorRegistry.getInstance();
for (int i = 0; i < args.length; i++) {
String arg = args[i];

@@ -399,11 +405,29 @@ public class Main implements AntMain {
} else if (arg.equals("-autoproxy")) {
proxy = true;
} else if (arg.startsWith("-")) {
// we don't have any more args to recognize!
String msg = "Unknown argument: " + arg;
System.err.println(msg);
printUsage();
throw new BuildException("");
boolean processed = false;
for (ArgumentProcessor processor : processorRegistry.getProcessors()) {
int newI = processor.readArguments(args, i);
if (newI != -1) {
List<String> extraArgs = extraArguments.get(processor.getClass());
if (extraArgs == null) {
extraArgs = new ArrayList<String>();
extraArguments.put(processor.getClass(), extraArgs);
}
for (; i < newI && i < args.length; i++) {
extraArgs.add(args[i]);
}
processed = true;
break;
}
}
if (!processed) {
// we don't have any more args to recognize!
String msg = "Unknown argument: " + arg;
System.err.println(msg);
printUsage();
throw new BuildException("");
}
} else {
// if it's no other arg, it may be the target
targets.addElement(arg);
@@ -726,6 +750,17 @@ public class Main implements AntMain {
return;
}

ArgumentProcessorRegistry processorRegistry = ArgumentProcessorRegistry.getInstance();

for (ArgumentProcessor processor : processorRegistry.getProcessors()) {
List<String> extraArgs = extraArguments.get(processor.getClass());
if (extraArgs != null) {
if (processor.handleArg(extraArgs)) {
return;
}
}
}

final Project project = new Project();
project.setCoreLoader(coreLoader);

@@ -781,8 +816,24 @@ public class Main implements AntMain {
proxySetup.enableProxies();
}

for (ArgumentProcessor processor : processorRegistry.getProcessors()) {
List<String> extraArgs = extraArguments.get(processor.getClass());
if (extraArgs != null) {
processor.prepareConfigure(project, extraArgs);
}
}

ProjectHelper.configureProject(project, buildFile);

for (ArgumentProcessor processor : processorRegistry.getProcessors()) {
List<String> extraArgs = extraArguments.get(processor.getClass());
if (extraArgs != null) {
if (processor.handleArg(project, extraArgs)) {
return;
}
}
}

if (projectHelp) {
printDescription(project);
printTargets(project, msgOutputLevel > Project.MSG_INFO,
@@ -954,50 +1005,45 @@ public class Main implements AntMain {
* Prints the usage information for this class to <code>System.out</code>.
*/
private static void printUsage() {
String lSep = System.getProperty("line.separator");
StringBuffer msg = new StringBuffer();
msg.append("ant [options] [target [target2 [target3] ...]]" + lSep);
msg.append("Options: " + lSep);
msg.append(" -help, -h print this message" + lSep);
msg.append(" -projecthelp, -p print project help information" + lSep);
msg.append(" -version print the version information and exit" + lSep);
msg.append(" -diagnostics print information that might be helpful to" + lSep);
msg.append(" diagnose or report problems." + lSep);
msg.append(" -quiet, -q be extra quiet" + lSep);
msg.append(" -silent, -S print nothing but task outputs and build failures" + lSep);
msg.append(" -verbose, -v be extra verbose" + lSep);
msg.append(" -debug, -d print debugging information" + lSep);
msg.append(" -emacs, -e produce logging information without adornments"
+ lSep);
msg.append(" -lib <path> specifies a path to search for jars and classes"
+ lSep);
msg.append(" -logfile <file> use given file for log" + lSep);
msg.append(" -l <file> ''" + lSep);
msg.append(" -logger <classname> the class which is to perform logging" + lSep);
msg.append(" -listener <classname> add an instance of class as a project listener"
+ lSep);
msg.append(" -noinput do not allow interactive input" + lSep);
msg.append(" -buildfile <file> use given buildfile" + lSep);
msg.append(" -file <file> ''" + lSep);
msg.append(" -f <file> ''" + lSep);
msg.append(" -D<property>=<value> use value for given property" + lSep);
msg.append(" -keep-going, -k execute all targets that do not depend" + lSep);
msg.append(" on failed target(s)" + lSep);
msg.append(" -propertyfile <name> load all properties from file with -D" + lSep);
msg.append(" properties taking precedence" + lSep);
msg.append(" -inputhandler <class> the class which will handle input requests" + lSep);
msg.append(" -find <file> (s)earch for buildfile towards the root of" + lSep);
msg.append(" -s <file> the filesystem and use it" + lSep);
msg.append(" -nice number A niceness value for the main thread:" + lSep
+ " 1 (lowest) to 10 (highest); 5 is the default"
+ lSep);
msg.append(" -nouserlib Run ant without using the jar files from" + lSep
+ " ${user.home}/.ant/lib" + lSep);
msg.append(" -noclasspath Run ant without using CLASSPATH" + lSep);
msg.append(" -autoproxy Java1.5+: use the OS proxy settings"
+ lSep);
msg.append(" -main <class> override Ant's normal entry point");
System.out.println(msg.toString());
System.out.println("ant [options] [target [target2 [target3] ...]]");
System.out.println("Options: ");
System.out.println(" -help, -h print this message");
System.out.println(" -projecthelp, -p print project help information");
System.out.println(" -version print the version information and exit");
System.out.println(" -diagnostics print information that might be helpful to");
System.out.println(" diagnose or report problems.");
System.out.println(" -quiet, -q be extra quiet");
System.out.println(" -silent, -S print nothing but task outputs and build failures");
System.out.println(" -verbose, -v be extra verbose");
System.out.println(" -debug, -d print debugging information");
System.out.println(" -emacs, -e produce logging information without adornments");
System.out.println(" -lib <path> specifies a path to search for jars and classes");
System.out.println(" -logfile <file> use given file for log");
System.out.println(" -l <file> ''");
System.out.println(" -logger <classname> the class which is to perform logging");
System.out.println(" -listener <classname> add an instance of class as a project listener");
System.out.println(" -noinput do not allow interactive input");
System.out.println(" -buildfile <file> use given buildfile");
System.out.println(" -file <file> ''");
System.out.println(" -f <file> ''");
System.out.println(" -D<property>=<value> use value for given property");
System.out.println(" -keep-going, -k execute all targets that do not depend");
System.out.println(" on failed target(s)");
System.out.println(" -propertyfile <name> load all properties from file with -D");
System.out.println(" properties taking precedence");
System.out.println(" -inputhandler <class> the class which will handle input requests");
System.out.println(" -find <file> (s)earch for buildfile towards the root of");
System.out.println(" -s <file> the filesystem and use it");
System.out.println(" -nice number A niceness value for the main thread:"
+ " 1 (lowest) to 10 (highest); 5 is the default");
System.out.println(" -nouserlib Run ant without using the jar files from"
+ " ${user.home}/.ant/lib");
System.out.println(" -noclasspath Run ant without using CLASSPATH");
System.out.println(" -autoproxy Java1.5+: use the OS proxy settings");
System.out.println(" -main <class> override Ant's normal entry point");
for (ArgumentProcessor processor : ArgumentProcessorRegistry.getInstance().getProcessors()) {
processor.printUsage(System.out);
}
}

/**


Loading…
Cancel
Save