Getting FxCop to break the build

Recently I wrote a long post on our continuous build setup and what we had to do to get it all working.  However, switching on FxCop wasn't quite as simple as I thought it was going to be.

The Problem
You might recall that we are using the FxCop task from the MSBuild Community Tasks project at http://msbuildtasks.tigris.org/ and our build script looked like this:

<FxCop 
    TargetAssemblies="@(FxcopTargets)"
    RuleLibraries="@(FxCopRuleAssemblies)" 
    AnalysisReportFileName="D:\Projects\MyProject\Build\SmokeTest\fxcop.xml"
    DependencyDirectories="$(MSBuildCommunityTasksPath);C:\Program Files\Extreme Optimization\Statistics Library for .NET\bin;D:\Projects\MyProject\Build\Tools\log4net-1.2.10\bin\net\2.0\debug;C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0"
    FailOnError="False"
    ApplyOutXsl="False"
    OutputXslFileName="C:\Program Files\Microsoft FxCop 1.35\Xml\FxCopReport.xsl"
    Verbose="False"
    includeSummaryReport="True"
    ExcludeRules="Microsoft.Design#CA2210;Microsoft.Globalization#CA1303"
   
/>

Now we set FailOnError="True" and thought that would be enough.  Turns out it isn't.  For those who interested, the reasons for this are as follows:

  • Setting FailOnError="True" ensures that the return value of the external FxCop process is returned to MSBuild as a task status (you can see this in the source code for the FxCop task)
  • The FxCop task shells out to FxCopCmd.exe
  • The return values for FxCopCmd.exe are used to indicate a catastrophic failure in the analysis process, not rule violations

In other words, this setting simply ensures that if the code analysis cannot be carried out, then the build will fail.  This is useful, but not what I wanted.

The Solution
All the violations of FxCop rules are written out to an XML file by the above script.  So we have to load that XML file and parse out any violations which we think are important.  Fortunately the MSBuild Community Tasks project include an XmlRead task, which allows you to execute some XPath against a XML document and get the results into an MSBuild property.  Our build script now includes the following:

</PropertyGroup>
    <
FxCopCriticalErrors>0</FxCopCriticalErrors>
    <
FxCopErrors>0</FxCopErrors>
    <
FxCopCriticalWarnings>0</FxCopCriticalWarnings>
</
PropertyGroup>
<XmlRead ContinueOnError="True"
    XmlFileName="D:\Projects\MyProject\Build\SmokeTest\fxcop.xml"
    XPath="string(count(//Issue[@Level='CriticalError']))">
        <
Output TaskParameter="Value" PropertyName="FxCopCriticalErrors" />
</
XmlRead>
<
XmlRead ContinueOnError="True"
    XmlFileName="D:\Projects\MyProject\Build\SmokeTest\fxcop.xml"
    XPath="string(count(//Issue[@Level='Error']))">
        <
Output TaskParameter="Value" PropertyName="FxCopErrors" />
</
XmlRead>
<
XmlRead ContinueOnError="True"
    XmlFileName="D:\Projects\MyProject\Build\SmokeTest\fxcop.xml"
    XPath="string(count(//Issue[@Level='CriticalWarning']))">
        <
Output TaskParameter="Value" PropertyName="FxCopCriticalWarnings" />
</
XmlRead>
<
Error Text="FxCop encountered $(Count) material rule violations"
    Condition="$(FxCopCriticalErrors) &gt; 0 or $(FxCopErrors) &gt; 0 or $(FxCopCriticalWarnings) &gt; 0" />

PropertyGroup>
    <
FxCopCriticalErrors>0</FxCopCriticalErrors>
    <
FxCopErrors>0</FxCopErrors>
    <
FxCopCriticalWarnings>0</FxCopCriticalWarnings>
</
PropertyGroup>
<XmlRead ContinueOnError="True"
    XmlFileName="D:\Projects\MyProject\Build\SmokeTest\fxcop.xml"
    XPath="string(count(//Issue[@Level='CriticalError']))">
        <
Output TaskParameter="Value" PropertyName="FxCopCriticalErrors" />
</
XmlRead>
<
XmlRead ContinueOnError="True"
    XmlFileName="D:\Projects\MyProject\Build\SmokeTest\fxcop.xml"
    XPath="string(count(//Issue[@Level='Error']))">
        <
Output TaskParameter="Value" PropertyName="FxCopErrors" />
</
XmlRead>
<
XmlRead ContinueOnError="True"
    XmlFileName="D:\Projects\MyProject\Build\SmokeTest\fxcop.xml"
    XPath="string(count(//Issue[@Level='CriticalWarning']))">
        <
Output TaskParameter="Value" PropertyName="FxCopCriticalWarnings" />
</
XmlRead>
<
Error Text="FxCop encountered $(Count) material rule violations"
    Condition="$(FxCopCriticalErrors) &gt; 0 or $(FxCopErrors) &gt; 0 or $(FxCopCriticalWarnings) &gt; 0" />

Not only does this work, but it also gives you a lot of control over what you consider important.  For instance, we break the build for Critical Warnings as well as Errors and Critical Errors.  You could also use this script to enforce limits on the types of violations, so you may want to break the build if there are more than 30 warnings.

You may be surprised by the ContinueOnError="True" but this is to cope with the fact the FxCop doesn't produce any XML output if there are no violations.  In this (happy) case, the default property values of 0 will be used as the XmlRead task will not set the property values.

blog comments powered by Disqus