DO NOT REPLY TO THIS EMAIL, BUT PLEASE POST YOUR BUG RELATED COMMENTS THROUGH THE WEB INTERFACE AVAILABLE AT <http://nagoya.apache.org/bugzilla/show_bug.cgi?id=4387>. ANY REPLY MADE TO THIS MESSAGE WILL NOT BE COLLECTED AND INSERTED IN THE BUG DATABASE.
http://nagoya.apache.org/bugzilla/show_bug.cgi?id=4387 Problem using vssget to get latest version from MS SourceSafe when some files are checked out Summary: Problem using vssget to get latest version from MS SourceSafe when some files are checked out Product: Ant Version: 1.4.1 Platform: All OS/Version: All Status: NEW Severity: Enhancement Priority: Other Component: Optional Tasks AssignedTo: [EMAIL PROTECTED] ReportedBy: [EMAIL PROTECTED] A couple of weeks ago I sent a mail to ant-dev with this content hoping that my modifications might be considered, but I see that the subject of this mail was unfortunate and that enhancements generally are posted through the bug database. I apolologize for posting this again in what appears to be the appropriate channel if you already have noticed my previous mail! Due to needs in my project I have modified org.apache.tools.ant.taskdefs.optional.vss.MSVSS and org.apache.tools.ant.taskdefs.optional.vss.MSVSSGET to handle the folloing scenario: I am getting the latest version of my project from MS SourceSafe to my development directory in which some files are checked out. When using MSVSSGET (version 1.4.1) the build fails, with: [vssget] $/MyProject/mypackage: [vssget] [vssget] A writable copy of C:\projects\myproject\mypackage\A.java already exists I am using JDK 1.3.1 and MS SourceSafe 6.0. >From MSDN I have found an overview of options for ss GET (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/ssusexp98/ html/cmdline_switchg.asp). There are four options to specify how local writable copies are handled: -GWA Displays a dialog box asking the user to choose between replacing, skipping, or merging write-only files on Get Latest Version and Check Out operations. -GWM Merges write-only files on certain operations (Get and Check Out). -GWR Replaces write-only files on certain operations (Get and Check Out). -GWS Skips write-only files on certain operations (Get and Check Out). Thus I have made the following proposition to org.apache.tools.ant.taskdefs.optional.vss.MSVSS and org.apache.tools.ant.taskdefs.optional.vss.MSVSSGET that handles these options: MSVSS: public abstract class MSVSS extends Task { private String m_SSDir = ""; private String m_vssLogin = null; private String m_vssPath = null; private String m_serverPath = null; /** * Set the directory where ss.exe is located * * @param dir the directory containing ss.exe */ public final void setSsdir(String dir) { m_SSDir = project.translatePath(dir); } /** * Builds and returns the command string to execute ss.exe */ public final String getSSCommand() { String toReturn = m_SSDir; if ( !toReturn.equals("") && !toReturn.endsWith("\\") ) { toReturn += "\\"; } toReturn += SS_EXE; return toReturn; } /** * Set the login to use when accessing vss. * <p> * Should be formatted as username,password * * @param login the login string to use */ public final void setLogin(String login) { m_vssLogin = login; } /** * @return the appropriate login command if the 'login' attribute was specified, otherwise an empty string */ public void getLoginCommand(Commandline cmd) { if ( m_vssLogin == null ) { return; } else { cmd.createArgument().setValue(FLAG_LOGIN + m_vssLogin); } } /** * Set the path to the item in vss to operate on * <p> * Ant can't cope with a '$' sign in an attribute so we have to add it here. * Also we strip off any 'vss://' prefix which is an XMS special and should probably be removed! * * @param vssPath */ public final void setVsspath(String vssPath) { if ( vssPath.startsWith("vss://") ) { m_vssPath= PROJECT_PREFIX + vssPath.substring(5); } else { m_vssPath = PROJECT_PREFIX + vssPath; } } /** * @return m_vssPath */ public String getVsspath() { return m_vssPath; } /** * Set the path to the location of the ss.ini * * @param serverPath */ public final void setServerpath(String serverPath) { m_serverPath = serverPath; } protected int run(Commandline cmd) { try { Execute exe = new Execute(new LogStreamHandler(this, Project.MSG_INFO, Project.MSG_WARN)); // If location of ss.ini is specified we need to set the // environment-variable SSDIR to this value if (m_serverPath != null) { String[] env = exe.getEnvironment(); if( env == null ) { env = new String[0]; } String[] newEnv = new String[env.length+1]; for( int i=0;i<env.length;i++ ) { newEnv[i] = env[i]; } newEnv[env.length] = "SSDIR=" + m_serverPath; exe.setEnvironment(newEnv); } exe.setAntRun(project); exe.setWorkingDirectory(project.getBaseDir()); exe.setCommandline(cmd.getCommandline()); return exe.execute(); } catch (java.io.IOException e) { throw new BuildException(e, location); } } /** * Constant for the thing to execute */ private static final String SS_EXE = "ss"; /** */ public static final String PROJECT_PREFIX = "$"; /** * The 'Get' command */ public static final String COMMAND_GET = "Get"; /** * The 'Checkout' command */ public static final String COMMAND_CHECKOUT = "Checkout"; /** * The 'Checkin' command */ public static final String COMMAND_CHECKIN = "Checkin"; /** * The 'Label' command */ public static final String COMMAND_LABEL = "Label"; /** * The 'History' command */ public static final String COMMAND_HISTORY = "History"; /** */ public static final String FLAG_LOGIN = "-Y"; /** */ public static final String FLAG_OVERRIDE_WORKING_DIR = "-GL"; /** */ public static final String FLAG_AUTORESPONSE_DEF = "-I-"; /** */ public static final String FLAG_AUTORESPONSE_YES = "-I-Y"; /** */ public static final String FLAG_AUTORESPONSE_NO = "-I-N"; /** */ public static final String FLAG_RECURSION = "-R"; /** */ public static final String FLAG_VERSION = "-V"; /** */ public static final String FLAG_VERSION_DATE = "-Vd"; /** */ public static final String FLAG_VERSION_LABEL = "-VL"; /** */ public static final String FLAG_WRITABLE = "-W"; /** */ public static final String VALUE_NO = "-N"; /** */ public static final String VALUE_YES = "-Y"; /** */ public static final String FLAG_QUIET = "-O-"; /** */ public static final String FLAG_REPLACEWRITABLE_ASK = "-GWA"; /** */ public static final String FLAG_REPLACEWRITABLE_MERGE = "-GWM"; /** */ public static final String FLAG_REPLACEWRITABLE_REPLACE = "-GWR"; /** */ public static final String FLAG_REPLACEWRITABLE_SKIP = "-GWS"; } The code added is the four flags at the bottom to handle the new options. MSVSSGET: public class MSVSSGET extends MSVSS { private String m_LocalPath = null; private boolean m_Recursive = false; private boolean m_Writable = false; private String m_Version = null; private String m_Date = null; private String m_Label = null; private String m_AutoResponse = null; private boolean m_Quiet = false; private String m_ReplaceWritable = null; /** * Executes the task. * <p> * Builds a command line to execute ss and then calls Exec's run method * to execute the command line. */ public void execute() throws BuildException { Commandline commandLine = new Commandline(); int result = 0; // first off, make sure that we've got a command and a vssdir ... if (getVsspath() == null) { String msg = "vsspath attribute must be set!"; throw new BuildException(msg, location); } // now look for illegal combinations of things ... // build the command line from what we got the format is // ss Get VSS items [-G] [-H] [-I-] [-N] [-O] [-R] [-V] [-W] [-Y] [-?] // as specified in the SS.EXE help commandLine.setExecutable(getSSCommand()); commandLine.createArgument().setValue(COMMAND_GET); // VSS items commandLine.createArgument().setValue(getVsspath()); // -GL getLocalpathCommand(commandLine); // -GWA or -GWM or -GWR or -GWS getReplaceWritableCommand(commandLine); // -I- or -I-Y or -I-N getAutoresponse(commandLine); // -O- getQuietCommand(commandLine); // -R getRecursiveCommand(commandLine); // -V getVersionCommand(commandLine); // -W getWritableCommand(commandLine); // -Y getLoginCommand(commandLine); result = run(commandLine); if ( result != 0 ) { String msg = "Failed executing: " + commandLine.toString(); throw new BuildException(msg, location); } } /** * Set the local path. */ public void setLocalpath(Path localPath) { m_LocalPath = localPath.toString(); } /** * Builds and returns the -GL flag command if required * <p> * The localpath is created if it didn't exist */ public void getLocalpathCommand(Commandline cmd) { if (m_LocalPath == null) { return; } else { // make sure m_LocalDir exists, create it if it doesn't File dir = project.resolveFile(m_LocalPath); if (!dir.exists()) { boolean done = dir.mkdirs(); if (done == false) { String msg = "Directory " + m_LocalPath + " creation was not " + "successful for an unknown reason"; throw new BuildException(msg, location); } project.log("Created dir: " + dir.getAbsolutePath()); } cmd.createArgument().setValue(FLAG_OVERRIDE_WORKING_DIR + m_LocalPath); } } /** * Set behaviour recursive or non-recursive */ public void setRecursive(boolean recursive) { m_Recursive = recursive; } /** * @return the 'recursive' command if the attribute was 'true', otherwise an empty string */ public void getRecursiveCommand(Commandline cmd) { if ( !m_Recursive ) { return; } else { cmd.createArgument().setValue(FLAG_RECURSION); } } /** * Sets/clears quiet mode */ public final void setQuiet (boolean quiet) { this.m_Quiet=quiet; } public void getQuietCommand (Commandline cmd) { if (m_Quiet) { cmd.createArgument().setValue (FLAG_QUIET); } } /** * Set behaviour, used in get command to make files that are 'got' writable */ public final void setWritable(boolean argWritable) { m_Writable = argWritable; } /** * @return the 'make writable' command if the attribute was 'true', otherwise an empty string */ public void getWritableCommand(Commandline cmd) { if ( !m_Writable ) { return; } else { cmd.createArgument().setValue(FLAG_WRITABLE); } } /** * Set the stored version string * <p> * Note we assume that if the supplied string has the value "null" that something * went wrong and that the string value got populated from a null object. This * happens if a ant variable is used e.g. version="${ver_server}" when ver_server * has not been defined to ant! */ public void setVersion(String version) { if (version.equals("") || version.equals("null") ) { m_Version = null; } else { m_Version = version; } } /** * Set the stored date string * <p> * Note we assume that if the supplied string has the value "null" that something * went wrong and that the string value got populated from a null object. This * happens if a ant variable is used e.g. date="${date}" when date * has not been defined to ant! */ public void setDate(String date) { if (date.equals("") || date.equals("null") ) { m_Date = null; } else { m_Date = date; } } /** * Set the labeled version to operate on in SourceSafe * <p> * Note we assume that if the supplied string has the value "null" that something * went wrong and that the string value got populated from a null object. This * happens if a ant variable is used e.g. label="${label_server}" when label_server * has not been defined to ant! */ public void setLabel(String label) { if ( label.equals("") || label.equals("null") ) { m_Label = null; } else { m_Label = label; } } /** * Simple order of priority. Returns the first specified of version, date, label * If none of these was specified returns "" */ public void getVersionCommand(Commandline cmd) { if ( m_Version != null) { cmd.createArgument().setValue(FLAG_VERSION + m_Version); } else if ( m_Date != null) { cmd.createArgument().setValue(FLAG_VERSION_DATE + m_Date); } else if (m_Label != null) { cmd.createArgument().setValue(FLAG_VERSION_LABEL + m_Label); } } public void setAutoresponse(String response){ if ( response.equals("") || response.equals("null") ) { m_AutoResponse = null; } else { m_AutoResponse = response; } } /** * Checks the value set for the autoResponse. * if it equals "Y" then we return -I-Y * if it equals "N" then we return -I-N * otherwise we return -I */ public void getAutoresponse(Commandline cmd) { if ( m_AutoResponse == null) { cmd.createArgument().setValue(FLAG_AUTORESPONSE_DEF); } else if ( m_AutoResponse.equalsIgnoreCase("Y")) { cmd.createArgument().setValue(FLAG_AUTORESPONSE_YES); } else if ( m_AutoResponse.equalsIgnoreCase("N")) { cmd.createArgument().setValue(FLAG_AUTORESPONSE_NO); }else { cmd.createArgument().setValue(FLAG_AUTORESPONSE_DEF); } // end of else } public void setReplaceWritable(String replace) { if ( replace.equals("") || replace.equals("null") ) { m_ReplaceWritable = null; } else { m_ReplaceWritable = replace; } } /** * Checks the value for replacewritable. * if it equals "ask" then we return -GWA * if it equals "merge" then we return -GWM * if it equals "replace" then we return -GWR * if it equals "skip" then we return -GWS * otherwise we return "" */ public void getReplaceWritableCommand(Commandline cmd) { if ( m_ReplaceWritable == null ) { return; } if ( m_ReplaceWritable.equalsIgnoreCase("ask") ) { cmd.createArgument().setValue(FLAG_REPLACEWRITABLE_ASK); } else if ( m_ReplaceWritable.equalsIgnoreCase("merge") ) cmd.createArgument().setValue(FLAG_REPLACEWRITABLE_MERGE); } else if ( m_ReplaceWritable.equalsIgnoreCase("replace") ) { cmd.createArgument().setValue(FLAG_REPLACEWRITABLE_REPLACE); } else if ( m_ReplaceWritable.equalsIgnoreCase("skip") ) { cmd.createArgument().setValue(FLAG_REPLACEWRITABLE_SKIP); } } } ...with the following addition to the javadoc: * <tr> * <td>replaceWritable</td> * <td>How to treat a local writable version of a file. By default, * this isn't treated. When set -GWA (ask), -GWM (merge), -GMR (replace) * or -GWS (skip) is added to the command. * <td>No<td> * </tr> The code added is a new instance variable m_ReplaceWritable, the new methods public void setReplaceWritable(String replace) and public void getReplaceWritableCommand(Commandline cmd) and calling getReplaceWritableCommand from within execute(). I have tested the added code in my environment (repeated: JDK 1.3.1, Ant 1.4.1, MS VSS 6.0) with the following results: Property replaceWritable not specified: No change to before (i.e. no option added to the command). Property replaceWritable="ask": Works only when autoresponse="Y", when it works the same way as replace. When autoresponse="N" the build fails just like before. This probably should be removed. I have left it for reference though! Property replaceWritable="merge": In my environment the local copy is left unchanged, which is what I would expect from skip. This appears to be a bug with MS VSS - works fine otherwise. Property replaceWritable="replace": The local copy is replaced as expected - works fine! Property replaceWritable="skip": The build fails as before with the same message. Since I have a far lesser understanding of the inner workings of Ant than you, I have left the code - because as I understand ss GET returns a warning and not an error in this case and you may have good ways of treating such a situation. I hope that this is useful:)
