使用MSBuildWorkspace从C#解决方案中获取错误列表的最快方法是什么?

时间:2018-08-13 19:05:57

标签: c# compiler-errors compilation roslyn

我正在使用MSBuildWorkspace,需要分析C#解决方案中的错误。我不需要实际的编译结果(文件),而只是错误。

使用MSBuildWorkspace从解决方案中获取错误列表的最快方法是什么?

2 个答案:

答案 0 :(得分:2)

您需要加载解决方案,然后遍历项目并查找所有错误。这不会创建任何文件(即,不会发出IL),但确实需要其余大部分编译器管道(词法分析,解析,绑定等)。

//Replace with the correct filepath
var filePath = @"SomeSolution.sln";
var msbws = MSBuildWorkspace.Create();
var soln = await msbws.OpenSolutionAsync(filePath);

foreach(var proj in soln.Projects)
{
    var name = proj.Name;
    var compilation = await proj.GetCompilationAsync();
    var errors = compilation.GetDiagnostics().Where(n => n.Severity == DiagnosticSeverity.Error).ToList();
    // TODO: Do something with the errors
}

如果您知道要打开哪个项目(可以忽略其他项目),则也可以使用OpenProjectAsync

答案 1 :(得分:1)

JoshVarty's Answer可以使用,但是可能无法正确定位MSBuild版本以便运行分析。这是针对最新的roslyn api(版本2.9)的完整示例:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Build.Locator;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.MSBuild;
using Microsoft.CodeAnalysis.Text;

class Program
{
    static async Task Main(string[] args) {
        // Attempt to set the version of MSBuild.
        var visualStudioInstances = MSBuildLocator.QueryVisualStudioInstances().ToArray();
        var instance = visualStudioInstances.Length == 1
            // If there is only one instance of MSBuild on this machine, set that as the one to use.
            ? visualStudioInstances[0]
            // Handle selecting the version of MSBuild you want to use.
            : SelectVisualStudioInstance(visualStudioInstances);

        Console.WriteLine($"Using MSBuild at '{instance.MSBuildPath}' to load projects.");

        // NOTE: Be sure to register an instance with the MSBuildLocator 
        //       before calling MSBuildWorkspace.Create()
        //       otherwise, MSBuildWorkspace won't MEF compose.
        MSBuildLocator.RegisterInstance(instance);

        using (var workspace = MSBuildWorkspace.Create()) {
            // Print message for WorkspaceFailed event to help diagnosing project load failures.
            workspace.WorkspaceFailed += (o, e) => Console.WriteLine(e.Diagnostic.Message);

            var solutionPath = args[0];
            Console.WriteLine($"Loading solution '{solutionPath}'");

            // Attach progress reporter so we print projects as they are loaded.
            var solution = await workspace.OpenSolutionAsync(solutionPath, new ConsoleProgressReporter());
            Console.WriteLine($"Finished loading solution '{solutionPath}'");

            // Print the number of errors found for each project
            foreach(var project in solution.Projects) {
                var name = project.Name;
                var compilation = await project.GetCompilationAsync();
                var errors = compilation.GetDiagnostics().Where(n => n.Severity == DiagnosticSeverity.Error);
                Console.WriteLine($"project '{name}' contained '{errors.Count()}');
            }
        }
    }

    private static VisualStudioInstance SelectVisualStudioInstance(VisualStudioInstance[] visualStudioInstances) {
        Console.WriteLine("Multiple installs of MSBuild detected please select one:");
        for (int i = 0; i < visualStudioInstances.Length; i++) {
            Console.WriteLine($"Instance {i + 1}");
            Console.WriteLine($"    Name: {visualStudioInstances[i].Name}");
            Console.WriteLine($"    Version: {visualStudioInstances[i].Version}");
            Console.WriteLine($"    MSBuild Path: {visualStudioInstances[i].MSBuildPath}");
        }

        while (true) {
            var userResponse = Console.ReadLine();
            if (int.TryParse(userResponse, out int instanceNumber) &&
                instanceNumber > 0 &&
                instanceNumber <= visualStudioInstances.Length) {
                return visualStudioInstances[instanceNumber - 1];
            }
            Console.WriteLine("Input not accepted, try again.");
        }
    }

    private class ConsoleProgressReporter : IProgress<ProjectLoadProgress> {
        public void Report(ProjectLoadProgress loadProgress) {
            var projectDisplay = Path.GetFileName(loadProgress.FilePath);
            if (loadProgress.TargetFramework != null) {
                projectDisplay += $" ({loadProgress.TargetFramework})";
            }

            Console.WriteLine($"{loadProgress.Operation,-15} {loadProgress.ElapsedTime,-15:m\\:ss\\.fffffff} {projectDisplay}");
        }
    }
}