1
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
2
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
5
using System.Globalization;
10
using Microsoft.Build.Framework;
11
using Microsoft.Build.Utilities;
12
using Microsoft.Win32;
14
namespace ICSharpCode.Build.Tasks
16
public sealed class FxCop : ToolTask
20
public string LogFile { get; set; }
21
public string InputAssembly { get; set; }
22
public string[] Rules { get; set; }
23
public string[] RuleAssemblies { get; set; }
24
public string[] ReferencePaths { get; set;}
25
public ITaskItem[] Dictionary { get; set;}
27
protected override string ToolName {
29
return "FxCopCmd.exe";
33
public override bool Execute()
35
if (string.IsNullOrEmpty(ToolPath)) {
36
string path = FindFxCopPath();
37
Log.LogMessage(MessageImportance.High, Resources.RunningCodeAnalysis);
41
Log.LogError(Resources.CannotFindFxCop);
45
realLogFile = LogFile ?? Path.GetTempFileName();
47
bool result = base.Execute();
48
if (File.Exists(realLogFile)) {
51
Console.WriteLine(File.ReadAllText(realLogFile));
53
XmlDocument doc = new XmlDocument();
54
doc.Load(realLogFile);
55
foreach (XmlNode node in doc.DocumentElement.SelectNodes(".//Exception")) {
56
XmlElement el = node as XmlElement;
57
if (el == null) continue;
60
string checkId = el.GetAttribute("CheckId");
61
string keyword = el.GetAttribute("Keyword");
62
string message = el["ExceptionMessage"].InnerText;
63
if (checkId.Length > 0) {
64
Log.LogError(null, null, keyword, "", 0, 0, 0, 0,
65
"{0} : {2} ({1}): {3}", keyword, el.GetAttribute("Category"), checkId, el.GetAttribute("Target"), message);
67
Log.LogError(keyword + " : " + message);
70
foreach (XmlNode node in doc.DocumentElement.SelectNodes(".//Message")) {
71
XmlElement el = node as XmlElement;
72
if (el == null) continue;
73
result &= LogMessage(el);
75
} catch (XmlException e) {
76
Log.LogError(Resources.CannotReadFxCopLogFile + " " + e.Message);
81
if (LogFile == null) {
82
File.Delete(realLogFile);
90
/// <returns>True for warning, false for error.</returns>
91
bool LogMessage(XmlElement message)
93
bool isWarning = true;
94
string checkId = message.GetAttribute("CheckId");
95
if (message.HasAttribute("TypeName")) {
96
checkId = checkId + ":" + message.GetAttribute("TypeName");
98
string category = message.GetAttribute("Category");
99
foreach (XmlNode node in message.SelectNodes(".//Issue")) {
100
XmlElement issueEl = node as XmlElement;
101
if (issueEl == null) continue;
103
if ("true".Equals(message.GetAttribute("BreaksBuild"), StringComparison.OrdinalIgnoreCase)) {
106
string issueText = issueEl.InnerText;
107
string issuePath = issueEl.GetAttribute("Path");
108
string issueFile = issueEl.GetAttribute("File");
109
string issueLine = issueEl.GetAttribute("Line");
110
int issueLineNumber = 0;
111
string issueFullFile = null;
113
// Try to find additional information about this type
114
string memberName = null;
115
string typeName = null;
116
XmlNode parent = message.ParentNode;
117
while (parent != null) {
118
if (parent.Name == "Type") {
119
if (typeName == null) {
120
typeName = ((XmlElement)parent).GetAttribute("Name");
122
typeName = ((XmlElement)parent).GetAttribute("Name") + "+" + typeName;
124
} else if (parent.Name == "Namespace" && typeName != null) {
125
typeName = ((XmlElement)parent).GetAttribute("Name") + "." + typeName;
126
} else if (parent.Name == "Member" && memberName == null) {
127
memberName = ((XmlElement)parent).GetAttribute("Name");
128
// first, strip the # inserted by FxCop
129
if (memberName.StartsWith("#"))
130
memberName = memberName.Substring(1);
131
// second, translate .ctor to #ctor
132
if (memberName.StartsWith(".ctor") || memberName.StartsWith(".cctor"))
133
memberName = "#" + memberName.Substring(1);
135
parent = parent.ParentNode;
137
if (issuePath.Length > 0 && issueLine.Length > 0 && issueFile.Length > 0) {
138
issueFullFile = Path.Combine(issuePath, issueFile);
139
issueLineNumber = int.Parse(issueLine, CultureInfo.InvariantCulture);
140
} else if (typeName != null) {
141
issueFullFile = "positionof#" + typeName + "#" + memberName;
144
string data = typeName + "|" + memberName;
146
if (message.HasAttribute("Id")) {
147
data = data + "|" + message.GetAttribute("Id");
151
Log.LogWarning(data, checkId, category, issueFullFile, issueLineNumber, 0, 0, 0, issueText);
153
Log.LogError(data, checkId, category, issueFullFile, issueLineNumber, 0, 0, 0, issueText);
159
string FindFxCopPath()
161
// Code duplication: FxCopWrapper.cs in CodeAnalysis addin
162
string fxCopPath = null;
163
using (RegistryKey key = Registry.LocalMachine.OpenSubKey(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\9.0\Setup\EDev")) {
165
fxCopPath = key.GetValue("FxCopDir") as string;
168
if (IsFxCopPath(fxCopPath)) {
171
using (RegistryKey key = Registry.LocalMachine.OpenSubKey(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\8.0\Setup\EDev")) {
173
fxCopPath = key.GetValue("FxCopDir") as string;
176
if (IsFxCopPath(fxCopPath)) {
180
fxCopPath = FromRegistry(Registry.ClassesRoot.OpenSubKey(@"FxCop.Project.9.0\Shell\Open\Command"));
181
if (IsFxCopPath(fxCopPath)) {
184
fxCopPath = FromRegistry(Registry.CurrentUser.OpenSubKey(@"Software\Classes\FxCopProject\Shell\Open\Command"));
185
if (IsFxCopPath(fxCopPath)) {
188
fxCopPath = FromRegistry(Registry.ClassesRoot.OpenSubKey(@"FxCopProject\Shell\Open\Command"));
189
if (IsFxCopPath(fxCopPath)) {
195
bool IsFxCopPath(string fxCopPath)
197
return !string.IsNullOrEmpty(fxCopPath) && File.Exists(Path.Combine(fxCopPath, ToolName));
200
string FromRegistry(RegistryKey key)
202
// Code duplication: FxCopWrapper.cs in CodeAnalysis addin
203
if (key == null) return string.Empty;
205
string cmd = key.GetValue("").ToString();
207
if (cmd.StartsWith("\""))
208
pos = cmd.IndexOf('"', 1);
210
pos = cmd.IndexOf(' ');
212
if (cmd.StartsWith("\""))
213
return Path.GetDirectoryName(cmd.Substring(1, pos - 1));
215
return Path.GetDirectoryName(cmd.Substring(0, pos));
216
} catch (ArgumentException ex) {
218
Log.LogWarningFromException(ex);
224
protected override string GenerateFullPathToTool()
226
return Path.Combine(ToolPath.Trim('"'), ToolName);
229
static void AppendSwitch(StringBuilder b, string @switch, string val)
231
if (!string.IsNullOrEmpty(val)) {
245
//protected override string GenerateResponseFileCommands()
246
protected override string GenerateCommandLineCommands()
248
// using a response file fails when the a path contains spaces, but using the command line
249
// works fine (FxCop bug?)
251
StringBuilder b = new StringBuilder();
252
AppendSwitch(b, "o", realLogFile);
253
AppendSwitch(b, "f", InputAssembly);
254
if (ReferencePaths != null) {
255
foreach (string path in ReferencePaths) {
256
AppendSwitch(b, "d", Path.GetDirectoryName(path));
259
if (RuleAssemblies != null) {
260
foreach (string asm in RuleAssemblies) {
261
if (asm.StartsWith("\\")) {
262
AppendSwitch(b, "r", ToolPath + asm);
264
AppendSwitch(b, "r", asm);
269
foreach (string rule in Rules) {
270
AppendSwitch(b, "ruleid", rule);
273
if (Dictionary != null) {
274
foreach (ITaskItem dic in Dictionary) {
275
AppendSwitch(b, "dic", dic.ItemSpec);
279
Console.WriteLine(b.ToString());