Background
As part of my current project we track and reporting on a number of source metrics (average/max methods per class, average/max calls per method, average/max cyclomatic complexity etc..) on a daily basis.
Unfortunately, as we are running Visual Studio/Team Foundation 2005 we are unable to use the built in metrics (and hence the integration with TFS) and so have fallen back to using an external product which has been filling this space for a long time called Source Monitor.
Anyway, when I joined the project this tracking was being done manually; a daily routine was in place where someone would...
- Perform a local get of the latest source.
- Check out the Source Monitor .smp project file.
- Open this and create a new check point (which for our code base takes a couple of minutes).
- And finally export the results as xml before running an xslt to generate html for pasting into an email for circulation.
Well, as we are running a pretty tight continuous integration ship (yes we had to build our own CI notification on top of VS 2005), this struck me from day one as an obvious candidate for improved automation. Today I finally got the few hours it took to knock up a custom MSBuild task to do the job.
Note: I honestly didn't want to go to this level of effort, but Source Monitor requires an 'instruction' file to be generated when executing at the command line and having searched the web, was actually surprised to only find a few examples of doing this. Most of them were very much CruiseControl or Nant focussed.
Implementation
The actual implementation takes its inspiration from these two posts (Dan Rigsby / Robin Curry), but instead of placing all the logic directly within xml, tries to encapsulate as much of the ugly glue into the c# implementation within the task… hopefully simplifying and improving the maintainability of the actual calling build script.
The task itself derives from ToolTask. It adds 3 properties (2 of which are 'required'):
- ProjectFile (Required): The path of the .smp file to execute Source Monitor against (you should create this interactively using the Source Monitor interface).
- SummaryFile (Required): The path to write the Source Monitor summary report (in xml format).
- CheckPointName: The name to give to the new checkpoint within the project (If this is '' or omitted Source Monitor will use its own logic to name the checkpoint).
The following properties (inherited from ToolTask) are also relevant:
- ToolPath: The optional location to find SourceMonitor.exe (defaults to 'C:\Program Files\SourceMonitor\')
- ToolName: The optional filename of SourceMonitor.exe (defaults to 'SourceMonitor.exe' - you should only need to change this if a new version of the tool changes the exe name)
Usage
So, the source to the task is attached in the zip file below (you will need to install Source Monitor separately yourself from here). Also included in this is a demonstration MSBuild .proj file (the next best thing to some proper unit tests) – hitting F5 from within Visual Studio will run this in debug, and it will generate a summary and details report on its own source code! If you simply want to use the task as-is within TFS server build then you will need to compile it then make the following changes to your build script:
- Copy the binary dll to: C:\Program Files\MsBuild\MSJ.MSBuildTasks.dll (or anywhere else, but this easily accessible from within scripts using the built-in $(MSBuildExtensionsPath) variable).
- Reference the task at the top of your script:
<UsingTask AssemblyFile="$(MSBuildExtensionsPath)\MSJ.MSBuildTasks.dll" TaskName="SourceMonitor" />
- Call the task at an appropriate point (obviously this must be after the source is downloaded); I personally prefer BeforeCompile:
Note: As we store the Source Monitor project in source control, we must check this out to make it writeable before we execute the task (Source Monitor needs write access to add the new checkpoint).
<PropertyGroup>
<!-- TF.exe -->
<TF>"$(TeamBuildRefPath)\..\tf.exe"</TF>
<SourceMonitorProjectFile>$(SolutionRoot)\SolutionProjectFile.smp</SourceMonitorProjectFile>
</PropertyGroup>
<Target Name="BeforeCompile">
<!-- Check out Source Monitor project (it needs to be writeable to add new checkpoint). -->
<Exec
WorkingDirectory="$(SolutionRoot)"
Command="$(TF) checkout "$(SourceMonitorProjectFile)"" />
<!-- Execute Source Monitor to add new checkpoint (named after the build number) and export summary/detail reports as xml. -->
<SourceMonitor
ProjectFile="$(SourceMonitorProjectFile)"
SummaryFile="$(SolutionRoot)\Build Reports\SourceMonitorSummary.xml"
DetailsFile="$(SolutionRoot)\Build Reports\SourceMonitorDetails.xml"
CheckPointName="$(BuildNumber)"
/>
<!-- Check in Source Monitor project with new checkpoint (use /override flag to avoid check-in policy issues). -->
<Exec
WorkingDirectory="$(SolutionRoot)"
Command="$(TF) checkin /comment:"Build script auto update" /noprompt /override:"Build script auto update" "$(SourceMonitorProjectFile)"" />
<!-- Run xslt batch file against xml or anything here… -->
</Target>
Note: If you install Source Monitor to a non default location (or you are running on a 64 bit server like us) you must specify this using the ToolLocation attribute:
<SourceMonitor
ToolLocation="C:\Program Files (x86)\SourceMonitor\"
ProjectFile="$(SourceMonitorProjectFile)"
...
/>
Download: