#198 Exec Task Output

0.85
closed
Tasks (408)
7
2014-08-25
2004-08-16
No

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!

Discussion

  • Michael Flanakin

    • priority: 5 --> 7
     
  • Rutger Dijkstra

    Rutger Dijkstra - 2004-11-28

    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();

    + }

    + }

    +}

     
  • Gary Feldman

    Gary Feldman - 2005-04-14

    Logged In: YES
    user_id=847172

    I've submitted a very simple patch for this in in the
    Patches database, ID 1183217

    Gary

     
  • Trevor Green

    Trevor Green - 2007-03-06

    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"/>

     
  • Trevor Green

    Trevor Green - 2007-03-06

    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"/>

     
  • Trevor Green

    Trevor Green - 2007-03-06

    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"/>

     
  • Charles Chan

    Charles Chan - 2010-02-18
    • assigned_to: nobody --> cchan_qa
     
  • Charles Chan

    Charles Chan - 2010-02-19

    This bug has been fixed in the latest CVS head.

    (Using Gary's fix from Patches database, ID 1183217. Thanks.)

     
  • Charles Chan

    Charles Chan - 2010-02-19
    • status: open --> closed
     

Log in to post a comment.

Get latest updates about Open Source Projects, Conferences and News.

Sign up for the SourceForge newsletter:





No, thanks