使用MSBuild对项目运行各种分析器时,所有故障都将以“静态分析结果交换格式(SARIF)”格式输出(参见例如https://github.com/sarif-standard/sarif-spec)。例如,构建可能会产生以下内容
{
"version": "0.1",
"toolInfo": {
"toolName": "Microsoft (R) Visual C# Compiler",
"productVersion": "1.1.0",
"fileVersion": "1.1.0"
},
"issues": [
{
"ruleId": "SA1401",
"locations": [
{
"analysisTarget": [
{
"uri": "C:\\SomeFile.cs",
"region": {
"startLine": 708,
"startColumn": 30,
"endLine": 708,
"endColumn": 36
}
}
]
}
],
"shortMessage": "Field must be private",
"fullMessage": "A field within a C# class has an access modifier other than private.",
"properties": {
"severity": "Warning",
"warningLevel": "1",
"defaultSeverity": "Warning",
"title": "Fields must be private",
"category": "StyleCop.CSharp.MaintainabilityRules",
"helpLink": "https:\/\/github.com\/DotNetAnalyzers\/StyleCopAnalyzers\/blob\/master\/documentation\/SA1401.md",
"isEnabledByDefault": "True",
"isSuppressedInSource": "True"
}
}
]
}
现在我希望能够以最简单的方式解析上面的数据(如果遇到任何非抑制问题,则打破构建)。如何做到这一点?
PS。我最好还是避免实现自己的MSBuild任务和安装特定的软件(例如PowerShell 3.0 - ConvertFrom-Json)。
答案 0 :(得分:4)
有一个SARIF SDK可用于处理SARIF文件。它可以作为NuGet包Sarif.Sdk使用,源代码在Microsoft / sarif-sdk项目的GitHub上。有一个方法文档docs / how-to.md,它显示了如何从磁盘读取SARIF文件并将其反序列化为SarifLog
对象;然后,您可以浏览SARIF对象模型以检查单个结果。
在您的情况下,您对结果的“属性包”中的isSuppressedInSource
属性感兴趣。“操作方法”文档说明了如何检索该属性:
Result result = …;
string isSuppressedInSource = result.GetProperty("isSuppressedInSource");
SARIF规范为available online,并且SARIF home page包含指向更多信息的链接。
最后:请注意,Visual Studio 2015 Update 2和Update 3之间的SARIF格式发生了显着变化。格式现在处于稳定的1.0.0版本。
(注意:很抱歉没有提供指向SDK,NuGet软件包和操作方法的直接链接。我没有足够的信誉点来发布两个以上的链接。)
答案 1 :(得分:0)
由于显然没有内置的方法,我最终使用内联的msbuild任务(https://msdn.microsoft.com/en-US/library/dd722601.aspx),定义为
<UsingTask TaskName="ParseUnsupressedAnalysisIssues" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll" >
<ParameterGroup>
<Files ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
<Result ParameterType="Microsoft.Build.Framework.ITaskItem[]" Output="true" />
</ParameterGroup>
<Task>
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.Xml" />
<Reference Include="System.Xml.Linq" />
<Using Namespace="System"/>
<Using Namespace="System.Collections.Generic"/>
<Using Namespace="System.IO"/>
<Using Namespace="System.Linq"/>
<Using Namespace="System.Runtime.Serialization.Json"/>
<Using Namespace="System.Xml"/>
<Using Namespace="System.Xml.Linq"/>
<Code Type="Fragment" Language="cs">
<![CDATA[
List<TaskItem> taskItems = new List<TaskItem>();
foreach(ITaskItem item in Files)
{
try
{
string path = item.GetMetadata("FullPath");
using (FileStream fs = new FileStream(path, FileMode.Open))
using (XmlDictionaryReader reader = JsonReaderWriterFactory.CreateJsonReader(fs, XmlDictionaryReaderQuotas.Max))
{
XElement doc = XElement.Load(reader);
XElement issuesRoot = doc.Elements("issues").SingleOrDefault();
List<XElement> unsupressedIssues = issuesRoot.Elements("item").Where(e => !"True".Equals((string)e.Element("properties").Element("isSuppressedInSource"), StringComparison.Ordinal)).ToList();
string unsupressedIssuesString = string.Join(Environment.NewLine, unsupressedIssues);
if(!string.IsNullOrEmpty(unsupressedIssuesString))
{
taskItems.Add(new TaskItem(item.ItemSpec));
Console.WriteLine(unsupressedIssuesString);
}
}
}
catch(Exception e)
{
taskItems.Add(new TaskItem(item.ItemSpec));
Console.WriteLine(e.ToString());
}
}
Result = taskItems.ToArray();
]]>
</Code>
</Task>
</UsingTask>
然后可以作为
调用<ParseUnsupressedAnalysisIssues Files="@(AnalyzerFiles)">
<Output ItemName="FailedAnalyzerFiles" TaskParameter="Result" />
</ParseUnsupressedAnalysisIssues>
<Error Text="FxCopAll: Following assemblies had analyzer errors @(FailedAnalyzerFiles)" Condition="'@(FailedAnalyzerFiles->Count())' > 0" Code="2"/>