embed plugin loader directly into the launcher
This commit is contained in:
103
PluginLoader/Compiler/RoslynCompiler.cs
Normal file
103
PluginLoader/Compiler/RoslynCompiler.cs
Normal file
@@ -0,0 +1,103 @@
|
||||
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));
|
||||
|
||||
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.WriteLine(
|
||||
$"{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; }
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user