Part 5: Continuous Integration - The CodeStatisticsBuild - From the software development trenches

Part 5: Continuous Integration - The CodeStatisticsBuild

Published 04 April 07 10:33 AM | cjlotz

This is the fifth post in a series where I document how to setup a Continuous Integration (CI) process using open source tools like CruiseControl.NET, NCover, NUnit, Subversion and Sandcastle together with MSBuild and VS 2005. Part 5 covers the CodeStatisticsBuild and the targets and tasks used by the CodeStatisticsBuild.

Post Updates

  • 11/12/2007 - Integrated NDepend into the statistics build and updated the CC.Net configuration to use build queues and entity references.

MSBuild Scripts

The CodeStatisticsBuild happens at the end of every iteration - once a week as we have 1 week iterations.  Its fires on successful completion of the DeploymentBuild. The purpose of the CodeStatisticsBuild is to generate metrics on our code base that we can use on a weekly basis to spot trends and identify areas of concern.  We generate metrics using FxCop, NCover and NDepend.

The CodeStatisticsBuild is quite simple as it re-uses most of the targets that have already been defined for the DeveloperBuild.  To run FxCop, the BuildAll target of the DeveloperBuild is invoked with the RunCodeAnalysis property set to True.  To run NCover, the CodeCoverage target of the DeveloperBuild is invoked.  To run NDepend, the NDepend target of the DeveloperBuild is invoked.  The only additional tasks required for the CodeStatisticsBuild are tasks to copy the metrics to the CCNetArtifactDirectory to allow CC.NET to merge the results into the build report.

CopyCodeAnalysisResults

If you look at the BuildCode and BuildTests targets that the BuildAll target depends on, you will see notice that the FxCop results are added to a FxCopResults item group.  The only thing left to do is therefore to copy these results for CC.NET to include in the build report.

1 <Target Name="CopyCodeAnalysisResults" 2 Condition=" '$(CCNetProject)' != '' "> 3 4 <Message Text="FxCopResults:$(NEW_LINE)$(TAB)@(FxCopResults->'%(Identity)', '$(NEW_LINE)$(TAB)')$(NEW_LINE)" Importance="low"/> 5 6 <CreateItem Include="$(CCNetArtifactDirectory)\*.$(FxCopFile)"> 7 <Output TaskParameter="Include" ItemName="ExistingFxCopResults"/> 8 </CreateItem> 9 10 <Delete Files="@(ExistingFxCopResults)"/> 11 <Copy SourceFiles="@(FxCopResults)" 12 DestinationFolder="$(CCNetArtifactDirectory)" 13 ContinueOnError="true"/> 14 15 </Target>

Observations:

  • Line 2: We test on the CCNetProject property to determine if the build was invoked via CC.NET.  The CCNetProject property is one of the properties created and passed on by CC.NET when using the MSBuild CC.NET task. 
  • Lines 6-10: The existing FxCop results are deleted.
  • Line 11-13: The new set of FxCop results are copied to the CCNetArtifactDirectory.

CopyCoverageResults

If you look at the CodeCoverage target, you will see notice that the NCover results are merged into a single NCoverSummaryFile.  The only thing left to do is therefore to copy this file for CC.NET to include in the build report.

1 <Target Name="CopyCodeCoverageResults" 2 Condition=" '$(CCNetProject)' != '' "> 3 4 <CreateItem Include="$(CCNetArtifactDirectory)\$(NCoverSummaryFile)"> 5 <Output TaskParameter="Include" ItemName="ExistingNCoverResults"/> 6 </CreateItem> 7 8 <Delete Files="@(ExistingNCoverResults)"/> 9 <Copy SourceFiles="$(CodeMetricsFolder)\$(NCoverSummaryFile)" 10 DestinationFolder="$(CCNetArtifactDirectory)" 11 ContinueOnError="true"/> 12 13 </Target>

Observations:

  • Line 2: We test on the CCNetProject property to determine if the build was invoked via CC.NET.
  • Lines 4-8: The existing NCover summary file is deleted.
  • Line 9-11: The new NCover summary file is copied to the CCNetArtifactDirectory.

CopyNDependResults

If you look at the NDepend target, you will see notice that the NDepend results are created in a NDependOutFolder.  Of these results, we are interested in the NDependResultsFile as it contains the combined metrics for the NDepend run. Unfortunately CC.NET currently is unable to display images as part of the web dashboard so the graphs created by NDepend like Abstractness vs. Instability cannot be displayed.  This will be fixed in a future version of CC.NET.  In the mean time, you can use this workaround if you absolutely want to include the images into your reports.  I have opted to exclude images for now.

1 <Target Name="CopyNDependResults" 2 Condition=" '$(CCNetProject)' != '' "> 3 4 <CreateItem Include="$(CCNetArtifactDirectory)\NDepend\$(NDependResultsFile)"> 5 <Output TaskParameter="Include" ItemName="ExistingNDependResults"/> 6 </CreateItem> 7 8 <Delete Files="@(ExistingNDependResults)"/> 9 10 <Copy SourceFiles="$(NDependOutputFolder)\$(NDependResultsFile)" 11 DestinationFolder="$(CCNetArtifactDirectory)\NDepend" 12 ContinueOnError="true"/> 13 14 </Target>

Observations:

  • Line 2: We test on the CCNetProject property to determine if the build was invoked via CC.NET.
  • Lines 4-6: The existing NDepend results file is deleted.
  • Line 9-11: The new NDepend results file is copied to the CCNetArtifactDirectory.

CruiseControl.NET Configuration

The build server is set use the following CC.NET configuration.  The config is really self explanatory if you are familiar with the different CruiseControl.NET configuration options.

1 <project name="CodeStatisticsBuild" queue="CCNet.Demo" queuePriority="3"> 2 &header; 3 <category>CodeStatistics</category> 4 <artifactDirectory>C:\Projects\CCNet.Demo\Builds\CodeStatisticsBuild\Artifacts</artifactDirectory> 5 6 <triggers> 7 <projectTrigger serverUri="tcp://buildserver.yourdomain.co.za:21234/CruiseManager.rem" project="DeploymentBuild"> 8 <triggerStatus>Success</triggerStatus> 9 <innerTrigger type="intervalTrigger" seconds="30" buildCondition="ForceBuild"/> 10 </projectTrigger> 11 </triggers> 12 13 <state type="state" /> 14 15 <labeller type="defaultlabeller"/> 16 17 <!-- Remove the log files from the previous build as we are creating a complete new build --> 18 <prebuild> 19 <exec> 20 <executable>clean.bat</executable> 21 <baseDirectory>C:\Projects\CCNet.Demo\Builds\CodeStatisticsBuild</baseDirectory> 22 </exec> 23 </prebuild> 24 25 <tasks> 26 <msbuild> 27 <executable>C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\MSBuild.exe</executable> 28 <workingDirectory>C:\Projects\CCNet.Demo</workingDirectory> 29 <projectFile>CCNET.Demo.proj</projectFile> 30 <buildArgs>/noconsolelogger /v:normal /p:FxCopDir="C:\Program Files\Microsoft Visual Studio 8\Team Tools\Static Analysis Tools\FxCop";SolutionName=CCNet.Demo.sln;Configuration=Release;Platform=AnyCPU;DeploymentBuild=false;RunCodeAnalysis=true;Environment=DEV;DocumentationFile=</buildArgs> 31 <targets>BuildAll,CopyCodeAnalysisResults,CodeCoverage,CopyCodeCoverageResults,NDepend,CopyNDependResults</targets> 32 <timeout>3600</timeout> <!-- 1 hour --> 33 <logger>C:\Program Files\CruiseControl.NET\server\ThoughtWorks.CruiseControl.MSBuild.dll</logger> 34 </msbuild> 35 </tasks> 36 37 <publishers> 38 <merge> 39 <files> 40 <file>C:\Projects\CCNet.Demo\Builds\CodeStatisticsBuild\Artifacts\*.CodeAnalysisLog.xml</file> 41 <file>C:\Projects\CCNet.Demo\Builds\CodeStatisticsBuild\Artifacts\CoverageSummary.xml</file> 42 <file>C:\Projects\CCNet.Demo\Builds\CodeStatisticsBuild\Artifacts\NDepend\*.xml</file> 43 </files> 44 </merge> 45 46 <statistics> 47 <statisticList> 48 <firstMatch name="Svn Revision" xpath="//modifications/modification/changeNumber" /> 49 <firstMatch name="Coverage" xpath="//coverageReport/project/@coverage" /> 50 <firstMatch name="ILInstructions" xpath="//ApplicationMetrics/@NILInstruction" /> 51 <firstMatch name="LinesOfCode" xpath="//ApplicationMetrics/@NbLinesOfCode" /> 52 <firstMatch name="LinesOfComment" xpath="//ApplicationMetrics/@NbLinesOfComment" /> 53 <statistic name='FxCop Warnings' xpath="count(//FxCopReport/Targets/Target/Modules/Module/Namespaces/Namespace/Types/Type/Members/Member/Messages/Message/Issue[@Level='Warning'])" /> 54 <statistic name='FxCop Errors' xpath="count(//FxCopReport/Targets/Target/Modules/Module/Namespaces/Namespace/Types/Type/Members/Member/Messages/Message/Issue[@Level='CriticalError'])" /> 55 </statisticList> 56 </statistics> 57 58 &pub; 59 </publishers> 60 61 &links; 62 </project> 63

Observations:

  • Lines 6-11: The build is set to trigger on successful completion of the DeploymentBuild
  • Lines 18-23: Before starting the build, the previous build's log files are removed
  • Lines 38-44: The NCover, FxCop and NDepend results are merged into the build results
  • Lines 47-55: The Statistics publisher is used to publish some additional metrics - see this post for more details.

Next Steps

Finally! That takes care of all the targets used by the CodeStatisticsBuild.  The next post will highlight all the targets required for the CodeDocumentationBuild. Keep watching this space smile_regular

Comments

# LordHits said on April 4, 2007 02:14 PM:

It seems Part 1's url is invalid? I'm getting an error when clicking it.

Thanks.

# cjlotz said on April 4, 2007 08:25 PM:

I accidentally deleted Part 1 using Windows Live Writer :-(  Luckily I had a found a cached copy in Google Reader and I've reposted Part 1.

Thanks

Carel

# LordHits said on April 4, 2007 08:53 PM:

Ouch. Thanks again for this set of articles.

# Thea Burger said on April 16, 2007 06:42 PM:

Hi Carel, does your NCover results show in your build report email?

# cjlotz said on April 17, 2007 08:40 AM:

Thea

Yes, it does show up. Setup your ccservice.exe.config/ccnet.exe.config to include the following

<file name="xsl\NCoverExplorerSummary.xsl" /> in the <xslFiles> node.  This will include the ncover summary report in the build e-mail.  The stylesheets beneath the <xslFiles> node are used by the EmailPublisher when creating the build e-mails.

# Elias said on May 3, 2007 08:23 AM:

Thank you so much for sharing.

This is exactly what I was looking for and the same technologies I was using.

Best to you,

Elias

# Isaac López said on August 1, 2007 01:24 AM:

My Main development its Java, but Right Now I´m Using C# for a  couple projects , this its what I was looking to emulate my Java Development Process

Isaac López

México

# Part 1: Continuous Integration using MSBuild, CruiseControl.NET, FxCop, NUnit, NCover + Subversion - From the software development trenches said on November 14, 2007 08:49 PM:

Pingback from  Part 1: Continuous Integration using MSBuild, CruiseControl.NET, FxCop, NUnit, NCover + Subversion - From the software development trenches

# From the software development trenches said on November 23, 2007 10:22 AM:

In previous posts about Code Metrics and Code Reviews , I explored some metrics and techniques that I

# From the software development trenches said on December 11, 2007 10:40 PM:

This is the fourth post in a series where I document how to setup a Continuous Integration (CI) process

# Using CruiseControl.NET Statistics to monitor your code base - From the software development trenches said on December 11, 2007 10:43 PM:

Pingback from  Using CruiseControl.NET Statistics to monitor your code base - From the software development trenches

Leave a Comment

(required) 
(required) 
(optional)
(required) 

Enter the numbers above: