Анализ "Формат обмена результатами статического анализа (SARIF)" в MSBuild

При запуске различных анализаторов для проекта с использованием 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).

3 ответа

Решение

Поскольку, по-видимому, не существует встроенного способа сделать это, я использовал встроенную задачу 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())' &gt; 0" Code="2"/>

Для работы с файлами SARIF доступен SARIF SDK. Он доступен в виде пакета NuGet Sarif.Sdk, а исходный код находится на GitHub в проекте Microsoft/sarif-sdk. Есть документ с практическими рекомендациями docs/how-to.md, который показывает, как прочитать файл SARIF с диска и десериализовать его в SarifLog объект; Затем вы можете перемещаться по объектной модели SARIF, чтобы изучить отдельные результаты.

В вашем случае вы заинтересованы в isSuppressedInSource свойство в "наборе свойств" результата. В документе How-To объясняется, как вы должны получить это свойство:

Result result = …;

string isSuppressedInSource = result.GetProperty("isSuppressedInSource");

Спецификация SARIF доступна онлайн, и есть домашняя страница SARIF со ссылками на дополнительную информацию.

И наконец: имейте в виду, что формат SARIF значительно изменился между Visual Studio 2015 Update 2 и Update 3. Формат теперь стабильный 1.0.0.

(ПРИМЕЧАНИЕ. Извините за отсутствие прямых ссылок на SDK, пакет NuGet и инструкции. У меня недостаточно очков репутации, чтобы опубликовать более двух ссылок.)

Вы можете использовать командную строку Violations, чтобы проанализировать файл SARIF и дать ему -max-violations 0параметр.

Существуют также инструменты для Jenkins и сообщения о нарушениях в различные службы Git. Задокументировано в Violations Lib.

Другие вопросы по тегам