Ever since setting up our CruiseControl.NET server for our internal projects, I wanted to integrate a Gendarme run to catch all those nasty little things that slip through, like not checking arguments for null
.
Now that I finally found some tuits to do it, the next problem became obvious: doing a quick commit often lead to a quick build fail. Gendarme would have no use if it didn't find anything, no? So the missing piece was integration into Visual Studio. Arthur hacked together a little XSLT+PowerShell and voila, Gendarme now runs on every build right from the studio and populates the “Error List” window with properly linked entries. Yay!
Read on for the details of the implementation. The complete source is downloadable at the top of the article.
First, the XSLT (“gendarme.xslt”), transforming Gendarme’s XML to properly formatted text.
="1.0"
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:call-template name="rules">
<xsl:with-param name="detailnodes" select="//results/rule"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="rules">
<xsl:param name="detailnodes"/>
<xsl:for-each select="$detailnodes">
<xsl:call-template name="defects">
<xsl:with-param name="detailnodes" select="target/defect"/>
</xsl:call-template>
</xsl:for-each>
</xsl:template>
<xsl:template name="defects">
<xsl:param name="detailnodes"/>
<xsl:for-each select="$detailnodes">
<xsl:value-of select="@Source"/>: error :[gendarme]
<xsl:value-of select="../../problem"/> Target: <xsl:value-of select="../@Name"/>.
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Second, the powershell fragment to execute gendarme and transform the output (“gendarme.ps1?).
param($SolutionDir)
function Convert-WithXslt($xmlFilePath, $xsltFilePath, $outputFilePath)
{
$xsltFilePath = resolve-path $xsltFilePath
$xmlFilePath = resolve-path $xmlFilePath
$outputFilePath = resolve-path $outputFilePath
$xslt = new-object system.xml.xsl.xslcompiledtransform
$xslt.load( $xsltFilePath )
$xslt.Transform( $xmlFilePath, $outputFilePath )
}
if(-not $SolutionDir)
{
$SolutionDir = ".";
}
"." > $SolutionDir\bin\gendarme.txt
gendarme.exe -config $SolutionDir\gendarmerules.xml --xml
$SolutionDir\bin\gendarme.xml $SolutionDir\bin\Debug\blahblah.exe
convert-withxslt $SolutionDir\bin\gendarme.xml $SolutionDir\gendarme.xslt
$SolutionDir\bin\gendarme.txt
(get-content $SolutionDir\bin\gendarme.txt) -replace '\(\D?(\d+)\)',
' ($1,1)' | set-content $SolutionDir\bin\gendarme.txt
exit 0
Instead of blahblah.exe, put your own assemblies you want to check.
And last but not least, the post build step to actually run the whole thing.
powershell -command $(SolutionDir)gendarme.ps1 -SolutionDir:$(SolutionDir)
type $(SolutionDir)bin\gendarme.txt
This can be added in your project properties on the “Build Events” tab, as “Post-build event command line”. In our project, this is executed in one of the unit-test projects which has dependencies on all other projects and thus builds last.
Don’t forget to enable the RemoteSigned
execution policy of the powershell; either by running...
set-executionpolicy -executionPolicy RemoteSigned
...from an Administrator’s shell or by adding one or both of the powershell.executionpolicy.reg and powershell.executionpolicy.wow6432.reg files to your registry.