Homepage | About Me | Testing ASP.net Book | Best Blog Posts | Personal Projects | Follow me on Twitter | GitHub | SlideShare | RSS
Blog.BenHall.me.uk

CCNet, MSBuild and MsBuildTasks

Wednesday, June 18, 2008

Recently, I setup a local CruiseControl.NET Server for a project I'm working on. In order for CCNet to execute a MSBuild project, you need to configure CCNet.config, this is found in your installation directory for the server - in my case 'C:\Program Files\CruiseControl.NET\server'.

The basic structure for a ccnet project would look something like this. You have a project and within that you have a series of tasks (you can find a more complete sample on the documentation page - http://ccnet.sourceforge.net/CCNET/Configuring%20the%20Server.html).  These tasks are CCNet tasks, built into the server to use as part of your build process. In my example, I'm using the MSBuild task to build MyProject.sln.

<cruisecontrol>
  <project name="MyProject">
    <artifactDirectory>E:\CCNet\MyProject\Artifacts</artifactDirectory>
    <tasks>
      <msbuild>
        <executable>C:\Windows\Microsoft.NET\Framework\v3.5\MSBuild.exe</executable>
        <workingDirectory>E:\CCNet\MyProject\Build\src\</workingDirectory>
        <projectFile>MyProject.sln</projectFile>
        <buildArgs>/noconsolelogger /p:Configuration=Debug</buildArgs>
        <logger>C:\Program Files\CruiseControl.NET\server\ThoughtWorks.CruiseControl.MSBuild.dll</logger>
        <timeout>900</timeout>
      </msbuild>
    </tasks>
    <publishers>
      <xmllogger logDir="E:\CCNet\MyProject\Logs" />
    </publishers>
     <modificationDelaySeconds>10</modificationDelaySeconds>
  </project>
</cruisecontrol>

At this point, my project happily building, next I want to execute my NUnit tests and have my results displayed in the web dashboard.

After my first attempt of using the CCNet NUnit task, the unit tests failed because I was referencing a file (naughty I know) on the disk and as NUnit shadow copies all of it's assemblies so they aren't locked I realised I needed to disable Shadow Copy. A very simple task I thought, NUnit accepts a parameter on the console application for this.

A quick look at the NUnit CCNet Task page and I was faced with a bit of a problem. The NUnit task looks like this:
<tasks>
   <nunit>
       <path>D:\dev\ccnet\ccnet\tools\nunit\nunit-console.exe</path>
       <assemblies>
          <assembly>D:\dev\MyProject.Tests\bin\Debug\MyProject.Tests.dll</assembly>
        </assemblies>
    </nunit>
</tasks>

Notice, no element for setting the command line properties!!! Such a core feature I can't believe it was missed. After a quick chat with the build manager at Red Gate, he pointed me towards http://msbuildtasks.tigris.org/ and their NUnit Task. This is a set of MSBuild Community Tasks which you can use as part of your msbuild script to perform various tasks - such as executing nunit tests.

Creating a MSBuild Script using MSBuild community tasks

MSBuild is a build system which defines how to build your .Net solutions. The script is based on xml, the basic outline is below:

<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Test" xmlns="
http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets"/>
    <!-- Include MSBuild tasks here -->
</Project>

As part of this I am importing the MSBuild community tasks targets file, this will allow me to use the tasks later on. Notice at the top within the project element, I have defined a DefaultTarget (DefaultTargets="Test"). As far as I can tell, this is the target which will be executed first when the script is built, this will make more sense in a moment.

Once we have this in place, we need to define some tasks. In the example below, the first target is the build. This target has a single task, run the MSBuild task against my solution with the configuration as release. This will execute MSBuild to compile my application - excellent!

After the build target, we have the 'Test' target, as defined in the DefaultTargets this is what is executed first.  I then use the DependsOnTarget to define the task which should be run before the current task, the 'Build' target could also depend on other targets. As a result, everything gets built in order based on their dependencies. This Test Target executed NUnit and runs the unit tests for MyProject.Tests.dll and outputs the results to nunit-results.xml. The continue on error property defines if the build should fail if a test fails - which it should!

<Target Name="Build">
  <MSBuild Projects="MyProject.sln" Properties="Configuration=Release" />
</Target>
<Target Name="Test" DependsOnTargets="Build">
    <Message Text="Tests to run"></Message>
    <NUnit Assemblies=""..\bin\Release\MyProject.Tests.dll""
           ContinueOnError="false"
           ToolPath="E:\CCNet\Tools\NUnit\"
           DisableShadowCopy="true"
           OutputXmlFile="$(Logs)\nunit-results.xml" />
</Target>

The most important line for me was the ability to DisableShadowCopy as a property, the reason why I had to use the community tasks.

By using this, I can now have CCNet run this MSBuild script which in turn builds and executes my unit tests. Hopefully, this gives you a very quick overview of how to use the MSBuild community tasks. In a later post, I will explain my complete MSBuild script for my current project and how I utilise the community tasks.

Note:

There are more advanced ways of managing your ccnet config and your MSBuild scripts, if you are managing a number of different projects I would really recommend you investigate different ways of managing the config.  This post was just how to manage your first project.

Technorati Tags: ,

Labels: ,

Blogger comments

At 7:36 PM, Anonymous Kit said...
Ben,

I just ran into this issue and after a couple of hours of banging my head against the wall, I downloaded the CC.NET source code and modified the NUnitArgument.cs file to include the /noshadow parameter. Works like a charm!

If you're interested, here are the steps:

1. Download the source from: http://ccnetlive.thoughtworks.com/CCNet-builds/1.4/1.4.0.3507/CruiseControl.NET-1.4.0.3507.source.zip

2. Download the modified NUnit args file from:
http://www.kitsirota.com/media/code/cc.net/

3. Unzip CruiseControl.NET-1.4.0.3507.source.zip

4. Copy NUnitArguments.cs to {cc.net_source_root}\project\core\tasks\NUnitArguments.cs

5. Run {cc.net_source_root}\b.bat

6. Navigate to {cc.net_source_root}\projects\server\ and run ccnet.exe

No additional packages, no overhead, Enjoy!