January 8, 2007 CI Factory, Continuous Integration

Update/Fix AsyncExec Task and Post Build Extension

In a previous post I introduced the task AsyncExec.  For the way I was using it in that post it worked great.  I hinted at using it to execute a process, letting the child process continue after the parent had exited.  The task was not in fact capable of this.  The ancestor task ExternalProgramBase spawned two threads for collecting the Standard Out and Error Out streams.  These threads as well as the one spawned for calling the base method ExecuteTask were not background threads.  These three threads plus the call to WaitForExit on the System.Diagnostics.Process object were causing the parent process to hang around.  So four things needed to be addressed to allow for the desired behavior.

I ended up replacing some of the ancestor functionality and spawning no new threads.  I manage the process and when or whether to wait for it to exit.  This uses much fewer resources and in turn is much faster.

One of the key things to note is that the output attribute does not work.  Take the example for the exec task in the NAnt docs, ping.  You could add the output attribute to this and quickly have a log of the command.  I decided not to try and figure out if this task was going to complete before or after the parent process so you will need a shim process to redirect output to a file.  This can easily be done with cmd and >.

<target name=test>

  <delete file=C:\temp\ping.txt if=${file::exists(‘C:\temp\ping.txt’)} />

  <asyncexec program=cmd.exe

            commandline=/C ping 192.168.1.3 > C:\temp\ping.txt

            taskname=ping

            resultproperty=ping

            failonerror=false

            verbose=true />

  <asyncexec program=notepad.exe waitforexit=false />

  <waitforexit>

    <tasknames>

      <string value=ping/>

    tasknames>

  waitforexit>

  <echo message=The exit code for pinging 192.168.1.3 was ${ping}./>

  <loadfile file=C:\temp\ping.txt property=output />

  <echo message=${output}/>

target>

This script will begin executing the ping command and continue on to opening notepad before the ping command has finished.  The ping command is being piped to the file ‘c:\temp\ping.txt’.  Here is the example scripts output:

test:

[delete] Deleting file C:\temp\ping.txt.
[asyncexec] Starting ‘cmd.exe (/C ping 192.168.1.3 > C:\temp\ping.txt)’ in ‘C:\Projects\CI Factory\Current\Product\nAnt Scratch’
[asyncexec] Starting ‘notepad.exe ()’ in ‘C:\Projects\CI Factory\Current\Product\nAnt Scratch’
[asyncexec] C:\Projects\CI Factory\Current\Product\nAnt Scratch\Scratch.build.xml(9,6):
[asyncexec] External Program Failed: cmd.exe (return code was 1)
[echo] The exit code for pinging 192.168.1.3 was 1.
[echo]
[echo] Pinging 192.168.1.3 with 32 bytes of data:
[echo]
[echo] Request timed out.
[echo] Request timed out.
[echo] Request timed out.
[echo] Request timed out.
[echo]
[echo] Ping statistics for 192.168.1.3:
[echo] Packets: Sent = 4, Received = 0, Lost = 4 (100% loss),
[echo]

BUILD SUCCEEDED – 1 non-fatal error(s), 0 warning(s)

Total time: 21.3 seconds.

I have been working a good bit with the Post Build extension for CI Factory; I mention this in my post about the Backup Package.  While getting this fine tuned I noticed the Post Shim continued to execute until the Post Build completed.  This of course defeated at least half the purpose of the Post Build and even more of the Post Shim.  The improvements that I have made to the asyncexec task forced a small change to the Post Shim as you can see here:

<asyncexec taskname=PostBuild waitforexit=false program=${NantExePath} failonerror=false>

  <arg line=-buildfile:Post.Build.xml/>

  <arg line=-logger:NAnt.Core.XmlLogger />

  <arg line=-logfile:”${Common.ArtifactDirectoryPath}\postbuildlog.xml” />

  <arg line=@”${Common.PropertiesFile}” />

  <arg line=-D:CCNetLogFilePath=”${CCNetLogFilePath}” />

  <arg line=${CCNetForcedByArg} />

  <arg line=PostBuild/>

asyncexec>

The attribute taskname used to be required, but now is not, and there is a new attribute waitforexit. Waitforexit’s default is true, meaning that the parent process will not exit until the child process has exited. There is a relationship between waitforexit and taskname. Taskname is used to invoke the task waitforexit. Allowing you to determine when to wait in the script. So if you want to wait later in the script, as in the ping example, set the taskname to something and the waitforexit attribute of asyncexec to true. When the waitforexit is set to true, and the taskname is not set, the task will not act asynchronously, it will wait for the child process to exit before continuing. If you set the waitforexit attribute to false, and then do not set the taskname attribute, the process will be spawned with a completely independent life from the parent’s.  Basicaly you want to either set the attribute waitforexit to false or set the taskname and call the waitforexit task.

Here are the bits:

NAnt Stuff

Post Build Extension Zip and Doc

Source for asyncexec and waitforexit Async.Tasks.zip

16,651 Total Views

5 to “Update/Fix AsyncExec Task and Post Build Extension”

Trackbacks/Pingbacks

  1. Hanselminutes Podcast 54 – Squeezing Continuous Integration…

  2. hanselman says:

    Hanselminutes Podcast 54 – Squeezing Continuous Integration…

    My fifty-fourth podcast is up . In this episode we continue the discussion started in Episode 4 – Continuous…

  1. Scott Hanselman's Computer Zen says...

    Hanselminutes Podcast 54 – Squeezing Continuous Integration…

  2. hanselman says...

    Hanselminutes Podcast 54 – Squeezing Continuous Integration…

    My fifty-fourth podcast is up . In this episode we continue the discussion started in Episode 4 – Continuous…

  3. wads says...

    Hi Jay. This is great.

    How do I dynamically add the to the list? I’m trying to run nunit as async and the assemblies I will test are configured from an external file so I won’t know what should go in the until run time.

    thanks

  4. jflowers says...

    There is a function to add values to string list.
    <strings id=”tasks”/>
    <function execute=”${stringlist::add(‘tasks’, ‘ping’)}”/>
    <waitforexit>
    <tasknames refid=”tasks”/>
    </waitforexit>

  5. Rob says...

    Great NAnt tasks. Hope you don’t mind (let me know if you do), I’ve used the VB source you had and converted it to C#.

    I’ve provided the source, and credited you at:
    http://blog.huddle.net/speeding-up-the-build

    I was basically having problems using a trunk build of NAnt with the task, so doing the above allowed me to get it working. Excellent task, has shaved loads of time off our build.

Leave a comment

*

here