Как скомпилировать решение С# с Roslyn?
У меня есть часть программного обеспечения, которое генерирует код для проекта С# на основе действий пользователя. Я хотел бы создать графический интерфейс для автоматической компиляции решения, поэтому мне не нужно загружать Visual Studio, чтобы вызвать повторную компиляцию.
Я искал шанс сыграть с Рослином и решил попробовать использовать Roslyn вместо msbuild, чтобы сделать это. К сожалению, я не могу найти подходящих ресурсов для использования 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 еще не полностью заполнен языком, скорее всего, проекты, которые не скомпилируются успешно, когда вы это сделаете.
Ответ 2
Я также хотел составить полное решение "на лету". Настроим из ответ Кевина Пильча-Биссона и комментарий Josh E, я написал код, чтобы скомпилировать его и записать его в файлы.
Используемое программное обеспечение
Обновление для Visual Studio Community 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;
}
}
}