【问题标题】:What is the fastest way to get a list of errors from a C# solution with MSBuildWorkspace?使用 MSBuildWorkspace 从 C# 解决方案中获取错误列表的最快方法是什么?
【发布时间】:2018-08-13 19:05:57
【问题描述】:

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

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

【问题讨论】:

    标签: c# compiler-errors compilation roslyn


    【解决方案1】:

    您需要加载解决方案,然后遍历您的项目并找出所有错误。这不会创建任何文件(即不发出 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

    【讨论】:

    • 我对 Roslyn 的研究实际上是几年前从您的教程开始的,感谢您的回答:-)。这段代码是我目前使用的,除了我订购项目以使用 solution.GetProjectDependencyGraph().GetTopologicallySortedProjects() 进行编译并检查 .IsSuppressed 的诊断。
    【解决方案2】:

    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}");
            }
        }
    }
    

    【讨论】:

    • 我已经看到有关 MSBuildLocator 的信息,但是,我测试了没有它,并且解决方案到目前为止在我的家用机器和工作机器上都有效。您能否举个例子,当不使用 MSBuildLocator 时会导致问题?
    • 如果您在同一台机器上有两个 VS 安装(或两个 MSBuild 安装)。此外,即使您不在开发人员命令提示符下,上面的示例也可以工作
    • 此外,上面的示例将打印出加载错误(缺少参考或项目),而另一个将静默失败。
    猜你喜欢
    • 1970-01-01
    • 2012-02-12
    • 1970-01-01
    • 2023-02-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多