Here we go, Erik, using refactoring instead of subclassing, as you suggested
(and i'm pretty pleased with the results :).
Overview of changes:
Commandline:
- added createArgument( boolean insertAtStart ). createArgument() now calls
createArgument( false ). This allows args to be inserted at the beginning of
the args list (some Cvs flags need to be first in line, as do some rsync and
webalizer args). i know this is backwards-compatible because i actually broke
the Javac task while making this change, and had to fix it. i would like to
add createArgument( int index ), but i'm not up for that much error handling
this late at night ;).
AbstractCvsTask: (requires the above Commandline changes)
(all changes are backwards-compatible)
- Added property: compression={number 1-9 or boolean}. Adds -zN to the
command line.
- "command-global" properties, like cvsRoot and compression are passed on to
each command line.
- Refactoring to allow better re-use of Environment "global" Commandline
prorties (like cvsRoot and cvsPackage).
- Cvs commands are now accepted in 3 different formats:
1) command='...'
2) character data (one cvs command per line).
3) <commandline> child elements.
An example shows it best:
<cvs failonerror='true'
compression='true'
>
<commandline>
<argument value='update'/>
<argument value='-r'/>
<argument value='1.268'/>
<!-- alternately, value='-rTAG', with no space. -->
<argument value='build.xml'/>
</commandline>
<commandline>
<argument value='update'/>
<argument value='-r'/>
<argument value='HEAD'/>
<argument value='build.xml'/>
</commandline>
<commandline>
<argument line='log -r1.5 build.xml'/>
</commandline>
<![CDATA[
status src/main/org/apache/tools/ant/taskdefs/AbstractCvsTask.java
]]>
</cvs>
command=... is run first.
<commandline> is run second.
character data commands are run last.
command=... becomes optional if one of the other two forms are used, and
defaults to 'checkout' (legacy behaviour) if no command-input method is used
(in legacy terms, if no command is set). The command property is no longer
ignored if character data is set (as it was in my last patch).
It is completely backwards-compatible with the AbstractCvsTask, it only adds
new functionality. i find the legacy default behaviour odd (checkout?
shouldn't it be update, if anything?).
The above xml, patch and output from the task is attached.
TODO(?): add 'outputfile=...' support for <commandline>?
A note about the Cvs task documentation:
It's a bit misleading in that it says, basically, that no arguments are
needed and that command defaults to checkout. Checkout by itself won't work,
though:
[cvs] running cvs command: cvs -z3 checkout
[cvs] cvs [checkout aborted]: must specify at least one module or
directory
i would suggest changing the default command to null, but since that is
likely to break at least one build.xml in userspace i would recommend
changing it to update, which can succeed without more information and is
backwards compatible except in the (rare?) case that someone is doing a full
checkout at the start of a build.
----- stephan
Generic Universal Computer Guy
[EMAIL PROTECTED] - http://www.einsurance.de
Office: +49 (89) �552 92 862 Handy: �+49 (179) 211 97 67
Student: "Master, you must teach me the way of liberation!"
Master: "Tell me who it is that binds you."
Student: "No one binds me!"
Master: "Then why do you seek liberation?"
Buildfile: stephan.xml
test:
[cvs] running cvs command: cvs -z3 update -r 1.268 build.xml
[cvs] P build.xml
[cvs] running cvs command: cvs -z3 update -r HEAD build.xml
[cvs] P build.xml
[cvs] running cvs command: cvs -z3 log -r1.5 build.xml
[cvs]
[cvs] RCS file: /home/cvspublic/jakarta-ant/build.xml,v
[cvs] Working file: build.xml
[cvs] head: 1.269
[cvs] branch:
[cvs] locks: strict
[cvs] access list:
[cvs] symbolic names:
[cvs] ANT_141: 1.175.2.13
[cvs] ANT_141_B1: 1.175.2.12
[cvs] ANT_MAIN_14: 1.179
[cvs] ANT_14: 1.175.2.10
[cvs] ANT_MAIN_14B2: 1.177
[cvs] ANT_14_B2: 1.175.2.7
[cvs] ANT_14_B1: 1.175.2.1
[cvs] ANT_14_BRANCH: 1.175.0.2
[cvs] ANT_13: 1.130.2.8
[cvs] ANT_13_B3: 1.130.2.7
[cvs] ANT_13_B2: 1.130.2.4
[cvs] ANT_13_B1: 1.130
[cvs] ANT_13_BRANCH: 1.130.0.2
[cvs] ANT_12_FIXES: 1.89.0.2
[cvs] ANT_12: 1.89
[cvs] ANT_11: 1.40
[cvs] TOMCAT_31_FINAL: 1.16
[cvs] TOMCAT_31: 1.16.0.2
[cvs] TOMCAT_31_RC1: 1.16
[cvs] TOMCAT_31_BETA1: 1.12
[cvs] TOMCAT_31_M1: 1.3
[cvs] TOMCAT_31_M1_RC1: 1.3
[cvs] keyword substitution: kv
[cvs] total revisions: 290; selected revisions: 1
[cvs] description:
[cvs] ----------------------------
[cvs] revision 1.5
[cvs] date: 2000/01/30 17:15:32; author: rubys; state: Exp; lines: +1
-0
[cvs] More ant.home cleanup
[cvs]
=============================================================================
[cvs] running cvs command: cvs -z3 status
src/main/org/apache/tools/ant/taskdefs/AbstractCvsTask.java
[cvs] ===================================================================
[cvs] File: AbstractCvsTask.java Status: Locally Modified
[cvs]
[cvs] Working revision: 1.2
[cvs] Repository revision: 1.2
/home/cvspublic/jakarta-ant/src/main/org/apache/tools/ant/taskdefs/AbstractCvsTask.java,v
[cvs] Sticky Tag: (none)
[cvs] Sticky Date: (none)
[cvs] Sticky Options: (none)
[cvs]
BUILD SUCCESSFUL
Total time: 10 seconds
Index: src/main/org/apache/tools/ant/taskdefs/AbstractCvsTask.java
===================================================================
RCS file: /home/cvspublic/jakarta-ant/src/main/org/apache/tools/ant/taskdefs/AbstractCvsTask.java,v
retrieving revision 1.2
diff -u -r1.2 AbstractCvsTask.java
--- src/main/org/apache/tools/ant/taskdefs/AbstractCvsTask.java 7 Feb 2002 02:15:47 -0000 1.2
+++ src/main/org/apache/tools/ant/taskdefs/AbstractCvsTask.java 24 Mar 2002 23:31:45 -0000
@@ -60,6 +60,7 @@
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
+import java.util.Vector;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
@@ -79,9 +80,13 @@
* @author Kevin Ross <a href="mailto:[EMAIL PROTECTED]">[EMAIL PROTECTED]</a>
*/
public abstract class AbstractCvsTask extends Task {
-
+ /** Default compression level to use, if compression is enabled via setCompression( true ). */
+ public static final int DEFAULT_COMPRESSION_LEVEL = 3;
private Commandline cmd = new Commandline();
+ /** list of Commandline children */
+ private Vector vecCommandlines = new Vector();
+
/**
* the CVSROOT variable.
*/
@@ -100,13 +105,16 @@
/**
* the CVS command to execute.
*/
- private String command = "checkout";
+ private static final String default_command = "checkout";
+ private String command = null;
/**
* suppress information messages.
*/
private boolean quiet = false;
+ private int compression = 0;
+
/**
* report only, don't change any files.
*/
@@ -146,6 +154,10 @@
*/
private boolean failOnError = false;
+ /**
+ * see addText()
+ */
+ private String cdata = null;
/**
* Create accessors for the following, to allow different handling of
@@ -155,6 +167,10 @@
private OutputStream outputStream;
private OutputStream errorStream;
+ /** (It just bugs me to leave this to the compiler.) */
+ public AbstractCvsTask() {
+ }
+
public void setExecuteStreamHandler(ExecuteStreamHandler executeStreamHandler){
this.executeStreamHandler = executeStreamHandler;
@@ -222,39 +238,51 @@
return this.errorStream;
}
- public void execute() throws BuildException {
+ /**
+This reads in any data set via addText() and treats each line as one cvs command,
+executing it via runCommand().
+ */
+ protected synchronized void processCData() throws BuildException {
+ if( this.cdata == null || this.cdata.length() < 1 ) return;
+ java.util.StringTokenizer st = new java.util.StringTokenizer( this.cdata, org.apache.tools.ant.util.StringUtils.LINE_SEP );
+ String mycmd = null;
+ try {
+ while( st.hasMoreElements() )
+ {
+ mycmd = ((String)st.nextElement()).trim();
+ if( mycmd.length() == 0 ) continue;
+ if( mycmd.startsWith( ";" ) ) { continue; }
+ if( mycmd.startsWith( "#" ) ) { continue; }
+ mycmd = getProject().replaceProperties(mycmd);
+ Commandline c = new Commandline();
+ this.configureCommandline( c );
+ c.createArgument().setLine( mycmd );
+ //log( "processCData() cvs command: ["+c.toString()+"]", Project.MSG_DEBUG );
+ //log( "processCData() cvs command: ["+c.toString()+"]" );
+ //this.setCommand( mycmd );
+ this.runCommand( c );
+ }
+ }
+ catch( BuildException e ) {
+ throw( e );
+ }
+ catch( Exception e ) {
+ throw new BuildException( e, location );
+ }
+ }
+ /**
+ * Sets up the environment for toExecute and then runs it.
+ * @throws BuildException
+ */
+ protected void runCommand( Commandline toExecute ) throws BuildException {
+ //log( "runCommand( "+toExecute.toString()+" )" );
// XXX: we should use JCVS (www.ice.com/JCVS) instead of command line
// execution so that we don't rely on having native CVS stuff around (SM)
// We can't do it ourselves as jCVS is GPLed, a third party task
// outside of jakarta repositories would be possible though (SB).
- Commandline toExecute = new Commandline();
-
- toExecute.setExecutable("cvs");
- if (cvsRoot != null) {
- toExecute.createArgument().setValue("-d");
- toExecute.createArgument().setValue(cvsRoot);
- }
- if (noexec) {
- toExecute.createArgument().setValue("-n");
- }
- if (quiet) {
- toExecute.createArgument().setValue("-q");
- }
-
- toExecute.createArgument().setLine(command);
-
- //
- // get the other arguments.
- //
- toExecute.addArguments(cmd.getCommandline());
-
- if (cvsPackage != null) {
- toExecute.createArgument().setLine(cvsPackage);
- }
-
Environment env = new Environment();
if(port>0){
@@ -308,18 +336,46 @@
exe.setCommandline(toExecute.getCommandline());
exe.setEnvironment(env.getVariables());
+ String warning = "ACHTUNG, BABY: proceeding only by the grace of failonerror='false': ";
try {
- log("Executing: " + executeToString(exe), Project.MSG_DEBUG);
-
- int retCode = exe.execute();
+ String actualCommandLine = this.executeToString(exe);
+// log("running cvs command: " + actualCommandLine, Project.MSG_DEBUG);
+ log("running cvs command: " + actualCommandLine );
+ int retCode = 0;
+ retCode = exe.execute();
+ //log( "retCode="+retCode, Project.MSG_DEBUG );
/*Throw an exception if cvs exited with error. (Iulian)*/
if(failOnError && retCode != 0) {
- throw new BuildException("cvs exited with error code "+ retCode);
+ throw new BuildException("cvs exited with error code "+ retCode +
+ org.apache.tools.ant.util.StringUtils.LINE_SEP+
+ "Command line was ["+actualCommandLine+"]",
+ location );
}
- }
+ } // try
catch (IOException e) {
+ if( failOnError ) {
throw new BuildException(e, location);
}
+ else {
+ log( warning+e.getMessage(), Project.MSG_INFO);
+ }
+ }
+ catch (BuildException e) {
+ if( failOnError ) {
+ throw( e );
+ }
+ else {
+ log( warning+e.getMessage(), Project.MSG_INFO);
+ }
+ }
+ catch (Exception e) {
+ if( failOnError ) {
+ throw new BuildException(e, location);
+ }
+ else {
+ log( warning+e.getMessage(), Project.MSG_INFO);
+ }
+ }
finally {
//
// condition used to be if(output == null) outputStream.close(). This is
@@ -330,7 +386,6 @@
outputStream.close();
} catch (IOException e) {}
}
-
if (errorStream != null) {
try {
errorStream.close();
@@ -339,6 +394,37 @@
}
}
+ public void execute() throws BuildException {
+
+
+ if( this.getCommand() == null
+ && this.getText() == null
+ && vecCommandlines.size() == 0 ) {
+ // re-implement legacy behaviour:
+ this.setCommand( AbstractCvsTask.default_command );
+ }
+
+ String c = this.getCommand();
+ if( c != null ) {
+ this.addConfiguredCommandline( this.cmd, true );
+ this.cmd.createArgument().setLine(c);
+ }
+
+ // this isn't at the top of the function so we can re-implement legacy behaviour of 'checkout'
+ if( this.getCommand() == null
+ && this.getText() == null
+ && vecCommandlines.size() == 0 ) {
+ throw new BuildException( "You must specify either the command property, <commandline> elements, or character data (one cvs command per line)!", location );
+ }
+
+ //log( "Running <commandline> stuff..." );
+ for( int i = 0; i < vecCommandlines.size(); i++ ) {
+ this.runCommand( (Commandline)vecCommandlines.get( i ) );
+ }
+ //log( "Running character data stuff..." );
+ this.processCData();
+ }
+
private String executeToString(Execute execute){
StringBuffer stringBuffer = new StringBuffer(250);
@@ -349,17 +435,14 @@
stringBuffer.append(" ");
}
String newLine = System.getProperty("line.separator");
- stringBuffer.append(newLine);
- stringBuffer.append(newLine);
- stringBuffer.append("environment:");
- stringBuffer.append(newLine);
-
-
String[] variableArray = execute.getEnvironment();
if(variableArray != null){
+ stringBuffer.append(newLine);
+ stringBuffer.append(newLine);
+ stringBuffer.append("environment:");
+ stringBuffer.append(newLine);
for(int z=0; z<variableArray.length; z++){
-
stringBuffer.append(newLine);
stringBuffer.append("\t");
stringBuffer.append(variableArray[z]);
@@ -451,10 +534,14 @@
* of commands externally.
*/
public void addCommandArgument(String arg){
+ this.addCommandArgument( cmd, arg);
+ }
- this.cmd.createArgument().setValue(arg);
+ public void addCommandArgument(Commandline c, String arg){
+ c.createArgument().setValue(arg);
}
+
public void setDate(String p) {
if(p != null && p.trim().length() > 0) {
addCommandArgument("-D");
@@ -465,6 +552,9 @@
public void setCommand(String c) {
this.command = c;
}
+ public String getCommand() {
+ return this.command;
+ }
public void setQuiet(boolean q) {
quiet = q;
@@ -489,6 +579,83 @@
public void setFailOnError(boolean failOnError) {
this.failOnError = failOnError;
}
-}
+ /**
+ * Accepts cvs commands, one command per line, and executes them in order after any
+ * other commands. Example:
+ * <code>
+ * <cvs>
+ * up -r 1.8 some/file
+ * status some/other/file
+ * ; comment lines, too.
+ * # and for Perl fans...
+ *</cvs>
+ * </code>
+ * 'command' is ignored if this is set (sorry).
+ */
+ public void addText( String text ) {
+ this.cdata = text;
+ //if( text == null ) return;
+ //this.setCommand( null ); // this is a very lame workaround. Ask me someday. ([EMAIL PROTECTED])
+ }
+
+ public String getText() {
+ return this.cdata;
+ }
+
+ /**
+ * Configure a commandline element for things like cvsRoot, quiet, etc.
+ */
+ protected void configureCommandline( Commandline c ) {
+ if( c == null ) { return; }
+ c.setExecutable( "cvs" );
+ if (cvsPackage != null) {
+ c.createArgument(true).setLine(cvsPackage);
+ }
+ if ( this.compression > 0 && this.compression < 10 ) {
+ c.createArgument(true).setValue("-z"+this.compression);
+ }
+ if (quiet) {
+ c.createArgument(true).setValue("-q");
+ }
+ if (noexec) {
+ c.createArgument(true).setValue("-n");
+ }
+ if (cvsRoot != null) {
+ c.createArgument(true).setLine("-d"+cvsRoot);
+ }
+ }
+
+ public void addConfiguredCommandline( Commandline c ) {
+ this.addConfiguredCommandline( c, false );
+ }
+ /**
+ * Configures and adds the given Commandline.
+ * @param insertAtStart If true, c is
+ */
+ public void addConfiguredCommandline( Commandline c, boolean insertAtStart ) {
+ if( c == null ) { return; }
+ this.configureCommandline( c );
+ if( insertAtStart ) {
+ vecCommandlines.insertElementAt( c, 0 );
+ }
+ else {
+ vecCommandlines.addElement( c );
+ }
+ //log( (insertAtStart ? "Inserting" : "Adding") + " Commandline: "+c.toString() );
+ }
+ /**
+ * If set to a value 1-9 it adds -zN to the cvs command line, else it disables compression.
+ */
+ public void setCompression( int level ) {
+ this.compression = level;
+ }
+ /**
+ * @param usecomp If true, turns on compression using default level, AbstractCvsTask.DEFAULT_COMPRESSION_LEVEL.
+ */
+ public void setCompression( boolean usecomp ) {
+ this.setCompression( usecomp ? AbstractCvsTask.DEFAULT_COMPRESSION_LEVEL : 0 );
+ }
+
+}
Index: src/main/org/apache/tools/ant/types/Commandline.java
===================================================================
RCS file: /home/cvspublic/jakarta-ant/src/main/org/apache/tools/ant/types/Commandline.java,v
retrieving revision 1.18
diff -u -r1.18 Commandline.java
--- src/main/org/apache/tools/ant/types/Commandline.java 10 Jan 2002 11:21:20 -0000 1.18
+++ src/main/org/apache/tools/ant/types/Commandline.java 24 Mar 2002 23:31:45 -0000
@@ -80,7 +80,7 @@
* <code>createAcommandline</code> which returns an instance of this class.
*
* @author [EMAIL PROTECTED]
- * @author <a href="mailto:[EMAIL PROTECTED]">Stefan Bodewig</a>
+ * @author <a href="mailto:[EMAIL PROTECTED]">Stefan Bodewig</a>
*/
public class Commandline implements Cloneable {
@@ -124,15 +124,20 @@
* @param line line to split into several commandline arguments
*/
public void setLine(String line) {
+ if( line == null ) { return; }
+ // log( "Argument.setLine( "+line+")" );
parts = translateCommandline(line);
}
+ protected void log( String msg ) {
+ System.out.println( getClass().getName()+": "+msg );
+ }
/**
* Sets a single commandline argument and treats it like a
* PATH - ensures the right separator for the local platform
* is used.
*
- * @param value a single commandline argument.
+ * @param value a single commandline argument.
*/
public void setPath(Path value) {
parts = new String[] {value.toString()};
@@ -140,9 +145,9 @@
/**
* Sets a single commandline argument to the absolute filename
- * of the given file.
+ * of the given file.
*
- * @param value a single commandline argument.
+ * @param value a single commandline argument.
*/
public void setFile(File value) {
parts = new String[] {value.getAbsolutePath()};
@@ -154,7 +159,15 @@
public String[] getParts() {
return parts;
}
- }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ for( int i = 0; i < parts.length; i++ ) {
+ sb.append( parts[i]+" " );
+ }
+ return sb.toString();
+ }
+ } // class Argument
/**
* Class to keep track of the position of an Argument.
@@ -187,20 +200,31 @@
}
return realPos;
}
- }
+ } // class Marker
/**
- * Creates an argument object.
- * Each commandline object has at most one instance of the argument class.
+ * Calls <code>this.createArgument( false )</code>.
* @return the argument object.
*/
public Argument createArgument() {
+ return this.createArgument( false );
+ }
+ /**
+ * Creates an argument object and adds it to our list of args.
+ * @param insertAtStart if true, the argument is inserted at the beginning of the list of
+ * args, otherwise it is appended.
+ */
+ public Argument createArgument( boolean insertAtStart ) {
Argument argument = new Argument();
- arguments.addElement(argument);
+ if( insertAtStart ) {
+ arguments.insertElementAt(argument,0);
+ }
+ else {
+ arguments.addElement(argument);
+ }
return argument;
}
-
/**
* Sets the executable to run.
*/
@@ -248,11 +272,11 @@
for (int i=0; i<arguments.size(); i++) {
Argument arg = (Argument) arguments.elementAt(i);
String[] s = arg.getParts();
- for (int j=0; j<s.length; j++) {
+ if( s != null ) for (int j=0; j<s.length; j++) {
result.addElement(s[j]);
}
}
-
+
String [] res = new String[result.size()];
result.copyInto(res);
return res;
@@ -271,7 +295,7 @@
* surround the argument by double quotes.</p>
*
* @exception BuildException if the argument contains both, single
- * and double quotes.
+ * and double quotes.
*/
public static String quoteArgument(String argument) {
if (argument.indexOf("\"") > -1) {
@@ -310,7 +334,7 @@
}
// parse with a simple finite state machine
-
+
final int normal = 0;
final int inQuote = 1;
final int inDoubleQuote = 2;
@@ -390,7 +414,7 @@
public void clearArgs() {
arguments.removeAllElements();
}
-
+
/**
* Return a marker.
*
@@ -401,4 +425,8 @@
public Marker createMarker() {
return new Marker(arguments.size());
}
+ protected void log( String msg ) {
+ System.out.println( this.getClass().getName()+": "+msg );
+ }
+
}
--
To unsubscribe, e-mail: <mailto:[EMAIL PROTECTED]>
For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>