Hello!

NAnt's exec task does not have the output and append attributes as does
Ant's. I've added this functionality to ExecTask.cs and
ExternalProgramBase.cs. The modified sources are attached. Please add them
to CVS.

Cheers,
  Klemen.
// NAnt - A .NET build tool
// Copyright (C) 2001 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
//
// Gerry Shaw ([EMAIL PROTECTED])

namespace SourceForge.NAnt.Tasks {

    using System;
    using System.IO;
    using SourceForge.NAnt.Attributes;

    /// <summary>Executes a system command.</summary>
    /// <example>
    ///   <para>Ping nant.sourceforge.net.</para>
    ///   <code><![CDATA[<exec program="ping" 
commandline="nant.sourceforge.net"/>]]></code>
    /// </example>
    [TaskName("exec")]
    public class ExecTask : ExternalProgramBase {
        
        string _program = null;
        string _commandline = null;
        string _baseDirectory = null;
        string _workingDirectory = null;
        int _timeout = Int32.MaxValue;
        string _outputFile = null;
        bool _outputAppend = false;
      
         /// <summary>The program to execute without command arguments.</summary>
        [TaskAttribute("program", Required=true)]
        public string FileName  { set { _program = value; } }                
                
        /// <summary>The command line arguments for the program.</summary>
        [TaskAttribute("commandline")]public string Arguments { set { _commandline = 
value; } }

                /// <summary>The file to which the standard output will be 
redirected.</summary>
                /// <remarks>By default, the standard output is redirected to the 
console.</remarks>
                [TaskAttribute("output", Required=false)]
                public string Output { set { _outputFile = value; } }
                
                /// <summary>true if the output file is to be appended to.</summary>
                /// <remarks>False by default.</remarks>
                [TaskAttribute("append", Required=false)]
                public bool Append { set { _outputAppend = value; } }

        public override string ProgramFileName  { get
        { 
            if( null == _baseDirectory || Path.IsPathRooted(_program) )
                return _program;
            else
                return Path.GetFullPath( Path.Combine( BaseDirectory, _program ));
            }
        }    
        public override string ProgramArguments { get { return _commandline; } }
        
        /// <summary>The directory the program is in.</summary>
        /// <remarks><para>The basedir will be evaluated relative to the project's 
BaseDirectory if it is relative.</para></remarks>
        [TaskAttribute("basedir")]
        public override string BaseDirectory    { get { return 
Project.GetFullPath(_baseDirectory); } set { _baseDirectory = value; } }

        /// <summary>The directory in which the command will be executed.</summary>
        /// <remarks><para>The working will be evaluated relative to the project's 
BaseDirectory if it is relative.</para></remarks>
        [TaskAttribute("workingdir")]
        public virtual string WorkingDirectory    { get { return 
Project.GetFullPath(_workingDirectory); } set { _workingDirectory = value; } }
        
        public override string OutputFile { get { return _outputFile; } }
        
        public override bool OutputAppend { get { return _outputAppend; } }

        /// <summary>Stop the build if the command does not finish within the 
specified time.  Specified in milliseconds.  Default is no time out.</summary>
        [TaskAttribute("timeout")]
        [Int32Validator()]
        public override int TimeOut { get { return _timeout; } set { _timeout = value; 
}  }
        
        protected override void ExecuteTask() {
            Log.WriteLine(LogPrefix + "{0} {1}", ProgramFileName, GetCommandLine());
            base.ExecuteTask();
        }

        protected override void PrepareProcess(ref System.Diagnostics.Process process) 
{
            base.PrepareProcess(ref process);
            if(_workingDirectory != null) {
                process.StartInfo.WorkingDirectory = WorkingDirectory;
            }
        }
    }
}
// NAnt - A .NET build tool
// Copyright (C) 2001-2002 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
//
// Gerry Shaw ([EMAIL PROTECTED])
// Scott Hernandez ([EMAIL PROTECTED])

using System;
using System.Collections.Specialized;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Xml;

using SourceForge.NAnt.Attributes;

namespace SourceForge.NAnt.Tasks {

    /// <summary>Provides the abstract base class for tasks that execute external 
applications.</summary>
    public abstract class ExternalProgramBase : Task {

        /// <summary>Gets the application to start.</summary>
        public abstract string ProgramFileName { get; }                

        /// <summary>Gets the command line arguments for the application.</summary>
        public abstract string ProgramArguments { get; }

        /// <summary>The file to which the standard output will be 
redirected.</summary>
        public virtual string OutputFile { get { return null; } }
        
        /// <summary>true if the output file is to be appended to.</summary>
        public virtual bool OutputAppend { get { return false; } }
      
        /// <summary>Gets the working directory for the application.</summary>
        public virtual string BaseDirectory { 
            get {
                if (Project != null) {
                    return Project.BaseDirectory;
                } else {
                    return null;
                }
            }
            set{} //so that it can be overriden.
        }

        /// <summary>The maximum amount of time the application is allowed to execute, 
expressed in milliseconds.  Defaults to no time-out.</summary>
        public virtual int TimeOut { get { return Int32.MaxValue;  } set{} }

        StringCollection _args = new StringCollection();

        /// <summary>Get the command line arguments for the application.</summary>
        protected StringCollection Args {
            get { return _args; }
        }

        protected override void InitializeTask(XmlNode taskNode) {
            // initialize the _args collection
            foreach (XmlNode optionNode in taskNode) {
                                if(optionNode.Name.Equals("arg")) 
                                {

                                        // TODO: decide if we should enforce arg 
elements not being able
                                        // to accept a file and value attribute on the 
same element.
                                        // Ideally this would be done via schema and 
since it doesn't
                                        // really hurt for now I'll leave it in.

                
                                        XmlNode valueNode = 
optionNode.Attributes["value"];
                                        if (valueNode != null) 
                                        {
                                                
_args.Add(Project.ExpandProperties(valueNode.Value));
                                        }

                                        XmlNode fileNode  = 
optionNode.Attributes["file"];
                                        if (fileNode != null) 
                                        {
                                                
_args.Add(Project.GetFullPath(Project.ExpandProperties(fileNode.Value)));
                                        }
                                }
            }
        }

        /// <summary>Get the command line arguments, separated by spaces.</summary>
        public string GetCommandLine() {
            // append any nested <arg> arguments to command line
            StringBuilder arguments = new StringBuilder(ProgramArguments);
            foreach (string arg in _args) {
                arguments = arguments.Append(' ');
                arguments = arguments.Append(arg);
            }
            return arguments.ToString();
        }

        
        /// <summary>
        /// Sets the StartInfo Options and returns a new Process that can be run.
        /// </summary>
        /// <returns>new Process with information about programs to run, etc.</returns>
        protected virtual void PrepareProcess(ref Process process){
            // create process (redirect standard output to temp buffer)
            process.StartInfo.FileName = ProgramFileName;
            process.StartInfo.Arguments = GetCommandLine();
            process.StartInfo.RedirectStandardOutput = true;
            process.StartInfo.RedirectStandardError = false;
            //required to allow redirects
            process.StartInfo.UseShellExecute = false;
            process.StartInfo.WorkingDirectory = BaseDirectory;
        }

        //Starts the process and handles errors.
        protected virtual Process StartProcess() {
            Process p = new Process();
            PrepareProcess(ref p);
            try {
                Log.WriteLineIf(Verbose, LogPrefix + "{0}>{1} {2}", 
p.StartInfo.WorkingDirectory, p.StartInfo.FileName, p.StartInfo.Arguments);
                p.Start();
            } catch (Exception e) {
                throw new BuildException(this.GetType().ToString() + ": Error running 
external program (" + ProgramFileName + "), see build log for details.", Location, e);
            }
            return p;
        }

        protected override void ExecuteTask() {
            Process process = StartProcess();
            try {
                // display standard output
                StreamReader stdOut = process.StandardOutput;
                string output = stdOut.ReadToEnd();
                if (output.Length > 0) {
                    if (OutputFile == null) {
                        int indentLevel = Log.IndentLevel;
                        Log.IndentLevel = 0;
                        Log.WriteLine(output);
                        Log.IndentLevel = indentLevel;
                    } else if (OutputFile != "") {
                        StreamWriter writer = new StreamWriter(OutputFile, 
OutputAppend);
                        writer.Write(output);
                        writer.Close();
                    }
                }

                /*
                // display standard error
                StreamReader stdErr = process.StandardError;
                string errors = stdErr.ReadToEnd();
                if (errors.Length > 0) {
                    int indentLevel = Log.IndentLevel;
                    Log.IndentLevel = 0;
                    Log.WriteLine(errors);
                    Log.IndentLevel = indentLevel;
                }

                */
                // wait for program to exit
                process.WaitForExit(TimeOut);
            } catch (Exception e) {
                throw new BuildException(this.GetType().ToString() + ": Error during 
external program execution (" + ProgramFileName + "), see build log for details.", 
Location, e);
            }

            // Keep the FailOnError check to prevent programs that return non-zero 
even if they are not returning errors.
            if (FailOnError && process!= null && process.ExitCode != 0) {
                throw new BuildException("External program returned errors, see build 
log for details.", Location);
            }
        }
    }
}

Reply via email to