Making Sure FxCop Warnings and Errors Break the Build

Thursday, June 18, 2009 – 9:40 AM

FxCop is a great tool but one thing I don’t like is that there’s no simple way to cause an MSBuild script to fail when it encounters an FxCop issue. FxCopCmd.exe doesn’t fail when it finds an error or warning. It returns an error exit code when a catastrophic failure occurs not just an analysis issue.

Failing the build when you have too many FxCop issues is key to getting clean and staying clean as part of your Continuous Integration (CI) build. Here’s how to setup and FxCop target to analyze the FxCop report and fail the build when analysis errors or warnings are reported…

I’ve configured this before on CI servers but this post prompted me to write about it, Partly because the code as posted has some issues. To get this working you need to write an MSBuild target which runs FxCopCmd.exe and then examines the XML output it generates to detect warnings and errors.

<UsingTask TaskName="MSBuild.Community.Tasks.XmlRead"
    AssemblyFile="$(ToolsBinPath)\MSBuild.Community.Tasks.dll" />

<PropertyGroup>
    <FxCop_CriticalErrors>0</FxCop_CriticalErrors>
    <FxCop_Errors>0</FxCop_Errors>
    <FxCop_CriticalWarnings>0</FxCop_CriticalWarnings>
    <FxCop_Warnings>0</FxCop_Warnings>
    <FxCop_TotalErrors>0</FxCop_TotalErrors>
    <FxCop_OutputPath>$(StaticAnalysis_OutputPath)\FxCop.xml</FxCop_OutputPath>
</PropertyGroup>

<Target Name="FxCop" DependsOnTargets="Compile">
    <ItemGroup>
        <FxCop_Assemblies 
            Include="$(BinaryOutputRoot)\$(Configuration)\**\NBody.*.dll; 
                $(BinaryOutputRoot)\$(Configuration)\NBody.*.exe;"
            Exclude="$(BinaryOutputRoot)\$(Configuration)\*.Tests.dll; 
                $(BinaryOutputRoot)\$(Configuration)\*.AcceptanceTests.dll; 
                $(BinaryOutputRoot)\$(Configuration)\*.vshost.exe"/>
    </ItemGroup>
    <Copy SourceFiles="$(ToolsBinPath)\FxCop\Xml\FxCopReport.xsl" 
                DestinationFolder="$(StaticAnalysis_OutputPath)" 
                SkipUnchangedFiles="true" />
    <Exec Command="$(ToolsBinPath)\FxCop\FxCopCmd.exe 
                @(FxCop_Assemblies->'/f:%(Identity)', ' ') 
                @(CommonCodeAnalysisRules->'/rid:%(Identity)', ' ') 
                /o:$(StaticAnalysis_OutputPath)\FxCop.xml 
                /igc 
                /oxsl:FxCopReport.xsl" 
                IgnoreExitCode="true" />

    <XmlRead ContinueOnError="True"
        XmlFileName="$(FxCop_OutputPath)"
        XPath="string(count(//Issue[@Level='CriticalError']))">
        <Output TaskParameter="Value" PropertyName="FxCop_CriticalErrors" />
    </XmlRead>
    <XmlRead ContinueOnError="True"
        XmlFileName="$(FxCop_OutputPath)"
        XPath="string(count(//Issue[@Level='Error']))">
        <Output TaskParameter="Value" PropertyName="FxCop_Errors" />
    </XmlRead>
    <XmlRead ContinueOnError="True"
        XmlFileName="$(FxCop_OutputPath)"
        XPath="string(count(//Issue[@Level='CriticalWarning']))">
        <Output TaskParameter="Value" PropertyName="FxCop_CriticalWarnings" />
    </XmlRead>
    <XmlRead ContinueOnError="True"
        XmlFileName="$(FxCop_OutputPath)"
        XPath="string(count(//Issue[@Level='Warning']))">
        <Output TaskParameter="Value" PropertyName="FxCop_Warnings" />
    </XmlRead>
    <XmlRead ContinueOnError="True"
        XmlFileName="$(FxCop_OutputPath)"
        XPath="string(count(//Issue))">
        <Output TaskParameter="Value" PropertyName="FxCop_TotalErrors" />
    </XmlRead>

    <Warning Text="FxCop encountered $(FxCop_TotalErrors) rule violations"
            Condition="$(FxCop_TotalErrors) &gt; 0" />

    <Error Text="FxCop encountered $(FxCop_TotalErrors) rule violations"
            Condition="$(FxCop_TotalErrors) &gt; $(FxCopTripWire)" />
</Target>

A couple of things to note:

  • The target copies FxCopReport.xsl to the output folder. This allows you to load FxCop.xml in a browser and pick up the XSL automatically.
  • The XmlRead task isn’t part of the standard MSBuild tasks and can be downloaded from here. These tasks also contain an FxCop task but it suffers from the same issue as using FxCopCmd directly, you still have to process the output.
  • XmlRead will fail if it doesn’t find something which meets the XPath so ContinueOnError attribute must be set to true, otherwise the build will fail when their are no errors.
  • Currently I’m not running FxCop on my test assemblies. This probably isn’t such a great idea. “Test code is production code”.
  • You could add more XmlRead tasks to parse the actual FxCop issues and print them as warnings to the MSBuild output. I’ve not done this as I usually read them from the CI server’s reports page.
  • You could also customize the criterial for failing the build. For example; fail on any critical errors by tolerate more warnings.
  • All the properties and items defined within the task are prefixed FxCop_ this creates a namespace for each task and prevents bugs creeping in due to targets inadvertently sharing properties or items.

Elsewhere I define. This allows me to turn off rules I don’t want and set a tripwire on the number of FxCop issues I’m prepared to accept before the build is considered to have failed.

<!-- 
    FxCop settings...
-->
<ItemGroup>
    <CommonCodeAnalysisRules Include="-Microsoft.Naming#CA1704"/>
    <CommonCodeAnalysisRules Include="-Microsoft.Performance#CA1822"/>
    <CommonCodeAnalysisRules Include="-Microsoft.Performance#CA1823"/>
    <CommonCodeAnalysisRules Include="-Microsoft.Design#CA1014"/>
    <CommonCodeAnalysisRules Include="-Microsoft.Design#CA1020"/>
    <CommonCodeAnalysisRules Include="-Microsoft.Design#CA2210"/>
</ItemGroup>
<PropertyGroup>
    <FxCopTripWire>4</FxCopTripWire>
</PropertyGroup>

<PropertyGroup>
    <StaticAnalysis_OutputPath>..\analysis</StaticAnalysis_OutputPath>       
    <BinaryOutputRoot>NBody.Viewer\bin</BinaryOutputRoot>
    <ToolsBinPath>Tools\Bin</ToolsBinPath>
</PropertyGroup>

So now you can slowly drive down the number of FxCop issues over time. If a developer creates more FxCop issues then they break the build! As the number of FxCop issues drop you can gradually lower the FxCop_Tripwire property value. Ideally it should be zero!

  1. 7 Responses to “Making Sure FxCop Warnings and Errors Break the Build”

  2. I like your tripwire idea and will see if I can apply it elsewhere (might help in introducing StyleCop and/or Gendarme).

    We’re pretty aggressive about applying Code Analysis / FxCop, so I find a simpler approach I wrote about recently (http://ggreig.livejournal.com/78764.html) does the trick for me.

    By Gavin Greig on Jun 19, 2009

  3. Gavin,

    Yes. If you can just start at “any FxCop issues are a build break” then that’s the way to go. I seem to go through life inheriting teams and projects with debt.

    Thanks for the post. it’s given me another idea for fixing some other FxCop issues.

    Thanks,

    Ade

    By Ade Miller on Jun 19, 2009

  4. Hi,

    lots of good ideas and useful snippets in this post! Thanks!

    I thought I might contribute my variation on the theme, with the following
    differences:

    * I encapsulate the FxCop call behind its msbuild community task

    * I wanted to set separate limits for each message category. Quite strict,
    I know :-)

    * I wanted to get the fxcops even when I rebuild just one project in vstudio.
    So in my xxproj I have the following

    32
    65
    75
    2

    * The XmlReads in the blog will still issue a “File not found” warning on
    projects with zero messages. This is because fxcop will not create a report
    for those projects. If you create a dummy one prior to calling the FxCop task,
    then fxcop will delete it!

    The following is in my $(SolutionDir)build.targets

    0

    By Alessio on Nov 13, 2009

  5. Obviously all the useful xml snippet in my previous post got scrapped by the blog engine. Ade, any chance you could fix the problem or republish elsewhere?

    By Alessio on Nov 13, 2009

  6. Alessio,

    I sent email to your gmail account. If you send me the XML I’ll republish the comment.

    Ade

    By Ade Miller on Nov 16, 2009

  7. I was a bit disappointed when I found out the task didn’t have this built in. It was worse when I found out that the SDC task include this feature, but doesn’t appear to allow specifying dependencies.

    Your example is exactly what I was looking for. Thanks!

    By Pedro on Feb 22, 2010

  1. 1 Trackback(s)

  2. Aug 13, 2009: Big Visible Charts,Continuous Integration and Distributed Teams | #2782 - Thinking about agile (small 'a') software development, patterns and practices for building Microsoft .NET applications.

Sorry, comments for this entry are closed at this time.