如何使用Roslyn编译C#解决方案?

Kal*_*exx 24 c# roslyn

我有一个软件,可以根据用户操作为C#项目生成代码.我想创建一个GUI来自动编译解决方案,所以我不必加载Visual Studio只是为了触发重新编译.

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

谁能指出我正确的方向?

Kev*_*lch 26

您可以使用加载解决方案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);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

  • 此解决方案在当前预览1.0.0-beta1-20141031-01中不起作用.`Roslyn.Services.Workspace.LoadSolution()`不存在,`Solution.Create()`也不存在.我认为你必须使用`MSBuildWorkspace.Create`和`OpenSolutionAsync`(对于现有的sln)或者只是`workspace.CurrentSolution`用于空的.同样,您需要从http://www.microsoft.com/en-us/download/details.aspx?id=44931安装MSBuild 2015工具才能使用MSBuildWorkspace.或者,您可以MEF导出目录并使用`CustomWorkspace`.似乎整个过程变得更加复杂 (5认同)

Rya*_*yle 17

我还想动态编译完整的解决方案.根据Kevin Pilch-Bisson的回答Josh E的评论,我编写了代码来编译自己并将其写入文件.

使用的软件

Visual Studio社区2015 Update 1

Microsoft.CodeAnalysis v1.1.0.0(使用带有命令的Package Manager控制台安装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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这似乎是一个更好的答案,因为 roslyn.services 命名空间已过时。 (2认同)
  • 根据 https://gist.github.com/DustinCampbell/32cd69d04ea1c08​​a16ae5c4cd21dd3a3,您需要引用 NuGet 包 `Microsoft.Build.Locator` 并调用 `MSBuildLocator.RegisterDefaults()`。根据我的经验,不这样做会导致“编译”中没有任何框架引用。 (2认同)