CCNet, MSBuild and MsBuildTasks
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.





Blogger comments
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!