Hi!

I have changed the code a little bit to reflect the changes we've been
emailing about yesterday.

Code changes introduce:
 * moving Quiet property from SvnCheckoutTask to AbstractSvnTask,
 * setting Quiet property's value to false as a default.

The code has been tested and works fine for <svn-update>,
<svn-checkout> and generic <svn> task.

Files that need to be patched live in src\Tasks\Svn folder. I am not a
CVS wizard and I attach the whole files as they are not that big. Is
creating a necessary patch as simple as invoking:

cvs diff file > file.patch

from the command-line?

-- 
Marcin Hoppe
http://hopson.blogspot.com
// NAnt - A .NET build tool
// Copyright (C) 2001-2003 Gerry Shaw
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
// Clayton Harbour ([EMAIL PROTECTED])

using System;
using System.Diagnostics;
using System.IO;
using System.Text;

using NAnt.Core;
using NAnt.Core.Attributes;
using NAnt.Core.Tasks;
using NAnt.Core.Types;
using NAnt.Core.Util;

using NAnt.SourceControl.Tasks;

namespace NAnt.Contrib.Tasks.Svn {
    /// <summary>
    /// A base class for creating tasks for executing CVS client commands on a 
    /// CVS repository.
    /// </summary>
    public abstract class AbstractSvnTask : AbstractSourceControlTask {
        #region Protected Static Fields

        /// <summary>
        /// An environment variable that holds path information about where
        /// svn is located.
        /// </summary>
        protected const string SVN_HOME = "SVN_HOME";

        /// <summary>
        /// The prefix used for command arguments.
        /// </summary>
        protected const string ARG_PREFIX = "--";

        /// <summary>
        /// Name of the password file that is used to cash password settings.
        /// </summary>
        protected static readonly String SVN_PASSFILE = 
            Path.Combine(".subversion", "auth");

        /// <summary>
        /// The name of the svn executable.
        /// </summary>
        protected const string SVN_EXE = "svn.exe";

        /// <summary>
        /// Environment variable that holds the executable name that is used for
        /// ssh communication.
        /// </summary>
        protected const string SVN_RSH = "RSH";

        #endregion Protected Static Fields

        #region Protected Instance Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="AbstractSvnTask" /> 
        /// class.
        /// </summary>
        protected AbstractSvnTask () : base() {
                SetPropertiesDefaults();
        }

        #endregion Protected Instance Constructors

        #region Protected Instance Properties

        /// <summary>
        /// The name of the executable.
        /// </summary>
        protected override string VcsExeName {
            get { return SVN_EXE; }
        }

        /// <summary>
        /// The name of the password file.
        /// </summary>
        protected override string PassFileName {
            get { return SVN_PASSFILE; }
        }

        /// <summary>
        /// Name of the home environment variable.
        /// </summary>
        protected override string VcsHomeEnv {
            get { return SVN_HOME; }
        }

        /// <summary>
        /// The name of the ssh/ rsh environment variable.
        /// </summary>
        protected override string SshEnv {
            get { return SVN_RSH; }
        }

        #endregion Protected Instance Properties

        #region Public Instance Properties

        /// <summary>
        /// The full path of the svn executable.
        /// </summary>
        public override string ExeName {
            get {return this.DeriveVcsFromEnvironment().FullName;}
        }

        /// <summary>
        /// <para>
        /// TODO: Add more documentation when I understand all svn root 
possibilities/
        /// protocols.
        /// The svn root is usually in the form of a URL from which the server, 
protocol
        /// and path information can be derived.  Although the path to the 
repository
        /// can also be determined from this the information is more implicit
        /// than explicit.  For example a subversion root URL of:
        ///
        /// http://svn.collab.net/repos/svn/trunk/doc/book/tools
        ///
        /// would have the following components:
        ///     protocol:       http/ web_dav
        ///     username:       anonymous
        ///     servername:     svn.collab.net
        ///     repository:     /repos/svn
        ///     server path:    /trunk/doc/book/tools
        ///     
        ///     In addition the revision path or branch can also be determined 
as
        ///     subversion stores this information as a seperate physical 
directory.
        ///     In this example:
        ///     
        ///     revision: trunk
        /// </para>
        /// </summary>
        [TaskAttribute("uri", Required=true)]
        [StringValidator(AllowEmpty=false)]
        public override string Root {
            get { return base.Root; }
            set { base.Root = StringUtils.ConvertEmptyToNull(value); }
        }

        /// <summary>
        /// The user performing the checkout.
        /// </summary>
        [TaskAttribute("username", Required=false)]
        public string UserName {
            get {return ((Option)this.CommandOptions["username"]).Value;}
            set {this.SetCommandOption("username", 
                     String.Format("username={0}", value), true);}
        }

        /// <summary>
        /// The pasword to use to login to svn.
        /// </summary>
        [TaskAttribute("password", Required=false)]
        public new string Password {
            get {return ((Option)this.CommandOptions["password"]).Value;}
            set {this.SetCommandOption("password", 
String.Format("password={0}", value), true);}
        }

        /// <summary>
        /// Indicates whether the task should be interactive or not.  This is
        /// set to <see langword="false" /> by default, and I don't see a reason
        /// to expose this to the NAnt task.
        /// </summary>
        public bool Interactive {
            get {return ((Option)this.CommandOptions["interactive"]).IfDefined;}
            set {this.SetCommandOption("interactive", "non-interactive", 
!value);}
        }

        /// <summary>
        /// The executable to use for ssh communication.
        /// </summary>
        [TaskAttribute("rsh", Required=false)]
        public override FileInfo Ssh {
            get { return base.Ssh; }
            set { base.Ssh = value; }
        }

        /// <summary>
        /// The command to execute.
        /// </summary>
        [TaskAttribute("command", Required=false)]
        public override string CommandName {
            get { return base.CommandName; }
            set { base.CommandName = value; }
        }

                /// <summary>
                /// Determines if the output should be minimized. The default is
                /// <see langword="true" />.
                /// </summary>
                [TaskAttribute("quiet", Required=false)]
                [BooleanValidator()]
                public bool Quiet {
                        get { return 
((Option)this.CommandOptions["quiet"]).IfDefined; }
                        set { this.SetCommandOption("quiet", "quiet", value); }
                }

        #endregion Public Instance Properties

        #region Override implementation of ExternalProgramBase

        /// <summary>
        /// Build up the command line arguments, determine which executable is 
being
        /// used and find the path to that executable and set the working 
        /// directory.
        /// </summary>
        /// <param name="process">The process to prepare.</param>
        protected override void PrepareProcess (Process process) {
            Log(Level.Verbose, "Command name: {0}", this.CommandName);
            if (null == this.Arguments || 0 == this.Arguments.Count) {
                this.AppendGlobalOptions();
                this.Arguments.Add(new Argument(this.CommandName));
                if (IsRootUsed) {
                    this.Arguments.Add(new Argument(this.Root));
                }

                Log(Level.Debug, "Commandline args null: {0}",
                    ((null == this.CommandLineArguments) ? "yes" : "no"));
                if (null == this.CommandLineArguments) {
                    this.AppendCommandOptions();
                }

                this.AppendFiles();
            }
            if (!Directory.Exists(this.DestinationDirectory.FullName)) {
                Directory.CreateDirectory(this.DestinationDirectory.FullName);
            }
            base.PrepareProcess(process);
            process.StartInfo.FileName = this.ExeName;

            process.StartInfo.WorkingDirectory = 
                this.DestinationDirectory.FullName;
        }

        #endregion Override implementation of ExternalProgramBase

        #region Private Instance Methods

        /// <summary>
        /// Determines if the root is used for the command based on 
        /// the command name.  Returns <code>true</code> if the root
        /// is used, otherwise returns <code>false</code>.
        /// </summary>
        private bool IsRootUsed {
            get {
                if (this.CommandName.IndexOf("co") > -1 ||
                    this.CommandName.IndexOf("checkout") > -1) {
                    return true;
                }
                return false;
            }
        }

        private void AppendGlobalOptions () {
            foreach (Option option in this.GlobalOptions.Values) {
                if (!option.IfDefined || option.UnlessDefined) {
                    // skip option
                    continue;
                }
                this.AddArg(option.Value);
            }
        }

        /// <summary>
        /// Append the command line options or commen names for the options
        /// to the generic options collection.  This is then piped to the
        /// command line as a switch.
        /// </summary>
        private void AppendCommandOptions () {
            foreach (Option option in this.CommandOptions.Values) {
                if (!option.IfDefined || option.UnlessDefined) {
                    // skip option
                    continue;
                }
                this.AddArg(option.Value);
            }
        }

        private void AddArg (String arg) {
            Arguments.Add(new Argument(string.Format("{0}{1}",
                ARG_PREFIX, arg)));
        }

                /// <summary>
                /// Set default values for non-requiered parameters.
                /// </summary>
                private void SetPropertiesDefaults() {
                        this.Interactive = false;
                        this.Quiet = false;
                }

        #endregion Private Instance Methods
    }
}
// NAnt - A .NET build tool
// Copyright (C) 2001-2003 Gerry Shaw
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
// Clayton Harbour ([EMAIL PROTECTED])

using System;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;

using NAnt.Core;
using NAnt.Core.Attributes;
using NAnt.Core.Tasks;
using NAnt.Core.Types;
using NAnt.Core.Util;

namespace NAnt.Contrib.Tasks.Svn {
    /// <summary>
    /// Executes the svn checkout command.
    /// </summary>
    /// <example>
    ///   <para>Checkout Gentle.NET.</para>
    ///   <code>
    ///     <![CDATA[
    /// <svn-update
    ///     destination="c:/dev/src/gentle.net" 
    ///     uri="http://www.mertner.com/svn/repos/projects/gentle"; 
    ///     recursive="true"
    ///     quiet="true"
    ///     username="anonymoose"
    ///     password="Canada" 
    ///     revision="HEAD"
    ///     cache-auth="false"
    ///     config-dir="c:\home"
    /// />
    ///     ]]>
    ///   </code>
    /// </example>
    [TaskName("svn-checkout")]
    public class SvnCheckoutTask : AbstractSvnTask {
        #region Private Instance Fields

        private string COMMAND_NAME = "checkout";

        #endregion Private Instance Fields

        #region Public Instance Constructors

        /// <summary>
        /// Initialize the task, and set the default parameters.
        /// </summary>
        public SvnCheckoutTask () : base() {}

        #endregion Public Instance Constructors

        #region Public Instance Properties

        /// <summary>
        /// Gets the svn command to execute.
        /// </summary>
        /// <value>
        /// The svn command to execute. The default value is "checkout".
        /// </value>
        public override string CommandName {
            get { return this.COMMAND_NAME; }
            set { this.COMMAND_NAME = value; }
        }

        /// <summary>
        /// <see langword="true" /> if the command should be executed 
recursively.
        /// The default is <see langword="true" />.
        /// </summary>
        [TaskAttribute("recursive", Required=false)]
        [BooleanValidator()]
        public bool Recursive {
            get {return 
((Option)this.CommandOptions["non-recursive"]).IfDefined;}
            set {this.SetCommandOption("non-recursive", "non-recursive", 
!value);}
        }

        /// <summary>
        /// The revision to checkout.  If no revision is specified then 
subversion
        /// will return the <code>HEAD</code>.
        /// </summary>
        /// <remarks>
        /// A revision argument can be one of:
        ///        NUMBER       revision number
        ///        "{" DATE "}" revision at start of the date
        ///        "HEAD"       latest in repository
        ///        "BASE"       base rev of item's working copy
        ///        "COMMITTED"  last commit at or before BASE
        ///        "PREV"       revision just before COMMITTED
        /// </remarks>
        [TaskAttribute("revision", Required=false)]
        public string Revision {
            get {return ((Option)this.CommandOptions["revision"]).Value;}
            set {
                string number_regex = @"(\+|-)?[0-9][0-9]*(\.[0-9]*)?";
                string date_regex = 
@"(?<Month>\d{1,2})/(?<Day>\d{1,2})/(?<Year>(?:\d{4}|\d{2}))";
                string magic_ref_regex = @"(HEAD)|(BASE)|(COMMITTED)|(PREV)";
                
                if ((new Regex(number_regex)).IsMatch(value) ||
                    ((new Regex(date_regex)).IsMatch(value)) ||
                    ((new Regex(magic_ref_regex)).IsMatch(value))) {
                    this.SetCommandOption("revision", 
string.Format("revision={0}", value), true);
                } else {
                    throw new 
BuildException(string.Format(CultureInfo.InvariantCulture,
                        "Invalid argument specified: {0}.", value), Location);
                }
            }
        }

        /// <summary>
        /// <see langword="true" /> if the authentiction token should be cached
        /// locally.
        /// </summary>
        [TaskAttribute("cache-auth", Required=false)]
        [BooleanValidator()]
        public bool CacheAuth {
            get {return ((Option)this.CommandOptions["cache-auth"]).IfDefined;}
            set {this.SetCommandOption("cache-auth", "no-auth-cache", !value);}
        }

        /// <summary>
        /// The location of the configuration directory.
        /// </summary>
        [TaskAttribute("config-dir", Required=false)]
        public string ConfigDir {
            get {return ((Option)this.CommandOptions["config-dir"]).Value;}
            set {this.SetCommandOption("config-dir", 
                     String.Format("config-dir={0}", value), true);
            }
        }

        #endregion

        #region Override implementation of ExternalProgramBase

        protected override void PrepareProcess (Process process) {
            base.PrepareProcess(process);
            process.StartInfo.Arguments += " . ";
        }

        #endregion Override implementation of ExternalProgramBase
    }
}

Reply via email to