Wednesday, October 11, 2006
Integrating FxCop into CruiseControl.NET
Recently, I needed to integrate FxCop into our CC.NET server. I had a
few design goals for the integration.
To get the output of FxCop to appear on the CC.NET dashboard, I needed to tell FxCop to write its output to a file. Use the "
- I wanted the CC.NET project which built the code and the FxCop project to break separately.
- I wanted the violated FxCop rules to appear on CC.NET dashboard.
- I didn't want the FxCop to checkout from version control or build the code.
- The official page on FxCop/CC.NET integration.
- John Rayner's posts on CC.NET, especially Getting FxCop to break the build
<XmlRead>
task of MSBuild Community
Tasks Project does have that ability.
The steps for my msbuild script are:
- Copy the files to be analyzed, including assemblies they depend on to a temporary folder.
- Run FxCop on the assemblies in question.
- Check the FxCop result and return an appropriate error code.
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="RunCheck" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- Required Import to use MSBuild Community Tasks -->
<Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets"/>
<Target Name="RunCheck">
<CallTarget Targets="Copy" />
<CallTarget Targets="Check" />
<CallTarget Targets="Report" />
</Target>
<Target Name="RunFxCopUI">
<CallTarget Targets="Copy" />
<Exec Command="attrib +R $(FxCopProject)" />
<Exec Command='"$(FxCopExe)" $(FxCopProject)'/>
</Target>
<Target Name="Copy">
<Copy SourceFiles="@(DllsAndPdbs)" DestinationFolder="$(FxCopWorkDirectory)" SkipUnchangedFiles="true"/>
</Target>
<Target Name="Check">
<Exec Command='"$(FxCopCmdExe)" /project:$(FxCopProject) /out:$(FxCopOutput) /directory:$(FxCopWorkDirectory) /forceoutput' />
</Target>
<Target Name="Report">
<XmlRead ContinueOnError="True" XmlFileName="$(FxCopOutput)" XPath="string(count(//Issue[@Level='CriticalError']))">
<Output TaskParameter="Value" PropertyName="FxCopCriticalErrors" />
</XmlRead>
<XmlRead ContinueOnError="True" XmlFileName="$(FxCopOutput)" XPath="string(count(//Issue[@Level='Error']))">
<Output TaskParameter="Value" PropertyName="FxCopErrors" />
</XmlRead>
<XmlRead ContinueOnError="True" XmlFileName="$(FxCopOutput)" XPath="string(count(//Issue[@Level='CriticalWarning']))">
<Output TaskParameter="Value" PropertyName="FxCopCriticalWarnings" />
</XmlRead>
<XmlRead ContinueOnError="True" XmlFileName="$(FxCopOutput)" XPath="string(count(//Issue[@Level='Warning']))">
<Output TaskParameter="Value" PropertyName="FxCopWarnings" />
</XmlRead>
<Math.Add Numbers="$(FxCopCriticalErrors);$(FxCopErrors);$(FxCopCriticalWarnings);$(FxCopWarnings)">
<Output TaskParameter="Result" PropertyName="FxCopRuleViolations" />
</Math.Add>
<Error Text="FxCop encountered $(FxCopRuleViolations) rule violation(s). Critical errors: $(FxCopCriticalErrors). Errors: $(FxCopErrors). Critical warnings: $(FxCopCriticalWarnings). Warnings: $(FxCopWarnings)."
Condition="$(FxCopRuleViolations) > 0" />
</Target>
<ItemGroup>
<DllsAndPdbs Include="..\..\..\ThirdParty\lib\*.dll;bin\debug\*.dll;bin\debug\*.pdb;"/>
</ItemGroup>
<PropertyGroup>
<FxCopWorkDirectory>FxCop</FxCopWorkDirectory>
<FxCopProject>ProjectName.FxCop</FxCopProject>
<FxCopOutput>$(FxCopWorkDirectory)\ProjectName.FxCop.output.xml</FxCopOutput>
</PropertyGroup>
<ItemGroup>
<OutputFiles Include="FxCop\*.dll;FxCop\*.pdb" />
</ItemGroup>
<PropertyGroup>
<FxCopCriticalErrors>0</FxCopCriticalErrors>
<FxCopErrors>0</FxCopErrors>
<FxCopCriticalWarnings>0</FxCopCriticalWarnings>
<FxCopWarnings>0</FxCopWarnings>
</PropertyGroup>
<PropertyGroup>
<ExpectedFxCopCmdPath>C:\Program Files\Microsoft FxCop 1.35\FxCopCmd.exe</ExpectedFxCopCmdPath>
</PropertyGroup>
<Choose>
<When Condition="Exists($(ExpectedFxCopCmdPath))">
<!-- Hope that the expected version of FxCop is installed -->
<PropertyGroup>
<FxCopCmdExe>$(ExpectedFxCopCmdPath)</FxCopCmdExe>
</PropertyGroup>
</When>
<Otherwise>
<!-- Otherwise hope that FxCop is in the path. -->
<PropertyGroup>
<FxCopCmdExe>fxcopcmd.exe</FxCopCmdExe>
</PropertyGroup>
</Otherwise>
</Choose>
</Project>
To get the output of FxCop to appear on the CC.NET dashboard, I needed to tell FxCop to write its output to a file. Use the "
/out:<filename>
"
switch for that. The "/forceoutput
" switch is also
nice because it causes an output file to be written even if no rules
are violated. Then I needed to merge that into the CC.NET build
log. The CC.NET
merge task is intended for that. The gotcha I discovered is
that I needed to put the <merge>
task under the <publishers>
tag, not the <tasks>
tag. If it's under <tasks>
,
it's not executed if the build fails, which foils my goal of getting
the rule violations onto the CC.NET dashboard.
That leaves only the question of how to cause the FxCop CC.NET
project
to run after the main project completes successfully. CC.NET
includes a <projectTrigger>
,
which is perfect for this task. Not only is the project trigger
able to run the dependent project only when the parent project
completes successfully, but it also is smart enough not to run the
dependent project multiple times if the parent project completes while
the child is still running. Here's my ccnet.config:
My biggest disappointment with the result is that I couldn't get the detailed FxCop results to appear on the CC.NET web dashboard page for each build; rather they appear on the "FxCop Report" page, which I decided was not worth the effort to change. However, I really didn't like the look of the standard report, so I edited my<cruisecontrol>
<project name="FxCop">
<publishExceptions>true</publishExceptions>
<triggers>
<projectTrigger project="ParentProject">
<triggerStatus>Success</triggerStatus>
</projectTrigger>
</triggers>
<tasks>
<msbuild>
<timeout>1800</timeout>
<executable>C:\Windows\Microsoft.NET\Framework\v2.0.50727\msbuild.exe</executable>
<projectFile>ProjectFile.Fxcop.msbuild.xml</projectFile>
<buildArgs>/noconsolelogger /v:diag</buildArgs>
<logger>ThoughtWorks.CruiseControl.MsBuild.XmlLogger,ThoughtWorks.CruiseControl.MsBuild.dll</logger>
</msbuild>
</tasks>
<publishers>
<merge>
<files> <file>FxCop\ProjectName.FxCop.output.xml</file> </files>
</merge>
<xmllogger />
</publishers>
</project>
</cruisecontrol>
CruiseControl\webdashboard\dashboard.config
file to use Brian
Likes's XSL.
Comments:
<< Home
i keep getting this error i dont know why
MSB3073: The command "FxCopCmd.exe /project:C:\Projects\Main\Src\Sln\Phoenix.fxcop /out:C:\Public\BuildLogs\fxcop\Phoenix.FxCop.xml" exited with code 4. in Phoenix.msbuild(6, 5)
when i try running it from command line manually it works
any suggestions. help
Post a Comment
MSB3073: The command "FxCopCmd.exe /project:C:\Projects\Main\Src\Sln\Phoenix.fxcop /out:C:\Public\BuildLogs\fxcop\Phoenix.FxCop.xml" exited with code 4. in Phoenix.msbuild(6, 5)
when i try running it from command line manually it works
any suggestions. help
<< Home