// 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 (claytonharbour@sporadicism.com)
using System;
using System.Collections;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Globalization;
using NAnt.Core;
using NAnt.Core.Attributes;
using NAnt.Core.Tasks;
using NAnt.Core.Types;
using NAnt.Core.Util;
namespace NAnt.SourceControl.Tasks {
///
/// A base class for creating tasks for executing CVS client commands on a
/// CVS repository.
///
public abstract class AbstractSourceControlTask : ExternalProgramBase {
#region Protected Static Fields
///
/// Name of the environmental variable specifying a users' home
/// in a *nix environment.
///
protected const String EnvHome = "HOME";
///
/// Used on windows to specify the location of application data.
///
protected const string AppData = "APPDATA";
///
/// The environment variable that holds path information.
///
protected const String PathVariable = "PATH";
///
/// The environment variable that holds the location of the
/// .cvspass file.
///
protected const string CvsPassFileVariable = "CVS_PASSFILE";
///
/// Property name used to specify the source control executable. This is
/// used as a readonly property.
///
protected const string PropExeName = "sourcecontrol.exename";
#endregion
#region Private Instance Fields
private string _exeName;
private string _root;
private DirectoryInfo _destinationDirectory;
private string _password;
private FileInfo _passFile;
private string _commandName;
private string _commandLine = null;
private Hashtable _commandOptions = new Hashtable();
private string _commandLineArguments;
private Hashtable _globalOptions = new Hashtable();
private FileInfo _ssh;
private FileSet _fileset = new FileSet();
#endregion Private Instance Fields
#region Private Static Fields
private static readonly log4net.ILog Logger =
log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
#endregion Private Static Fields
#region Protected Instance Constructors
///
/// Initializes a new instance of the
/// class.
///
protected AbstractSourceControlTask () : base() {
}
#endregion Protected Instance Constructors
#region Protected Instance Properties
///
/// The name of the passfile, overriden for each version control system (VCS).
///
protected abstract string PassFileName {get;}
///
/// The path to the specific home directory of the version control system,
/// this can be where the binary files are kept, or other app
/// information.
///
protected DirectoryInfo VcsHome {
get {
string vcsHome =
Environment.GetEnvironmentVariable(VcsHomeEnv);
if (null != vcsHome) {
if (Directory.Exists(vcsHome)) {
return new DirectoryInfo(vcsHome);
}
}
return null;
}
}
///
/// The environment variable that defines where the version control system
/// (VCS) home variable is kept.
///
protected abstract string VcsHomeEnv {get;}
///
/// The name of the version control system (VCS) executable file.
///
protected abstract string VcsExeName {get;}
#endregion
#region Public Instance Properties
///
///
/// The root variable contains information on how to locate a repository.
/// Although this information is in different formats it typically must
/// define the following:
///
/// - server location
/// - protocol used to communicate with the repository
/// - repository location on the server
/// - project location in the repository
///
///
///
[StringValidator(AllowEmpty=false)]
public virtual string Root {
get {return _root;}
set {_root = value;}
}
///
/// Destination directory for the local sandbox. If destination is not specified
/// then the current directory is used.
///
///
/// Root path of the local sandbox.
///
///
///
/// Root path of the local sandbox.
///
///
[TaskAttribute("destination", Required=false)]
public virtual DirectoryInfo DestinationDirectory {
get {
if (null == this._destinationDirectory) {
this._destinationDirectory = new DirectoryInfo(Environment.CurrentDirectory);
}
return this._destinationDirectory;
}
set { this._destinationDirectory = value; }
}
///
/// The password for logging in to the repository.
///
///
/// The password for logging in to the repository.
///
[TaskAttribute("password", Required=false)]
[Obsolete("Use task instead.", true)]
public virtual string Password {
get { return _password;}
set { _password = StringUtils.ConvertEmptyToNull(value); }
}
///
/// The full path to the cached password file. If not specified then the
/// environment variables are used to try and locate the file.
///
[TaskAttribute("passfile")]
public virtual FileInfo PassFile {
get { return _passFile; }
set { _passFile = value; }
}
///
/// Holds a collection of globally available options.
///
public Hashtable GlobalOptions {
get {return _globalOptions;}
set {_globalOptions = value;}
}
///
/// A collection of options that can be used to modify the default behavoir
/// of the version control commands. See the sub-tasks for implementation
/// specifics.
///
public Hashtable CommandOptions {
get { return _commandOptions;}
set { _commandOptions = value; }
}
///
/// Command-line arguments for the program. The command line arguments are used to specify
/// any cvs command options that are not available as attributes. These are appended
/// after the command itself and are additive to whatever attributes are currently specified.
///
///
/// <cvs-checkout cvsroot=":pserver:anonymous@cvs.sourceforge.net:/cvsroot/nant"
/// module="nant"
/// destination="e:\test\merillcornish\working"
/// readonly="true"
/// quiet="true"
/// commandline="-n"
/// cvsfullpath="C:\Program Files\TortoiseCVS\cvs.exe"
/// />
///
/// Produces the cvs command:
/// c:\Program Files\TortoiseCVS\cvs.exe -d:pserver:anonymous@cvs.sourceforge.net:/cvsroot/nant -q checkout -n nant
///
[TaskAttribute("commandline")]
public string CommandLineArguments {
get {return _commandLineArguments;}
set {_commandLineArguments = StringUtils.ConvertEmptyToNull(value);}
}
///
/// The name of the command that is going to be executed.
///
public virtual string CommandName {
get {return _commandName;}
set {_commandName = value;}
}
///
/// Used to specify the version control system (VCS) files that are going
/// to be acted on.
///
[BuildElement("fileset")]
public virtual FileSet VcsFileSet {
get { return _fileset; }
set { _fileset = value; }
}
///
/// The executable to use for ssh communication.
///
[TaskAttribute("ssh", Required=false)]
public virtual FileInfo Ssh {
get {return _ssh;}
set {_ssh = value;}
}
#endregion Public Instance Properties
#region Protected Instance Properties
///
/// The environment name for the ssh variable.
///
protected abstract string SshEnv {
get;
}
#endregion Protected Instance Properties
#region Override implementation of ExternalProgramBase
///
/// The name of the version control system executable.
///
public override string ExeName {
get {return _exeName;}
set {_exeName = value;}
}
///
/// Get the command line arguments for the task.
///
public override string ProgramArguments {
get {return _commandLine;}
}
///
/// Build up the command line arguments, determine which executable is being
/// used and find the path to that executable and set the working
/// directory.
///
/// The process to prepare.
protected override void PrepareProcess (Process process) {
base.PrepareProcess(process);
SetEnvironment(process);
}
#endregion Override implementation of ExternalProgramBase
#region Protected Instance Methods
///
/// Adds a new global option if none exists. If one does exist then
/// the use switch is toggled on or of.
///
/// The common name of the option.
/// The option value or command line switch
/// of the option.
/// true
if the option should be
/// appended to the commandline, otherwise false
.
protected void SetGlobalOption (String name, String value, bool on) {
Option option;
Log(Level.Debug, "Name: {0}", name);
Log(Level.Debug, "Value: {0}",value);
Log(Level.Debug, "On: {0}", on);
if (GlobalOptions.Contains(name)) {
option = (Option)GlobalOptions[name];
} else {
option = new Option();
option.OptionName = name;
option.Value = value;
GlobalOptions.Add(option.OptionName, option);
}
option.IfDefined = on;
}
///
/// Adds a new command option if none exists. If one does exist then
/// the use switch is toggled on or of.
///
/// The common name of the option.
/// The option value or command line switch
/// of the option.
/// true
if the option should be
/// appended to the commandline, otherwise false
.
protected void SetCommandOption (String name, String value, bool on) {
Option option;
if (CommandOptions.Contains(name)) {
option = (Option)CommandOptions[name];
} else {
option = new Option();
option.OptionName = name;
option.Value = value;
CommandOptions.Add(name, option);
}
option.IfDefined = on;
}
///
/// Set up the environment variables for a process.
///
/// A process to setup.
protected virtual void SetEnvironment (Process process) {
if (Ssh != null && !Ssh.Exists) {
FileInfo tempLookup = DeriveFullPathFromEnv(PathVariable, Ssh.Name);
if (null == tempLookup) {
tempLookup = DeriveFullPathFromEnv(PathVariable, Ssh.Name + ".exe");
}
if (null != tempLookup) {
Ssh = tempLookup;
}
}
if (Ssh != null) {
try {
process.StartInfo.EnvironmentVariables.Add(SshEnv, Ssh.FullName);
} catch (System.ArgumentException e) {
Logger.Warn("Possibility cvs_rsh key has already been added.", e);
}
}
if (null != this.PassFile) {
if (process.StartInfo.EnvironmentVariables.ContainsKey(CvsPassFileVariable)) {
process.StartInfo.EnvironmentVariables[CvsPassFileVariable] = this.PassFile.FullName;
} else {
process.StartInfo.EnvironmentVariables.Add(CvsPassFileVariable, PassFile.FullName);
}
}
Log(Level.Verbose, "Using ssh binary: {0}", process.StartInfo.EnvironmentVariables[SshEnv]);
Log(Level.Verbose, "Using .cvspass file: {0}", process.StartInfo.EnvironmentVariables[CvsPassFileVariable]);
}
///
/// Append the files specified in the fileset to the command line argument.
/// Files are changed to use a relative path from the working directory
/// that the task is spawned in.
///
protected void AppendFiles () {
foreach (string pathname in VcsFileSet.FileNames) {
string relativePath = pathname.Replace(DestinationDirectory.FullName, "");
if (relativePath.IndexOf('/') == 0 || relativePath.IndexOf('\\') == 0) {
relativePath = relativePath.Substring(1, relativePath.Length - 1);
}
relativePath = relativePath.Replace("\\", "/");
Arguments.Add(new Argument("\"" + relativePath + "\""));
}
}
///
/// Derive the location of the version control system from the environment
/// variable PATH
.
///
/// The file information of the version control system,
/// or null
if this cannot be found.
protected FileInfo DeriveVcsFromEnvironment () {
FileInfo vcsFile =
DeriveFullPathFromEnv(VcsHomeEnv, VcsExeName);
if (null == vcsFile) {
vcsFile = DeriveFullPathFromEnv(PathVariable, VcsExeName);
}
return vcsFile;
}
#endregion Protected Instance Methods
#region Private Instance Methods
private FileInfo DeriveFullPathFromEnv(string environmentVar, string fileName) {
string environmentValue = StringUtils.ConvertEmptyToNull(
System.Environment.GetEnvironmentVariable(environmentVar));
Log(Level.Debug, "Environment variable: {0}", environmentVar);
Log(Level.Debug, "Environment value: {0}", environmentValue);
if (environmentValue != null) {
string[] environmentPaths = environmentValue.Split(Path.PathSeparator);
foreach (string environmentPath in environmentPaths) {
if (environmentPath == null) {
continue;
}
// remove leading or trailing quotes, which are valid for
// individual entries in PATH but are considered invalid
// path characters
string cleanPath = environmentPath.Trim('\"');
Log(Level.Debug, "Environment Path: {0}", cleanPath);
Log(Level.Debug, "FileName: {0}", fileName);
string fileFullName = Path.Combine(cleanPath, fileName);
Log(Level.Debug, "FileFullName: {0}", fileFullName);
if (environmentPath.IndexOf(fileName) > -1 && File.Exists(fileName)) {
if (!(Path.GetDirectoryName(fileName).IndexOf(
Path.GetDirectoryName(System.AppDomain.CurrentDomain.BaseDirectory)) > 1)) {
return new FileInfo(fileName);
}
}
if (fileFullName.IndexOf(fileName) > -1 && File.Exists(fileFullName)) {
if (Path.GetDirectoryName(fileFullName).IndexOf(
Path.GetDirectoryName(System.AppDomain.CurrentDomain.BaseDirectory)) == -1) {
return new FileInfo(fileFullName);
}
}
}
}
return null;
}
#endregion Private Instance Methods
}
}