Monday, January 29, 2007

FXCop Custom MSBuild Task

FXCop is great for checking code, and it's easy enough to run the command
line utility from MSBuild. This will run the analysis and produce a report
of all the issues, but this has no "teeth", that is, nothing makes anyone
look at the report and fix the issues. In order to have FXCop actually
break the build you need to write a custom task the following is an example
of a simple task that breaks the build if there are any issues. It does
need to be told where to find the report via the report location property


using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Build.Tasks;
using Microsoft.Build.Utilities;
using Microsoft.Build.Framework;
using System.IO;
using System.Reflection;
using System.Diagnostics;
using System.Xml;

namespace Framework.Build.CustomTasks
{
public class CheckFXCop : Task
{


private string _reportLocation;


[Required]
public string ReportLocation
{
get { return _reportLocation; }
set { _reportLocation = value; }
}

/// <summary>
/// Executes the Task
/// </summary>
/// <returns>true if the task successfully executed; otherwise,
false.</returns>
public override bool Execute()
{

bool returnVal = true;
int numIssues = 0;

try
{

FileStream xmlFileStream = new
FileStream(this.ReportLocation, FileMode.Open);

XmlTextReader fxReport = new XmlTextReader(xmlFileStream);

while (fxReport.Read())
{

if (fxReport.LocalName == "Issue")
{
returnVal = false;
numIssues++;
}


}

if (!returnVal)
{

Log.LogError("There are " + numIssues.ToString() + "
FXCop issue(s). Please review the report");

}


return returnVal;

}
catch
{
return false;

}

}


}
}

3 comments:

Julien Couvreur said...

Here's my own, which adds actually running fxCop as part of the task:


using System;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using System.IO;
using System.Xml;

[assembly: CLSCompliant(true)]
namespace FxCopTask
{
public class FxCopTask : ToolTask
{
public override bool Execute()
{
try
{
int numIssues = 0;

bool successStatus = base.Execute();
if (!successStatus)
{
return successStatus;
}

if (!File.Exists(this.fxCopOutput))
{
return true;
}

using (FileStream xmlFileStream = new FileStream(this.fxCopOutput, FileMode.Open))
{
XmlTextReader fxReport = new XmlTextReader(xmlFileStream);

while (fxReport.Read())
{
if (fxReport.LocalName == "Issue" && fxReport.NodeType == XmlNodeType.Element)
{
successStatus = false;
numIssues++;
}
}

if (!successStatus)
{
Log.LogError("Found {0} fxCop warnings, see {1} for more details", numIssues, xmlFileStream.Name);
}

return successStatus;
}
}
catch (Exception e)
{
Log.LogErrorFromException(e);
return false;
}
}

const string FXCOPPATH_DEFAULT = "c:\\Program Files\\Microsoft FxCop 1.35\\FxCopCmd.exe";
private string fxCopPath = FXCOPPATH_DEFAULT;
public string FxCopPath
{
get { return fxCopPath; }
set { fxCopPath = value; }
}

private string projectPath;
public string ProjectPath
{
get { return projectPath; }
set { projectPath = value; }
}

private string fxCopOutput = "fxCopOut.xml";
public string FxCopOutput
{
get { return fxCopOutput; }
set { fxCopOutput = value; }
}

private string targetAssembly;
public string TargetAssembly
{
get { return targetAssembly; }
set { targetAssembly = value; }
}


protected override string GenerateFullPathToTool()
{
return fxCopPath;
}

protected override string GenerateCommandLineCommands()
{
CommandLineBuilder builder = new CommandLineBuilder();

if (projectPath != null)
{
builder.AppendSwitch("/project:" + projectPath);
}
else if (targetAssembly != null)
{
builder.AppendSwitch("/file:" + targetAssembly);
}
else
{
Log.LogWarning("FxCopTask requires eith a ProjectPath or a TargetAssembly");
}

if (fxCopOutput != null)
{
builder.AppendSwitch("/out:" + fxCopOutput);
}
return builder.ToString();
}

protected override string ToolName
{
get { return "fxCopCmd.exe"; }
}
}
}

Trevor Michealson said...

The main reason I don't run fxcop from the task is so I can have the report generated but not break the build. My bosses don't want to see the inital slow down that would result in having to address all of the issues identified by FxCop

ripper234 said...

Use a multiple-build system. We use Teamcity, and have many different builds.

You can have the FXCop build fail but "put on ignore" by people that don't care about it.