103 lines
3.4 KiB
C#
103 lines
3.4 KiB
C#
using Microsoft.CodeAnalysis;
|
|
using Microsoft.CodeAnalysis.CSharp;
|
|
using Microsoft.CodeAnalysis.Emit;
|
|
using Microsoft.CodeAnalysis.Text;
|
|
|
|
namespace PluginLoader.Compiler;
|
|
|
|
public class RoslynCompiler
|
|
{
|
|
private readonly List<Source> source = new();
|
|
private readonly bool debugBuild;
|
|
|
|
public RoslynCompiler(bool debugBuild = false)
|
|
{
|
|
this.debugBuild = debugBuild;
|
|
}
|
|
|
|
public void Load(Stream s, string name)
|
|
{
|
|
var mem = new MemoryStream();
|
|
using (mem)
|
|
{
|
|
s.CopyTo(mem);
|
|
source.Add(new(mem, name, debugBuild));
|
|
}
|
|
}
|
|
|
|
public byte[] Compile(string assemblyName, out byte[] symbols)
|
|
{
|
|
symbols = null;
|
|
|
|
var compilation = CSharpCompilation.Create(
|
|
assemblyName,
|
|
source.Select(x => x.Tree),
|
|
RoslynReferences.EnumerateAllReferences(),
|
|
new(
|
|
OutputKind.DynamicallyLinkedLibrary,
|
|
optimizationLevel: debugBuild ? OptimizationLevel.Debug : OptimizationLevel.Release, allowUnsafe: true));
|
|
|
|
using (var pdb = new MemoryStream())
|
|
using (var ms = new MemoryStream())
|
|
{
|
|
// write IL code into memory
|
|
EmitResult result;
|
|
if (debugBuild)
|
|
result = compilation.Emit(ms, pdb,
|
|
embeddedTexts: source.Select(x => x.Text),
|
|
options: new(debugInformationFormat: DebugInformationFormat.PortablePdb,
|
|
pdbFilePath: Path.ChangeExtension(assemblyName, "pdb")));
|
|
else
|
|
result = compilation.Emit(ms);
|
|
|
|
if (!result.Success)
|
|
{
|
|
// handle exceptions
|
|
var failures = result.Diagnostics.Where(diagnostic =>
|
|
diagnostic.IsWarningAsError ||
|
|
diagnostic.Severity == DiagnosticSeverity.Error);
|
|
|
|
foreach (var diagnostic in failures)
|
|
{
|
|
var location = diagnostic.Location;
|
|
var source = this.source.FirstOrDefault(x => x.Tree == location.SourceTree);
|
|
LogFile.Log.Debug(
|
|
$"{diagnostic.Id}: {diagnostic.GetMessage()} in file:\n{source?.Name ?? "null"} ({location.GetLineSpan().StartLinePosition})");
|
|
}
|
|
|
|
throw new("Compilation failed!");
|
|
}
|
|
|
|
if (debugBuild)
|
|
{
|
|
pdb.Seek(0, SeekOrigin.Begin);
|
|
symbols = pdb.ToArray();
|
|
}
|
|
|
|
ms.Seek(0, SeekOrigin.Begin);
|
|
return ms.ToArray();
|
|
}
|
|
}
|
|
|
|
private class Source
|
|
{
|
|
public Source(Stream s, string name, bool includeText)
|
|
{
|
|
Name = name;
|
|
var source = SourceText.From(s, canBeEmbedded: includeText);
|
|
if (includeText)
|
|
{
|
|
Text = EmbeddedText.FromSource(name, source);
|
|
Tree = CSharpSyntaxTree.ParseText(source, new(LanguageVersion.Latest), name);
|
|
}
|
|
else
|
|
{
|
|
Tree = CSharpSyntaxTree.ParseText(source, new(LanguageVersion.Latest));
|
|
}
|
|
}
|
|
|
|
public string Name { get; }
|
|
public SyntaxTree Tree { get; }
|
|
public EmbeddedText Text { get; }
|
|
}
|
|
} |