Bugs item #1010223, was opened at 2004-08-16 21:03 Message generated for change (Comment added) made by trevi_trev You can respond by visiting: https://sourceforge.net/tracker/?func=detail&atid=402868&aid=1010223&group_id=31650
Please note that this message will contain a full copy of the comment thread, including the initial issue submission, for this request, not just the latest update. Category: Tasks Group: 0.85 Status: Open Resolution: None Priority: 7 Private: No Submitted By: Michael Flanakin (flanakin) Assigned to: Nobody/Anonymous (nobody) Summary: Exec Task Output Initial Comment: I'm using the Aug 14 nightly build and I'm having a problem the exec task. It isn't a huge problem, just a beautification one. I specified the output attribute, which is working fine, but for some reason, the output is going to both the screen and the file. My understanding was that, when the output attribute was specified, the screen output is hidden. I believe this is how Ant works. If this is the intended mode of operation, there needs to be an option to hide console output. Here's my stripped exec task: <exec program="xyz.exe" output="xyz.log"> <arg value="/a:abc" /> <arg value="/1:123" /> </exec> The reason this is a problem is because the program outputs a screen's worth of text and, on top of that, it runs for each project within a solution - 5 times for my current project. This is all just wasted space that clogs up the build log and makes it harder to read/debug. Thanks in advance! ---------------------------------------------------------------------- Comment By: Trevor Green (trevi_trev) Date: 2007-03-06 10:15 Message: Logged In: YES user_id=832163 Originator: NO Perhaps this functionality can be configured using an attribute on the exec node? Something like <exec program="xyz.exe" output="xyz.log" console="false"/> ---------------------------------------------------------------------- Comment By: Trevor Green (trevi_trev) Date: 2007-03-06 10:14 Message: Logged In: YES user_id=832163 Originator: NO Perhaps this functionality can be configured using an attribute on the exec node? Something like <exec program="xyz.exe" output="xyz.log" console="false"/> ---------------------------------------------------------------------- Comment By: Gary Feldman (garyfx) Date: 2005-04-14 19:29 Message: Logged In: YES user_id=847172 I've submitted a very simple patch for this in in the Patches database, ID 1183217 Gary ---------------------------------------------------------------------- Comment By: Rutger Dijkstra (rutgerdijkstra) Date: 2004-11-28 23:33 Message: Logged In: YES user_id=1131474 Index: src/NAnt.Core/Tasks/ExternalProgramBase.cs ============================================== ===================== RCS file: /cvsroot/nant/nant/src/NAnt.Core/Tasks/ExternalProgram Base.cs,v retrieving revision 1.63 diff -u -r1.63 ExternalProgramBase.cs --- src/NAnt.Core/Tasks/ExternalProgramBase.cs 10 Nov 2004 07:17:23 -0000 1.63 +++ src/NAnt.Core/Tasks/ExternalProgramBase.cs 28 Nov 2004 18:35:13 -0000 @@ -40,8 +40,6 @@ public abstract class ExternalProgramBase : Task { #region Private Instance Fields - private StreamReader _stdError; - private StreamReader _stdOut; private ArgumentCollection _arguments = new ArgumentCollection(); private bool _useRuntimeEngine; private string _exeName; @@ -204,9 +202,16 @@ /// </remarks> public virtual TextWriter OutputWriter { get { - if (_outputWriter == null) { + if(_outputWriter != null) return _outputWriter; + if(Output == null) { _outputWriter = new LogWriter(this, Level.Info, CultureInfo.InvariantCulture); + } else if(string.Compare (Output.Name,"NUL",true) == 0) { + _outputWriter = TextWriter.Null; + } else { + FileMode mode = OutputAppend? FileMode.Append: FileMode.Create; + _outputWriter = TextWriter.Synchronized( + new StreamWriter( Output.Open (mode,FileAccess.Write))); } return _outputWriter; } @@ -227,9 +232,12 @@ /// </remarks> public virtual TextWriter ErrorWriter { get { - if (_errorWriter == null) { + if(_errorWriter != null) return _outputWriter; + if(Output == null) { _errorWriter = new LogWriter(this, Level.Warning, CultureInfo.InvariantCulture); + } else { + _errorWriter = OutputWriter; } return _errorWriter; } @@ -261,27 +269,19 @@ /// <para>The exit code of the external process indicates a failure.</para> /// </exception> protected override void ExecuteTask() { - Thread outputThread = null; - Thread errorThread = null; + Pipe stdoutPipe = null; + Pipe stderrPipe = null; try { - // Start the external process + // Start the external process and the IO-pipes Process process = StartProcess(); - outputThread = new Thread(new ThreadStart (StreamReaderThread_Output)); - errorThread = new Thread(new ThreadStart (StreamReaderThread_Error)); - - _stdOut = process.StandardOutput; - _stdError = process.StandardError; - - outputThread.Start(); - errorThread.Start(); - - // Wait for the process to terminate + stdoutPipe = new Pipe (process.StandardOutput,OutputWriter); + stderrPipe = new Pipe (process.StandardError,ErrorWriter); + + // Wait for everything to finnish process.WaitForExit(TimeOut); - - // Wait for the threads to terminate - outputThread.Join(2000); - errorThread.Join(2000); + stdoutPipe.WaitFinish(2000); + stderrPipe.WaitFinish(2000); if (!process.HasExited) { try { @@ -324,14 +324,12 @@ Location, e); } finally { - // ensure outputThread is always aborted - if (outputThread != null && outputThread.IsAlive) { - outputThread.Abort(); - } - // ensure errorThread is always aborted - if (errorThread != null && errorThread.IsAlive) { - errorThread.Abort(); - } + if(stderrPipe != null) stderrPipe.WaitFinish(0); + if(stdoutPipe != null) stdoutPipe.WaitFinish(0); + ErrorWriter.Close(); + OutputWriter.Close(); + //make sure the writers are re-created on a second run + _errorWriter = _outputWriter = null; } } @@ -427,55 +425,6 @@ #region Private Instance Methods - /// <summary> /// Reads from the stream until the external program is ended. /// </summary> - private void StreamReaderThread_Output() { - StreamReader reader = _stdOut; - bool doAppend = OutputAppend; - - while (true) { - string logContents = reader.ReadLine(); - if (logContents == null) { - break; - } - - // ensure only one thread writes to the log at any time - lock (_lockObject) { - OutputWriter.WriteLine(logContents); - if (Output != null) { - StreamWriter writer = new StreamWriter (Output.FullName, doAppend); - writer.WriteLine(logContents); - doAppend = true; - writer.Close(); - } - } - } - OutputWriter.Flush(); - } - /// <summary> /// Reads from the stream until the external program is ended. /// </summary> - private void StreamReaderThread_Error() { - StreamReader reader = _stdError; - bool doAppend = OutputAppend; - - while (true) { - string logContents = reader.ReadLine(); - if (logContents == null) { - break; - } - - // ensure only one thread writes to the log at any time - lock (_lockObject) { - ErrorWriter.WriteLine(logContents); - if (Output != null) { - StreamWriter writer = new StreamWriter (Output.FullName, doAppend); - writer.WriteLine(logContents); - doAppend = true; - writer.Close(); - } - } - } - ErrorWriter.Flush(); - } - /// <summary> /// Determines the path of the external program that should be executed. /// </summary> Index: tests/NAnt.Core/Tasks/ExecTaskTest.cs ============================================== ===================== RCS file: /cvsroot/nant/nant/tests/NAnt.Core/Tasks/ExecTaskTest. cs,v retrieving revision 1.10 diff -u -r1.10 ExecTaskTest.cs --- tests/NAnt.Core/Tasks/ExecTaskTest.cs 11 Aug 2004 07:17:07 -0000 1.10 +++ tests/NAnt.Core/Tasks/ExecTaskTest.cs 28 Nov 2004 18:21:08 -0000 @@ -74,6 +74,33 @@ // if we get here then we passed, ie, no hang = bug fixed } + [Test] + public void Test_OutputRedirect() { + string output = Path.Combine (this.TempDirectory.FullName,"redirected.txt"); + if(File.Exists(output)) File.Delete(output); + string result = ""; + if (PlatformHelper.IsWin32) { + result = RunBuild(FormatBuildFile ("program='cmd.exe' output='"+output+"'", "<arg value='/c echo Hello, World!'/>")); + } else { + result = RunBuild(FormatBuildFile("program='echo' output='"+output+"'", "<arg value='Hello, World!'/>")); + } + Assert.IsTrue(File.Exists(output),"output redirection to file failed"); + Assert.IsFalse(result.IndexOf("Hello, World!") != - 1, "unexpected text on stdout"); + } + [Test,ExpectedException(typeof(TestBuildException))] + public void Test_OutputRedirectToRoFile() { + string output = Path.Combine (this.TempDirectory.FullName,"ro.txt"); + FileInfo outputInfo = new FileInfo(output); + if(!outputInfo.Exists) outputInfo.Create(); + outputInfo.Attributes = FileAttributes.ReadOnly; + string result = ""; + if (PlatformHelper.IsWin32) { + result = RunBuild(FormatBuildFile ("program='cmd.exe' output='"+output+"'", "<arg value='/c echo Hello, World!'/>")); + } else { + result = RunBuild(FormatBuildFile("program='echo' output='"+output+"'", "<arg value='Hello, World!'/>")); + } + } + private string FormatBuildFile(string attributes, string nestedElements) { return String.Format(CultureInfo.InvariantCulture, _format, attributes, nestedElements); } --- src/NAnt.Core/Util/Pipe.cs +++ src/NAnt.Core/Util/Pipe.cs @@ -0,0 +1,63 @@ +using System; +using System.IO; +using System.Threading; + +namespace NAnt.Core.Util { + /// <summary> + /// Pipes text line-by-line from a TextReader to a TextWriter. + /// </summary> + public class Pipe { + + TextReader reader; + TextWriter writer; + Thread thread; + object synch; + + /// <summary> + /// Create a Pipe without synchronization. + /// </summary> + /// <param name="reader">source</param> + /// <param name="writer">destination</param> + public Pipe(TextReader reader, TextWriter writer):this (reader,writer,null) { + } + /// <summary> + /// Create a Pipe that synchronizes the writes on <c>synch</c>. + /// </summary> + /// <param name="reader"></param> + /// <param name="writer"></param> + /// <param name="synch"></param> + public Pipe(TextReader reader, TextWriter writer, object synch) { + this.reader = reader; + this.writer = writer; + this.synch = synch != null? synch: new Object(); + this.thread = new Thread(new ThreadStart (this.Pump)); + this.thread.Start(); + } + private void Pump() { + do { + string line = reader.ReadLine(); + if(line == null) break; + lock(synch) { + writer.WriteLine(line); + writer.Flush(); + } + } while(true); + } + /// <summary> + /// Wait for the pipe to finish it's job + /// </summary> + public void WaitFinish() { + this.thread.Join(); + } + /// <summary> + /// Wait for the pipe to finish it's job for a maximum of + /// <c>timeout</c> millisecond; after that, abort the thread. + /// </summary> + /// <param name="timeout">timeout in milliseconds</param> + public void WaitFinish(int timeout) { + if(this.thread.Join(timeout)) return; + this.thread.Abort(); + this.thread.Interrupt(); + } + } +} ---------------------------------------------------------------------- You can respond by visiting: https://sourceforge.net/tracker/?func=detail&atid=402868&aid=1010223&group_id=31650 ------------------------------------------------------------------------- Take Surveys. Earn Cash. Influence the Future of IT Join SourceForge.net's Techsay panel and you'll get the chance to share your opinions on IT & business topics through brief surveys-and earn cash http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV _______________________________________________ nant-developers mailing list nant-developers@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/nant-developers