【问题标题】:How do I compile a C# solution with Roslyn?如何使用 Roslyn 编译 C# 解决方案?
【发布时间】:2012-11-07 23:22:06
【问题描述】:

我有一个根据用户操作为 C# 项目生成代码的软件。我想创建一个 GUI 来自动编译解决方案,这样我就不必加载 Visual Studio 来触发重新编译。

我一直在寻找与 Roslyn 一起玩的机会,并决定尝试使用 Roslyn 而不是 msbuild 来执行此操作。不幸的是,我似乎找不到任何以这种方式使用 Roslyn 的好资源。

谁能指出我正确的方向?

【问题讨论】:

    标签: c# roslyn


    【解决方案1】:

    您可以使用Roslyn.Services.Workspace.LoadSolution 加载解决方案。完成此操作后,您需要按依赖顺序遍历每个项目,获取该项目的 Compilation 并在其上调用 Emit

    您可以使用下面的代码按依赖顺序获取编译。 (是的,我知道必须强制转换为 IHaveWorkspaceServices 很糟糕。我保证在下一个公开版本中会更好)。

    using Roslyn.Services;
    using Roslyn.Services.Host;
    using System;
    using System.Collections.Generic;
    using System.IO;
    
    class Program
    {
        static void Main(string[] args)
        {
            var solution = Solution.Create(SolutionId.CreateNewId()).AddCSharpProject("Foo", "Foo").Solution;
            var workspaceServices = (IHaveWorkspaceServices)solution;
            var projectDependencyService = workspaceServices.WorkspaceServices.GetService<IProjectDependencyService>();
            var assemblies = new List<Stream>();
            foreach (var projectId in projectDependencyService.GetDependencyGraph(solution).GetTopologicallySortedProjects())
            {
                using (var stream = new MemoryStream())
                {
                    solution.GetProject(projectId).GetCompilation().Emit(stream);
                    assemblies.Add(stream);
                }
            }
        }
    }
    

    注意 1:LoadSolution 仍然使用 msbuild 来解析 .csproj 文件并确定文件/引用/编译器选项。

    注意 2:由于 Roslyn 尚未完成语言,因此在您尝试此操作时可能会有项目无法成功编译。

    【讨论】:

    • 是否有一种方便的方法来确定依赖顺序(如sln.ProjectsInDependencyOrder),还是我必须自己实现? (例如,浏览项目引用并构建依赖树)
    • 有一个具有 GetTopologicallySortedProjects() 的 IProjectDependencyService。我手边没有电脑,无法准确检查如何获取它,但稍后会更新。
    • 此解决方案在当前预览版 1.0.0-beta1-20141031-01 中不起作用。 Roslyn.Services.Workspace.LoadSolution() 不存在,Solution.Create() 也不存在。我认为你必须使用MSBuildWorkspace.CreateOpenSolutionAsync (对于现有的sln)或者只是workspace.CurrentSolution 用于空的。此外,您需要从microsoft.com/en-us/download/details.aspx?id=44931 安装 MSBuild 2015 工具才能使用 MSBuildWorkspace。或者,您可以 MEF 导出目录并使用 CustomWorkspace。整个过程似乎变得更加复杂
    • 添加到@JoshE 的评论中,Solution 类现在有一个GetProjectDependencyGraph 方法,一旦你有了Solution 对象,它就可以完成所有这些工作。
    【解决方案2】:

    我还想即时编译一个完整的解决方案。从Kevin Pilch-Bisson's answerJosh E's comment 构建,我编写了代码来编译自身并将其写入文件。

    使用的软件

    Visual Studio 社区 2015 更新 1

    Microsoft.CodeAnalysis v1.1.0.0(使用包管理器控制台和命令Install-Package Microsoft.CodeAnalysis 安装)。

    代码

    using System;
    using System.Collections.Generic;
    using System.IO;
    using Microsoft.CodeAnalysis;
    using Microsoft.CodeAnalysis.Emit;
    using Microsoft.CodeAnalysis.MSBuild;
    
    namespace Roslyn.TryItOut
    {
        class Program
        {
            static void Main(string[] args)
            {
                string solutionUrl = "C:\\Dev\\Roslyn.TryItOut\\Roslyn.TryItOut.sln";
                string outputDir = "C:\\Dev\\Roslyn.TryItOut\\output";
    
                if (!Directory.Exists(outputDir))
                {
                    Directory.CreateDirectory(outputDir);
                }
    
                bool success = CompileSolution(solutionUrl, outputDir);
    
                if (success)
                {
                    Console.WriteLine("Compilation completed successfully.");
                    Console.WriteLine("Output directory:");
                    Console.WriteLine(outputDir);
                }
                else
                {
                    Console.WriteLine("Compilation failed.");
                }
    
                Console.WriteLine("Press the any key to exit.");
                Console.ReadKey();
            }
    
            private static bool CompileSolution(string solutionUrl, string outputDir)
            {
                bool success = true;
    
                MSBuildWorkspace workspace = MSBuildWorkspace.Create();
                Solution solution = workspace.OpenSolutionAsync(solutionUrl).Result;
                ProjectDependencyGraph projectGraph = solution.GetProjectDependencyGraph();
                Dictionary<string, Stream> assemblies = new Dictionary<string, Stream>();
    
                foreach (ProjectId projectId in projectGraph.GetTopologicallySortedProjects())
                {
                    Compilation projectCompilation = solution.GetProject(projectId).GetCompilationAsync().Result;
                    if (null != projectCompilation && !string.IsNullOrEmpty(projectCompilation.AssemblyName))
                    {
                        using (var stream = new MemoryStream())
                        {
                            EmitResult result = projectCompilation.Emit(stream);
                            if (result.Success)
                            {
                                string fileName = string.Format("{0}.dll", projectCompilation.AssemblyName);
    
                                using (FileStream file = File.Create(outputDir + '\\' + fileName))
                                {
                                    stream.Seek(0, SeekOrigin.Begin);
                                    stream.CopyTo(file);
                                }
                            }
                            else
                            {
                                success = false;
                            }
                        }
                    }
                    else
                    {
                        success = false;
                    }
                }
    
                return success;
            }
        }
    }
    

    【讨论】:

    • 找不到 RuntimeMetadataVersion 的值。没有找到包含 System.Object 的程序集,也没有通过选项指定 RuntimeMetadataVersion 的值。
    • FYI 要使用此示例,您还需要安装 Microsoft.Build.Tasks.Core 和 Microsoft.Build Packages
    • 这似乎是一个更好的答案,因为 roslyn.services 命名空间已过时。
    • 编译结果到磁盘的输出可以大大简化。无需创建 MemoryStream 并格式化文件名并创建 FileStream 并将内存流复制到文件流,您可以将一条语句拆分为两条: Project project = solution.GetProject(projectId);编译 projectCompilation = project.GetCompilationAsync().Result;然后稍后只需指定 projectCompilation.Emit(project.OutputFilePath)。 (这确实假设 .csproj 文件正确指定了输出文件。)
    • 根据gist.github.com/DustinCampbell/32cd69d04ea1c08a16ae5c4cd21dd3a3,需要引用NuGet包Microsoft.Build.Locator并调用MSBuildLocator.RegisterDefaults()。根据我的经验,不这样做会导致Compilation 中没有任何框架引用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-08-05
    • 2023-04-01
    • 2019-02-19
    • 1970-01-01
    • 1970-01-01
    • 2013-03-28
    • 2012-06-05
    相关资源
    最近更新 更多