Finnaly I found the way to call source generator in roslyn at runtime mentioned in #285
But it will throw exception like this
Compile Failed
The constraints for type parameter 'TBufferWriter' of method 'Model.Serialize<TBufferWriter>(ref MemoryPackWriter<TBufferWriter>, scoped ref Model?)' must match the constraints for type parameter 'TBufferWriter' of interface method 'IMemoryPackable<Model>.Serialize<TBufferWriter>(ref MemoryPackWriter<TBufferWriter>, scoped ref Model?)'. Consider using an explicit interface implementation instead.
The type 'TBufferWriter' must be a reference type in order to use it as parameter 'TBufferWriter' in the generic type or method 'Model.Serialize<TBufferWriter>(ref MemoryPackWriter<TBufferWriter>, scoped ref Model?)'
what happed here?
code example:
using MemoryPack;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Diagnostics;
using System.Reflection;
using System.Runtime.Loader;
using Test;
var sources = new List<(string Name,string Code)>();
sources.Add(("Model",
@"using MemoryPack;
namespace Test;
[MemoryPackable]
public partial class Model
{
public string Name { get; set; }
}
"));
List<string> references =
[
CodeCompiler.FromName("mscorlib"),
CodeCompiler.FromName("System"),
CodeCompiler.FromName("System.Core"),
CodeCompiler.FromName("System.Runtime"),
CodeCompiler.FromName("System.Memory"),
CodeCompiler.FromName("System.Private.CoreLib"),
CodeCompiler.FromName("System.Private.Uri"),
CodeCompiler.FromName("System.Runtime.Numerics"),
CodeCompiler.FromName("System.Collections"),
AppContext.BaseDirectory +"MemoryPack.Core.dll"
];
// Need To Copy this dll to output directory
List<string> analyzers = [AppContext.BaseDirectory +"MemoryPack.Generator.dll"];
var d = await CodeCompiler.CompileSources(AppContext.BaseDirectory +"output.dll",references,analyzers,sources,false);
if(d.Success)
{
Console.WriteLine("Compile Success");
}
else
{
Console.WriteLine("Compile Failed");
foreach(var error in d.Errors)
{
Console.WriteLine(error);
}
return;
}
var assembly = Assembly.LoadFile(AppContext.BaseDirectory + "output.dll");
var type = assembly.GetType("Test.Model");
// var type = typeof(Model);
var instance = Activator.CreateInstance(type);
type.GetProperty("Name").SetValue(instance,"Hello World");
var bytes = MemoryPackSerializer.Serialize<Model>(instance as Model);
Console.WriteLine(bytes.Length);
public static class CodeCompiler
{
static readonly string RuntimePath = System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory();
public static string FromName(string name)=>$"{RuntimePath}{name}.dll";
const string PROJECT_NAME = "VisualSharpOutput";
const string ASSEMBLY_NAME = "VisualSharpOutput";
public static async Task<CodeCompilationResult> CompileSources(string outputPath, IEnumerable<string> assemblyPaths, IEnumerable<string> analyzersPaths,
IEnumerable<(string Name,string Code)> sources, bool generateExecutable)
{
var projectInfo = ProjectInfo.Create(
ProjectId.CreateNewId(),
VersionStamp.Create(),
PROJECT_NAME,
ASSEMBLY_NAME,
LanguageNames.CSharp
);
var solution = new AdhocWorkspace()
.CurrentSolution
.AddProject(projectInfo);
var project = solution.GetProject(projectInfo.Id);
var compilationOptions = new CSharpCompilationOptions(generateExecutable ? OutputKind.ConsoleApplication : OutputKind.DynamicallyLinkedLibrary);
project = project.WithCompilationOptions(compilationOptions);
IEnumerable<MetadataReference> references = assemblyPaths.Select(path => MetadataReference.CreateFromFile(path));
project = project.AddMetadataReferences(references);
IEnumerable<AnalyzerFileReference> analyzers = analyzersPaths.Select(path => new AnalyzerFileReference(path, new SimpleAnalyzerAssemblyLoader()));
project = project.AddAnalyzerReferences(analyzers);
foreach(var source in sources)
{
project = project.AddDocument($"{source.Name}.cs", source.Code).Project;
}
// Create compilation
var compilation = await project.GetCompilationAsync();
var analyzerReferences = project.AnalyzerReferences;
var generators = analyzerReferences.SelectMany(reference => reference.GetGeneratorsForAllLanguages());
var generator = generators.First().GetType();
GeneratorDriver driver = CSharpGeneratorDriver.Create(
generators:generators,
driverOptions: new GeneratorDriverOptions(default,trackIncrementalGeneratorSteps:true)
);
driver = driver.RunGenerators(compilation);
var emitResult = compilation.Emit(outputPath);
// EmitResult emitResult = updatedCompilation.Emit(outputPath);
IEnumerable<string> errors = emitResult.Diagnostics
.Where(d => d.Severity == DiagnosticSeverity.Error)
.Select(d => d.GetMessage());
return new CodeCompilationResult(emitResult.Success, errors, emitResult.Success ? outputPath : "");
}
}
public class CodeCompilationResult(bool success, IEnumerable<string> errors,string pathToAssembly)
{
public bool Success=>success;
public IEnumerable<string> Errors=>errors;
public string PathToAssembly=>pathToAssembly;
}
public class SimpleAnalyzerAssemblyLoader : IAnalyzerAssemblyLoader
{
static AssemblyLoadContext _loadContext = new("SimpleAnalyzerAssemblyLoader");
public void AddDependencyLocation(string fullPath)
{
}
public Assembly LoadFromPath(string fullPath)=>_loadContext.LoadFromAssemblyPath(fullPath);
}
namespace Test
{
[MemoryPackable]
public partial class Model
{
public string Name { get; set; }
}
}
Finnaly I found the way to call source generator in roslyn at runtime mentioned in #285
But it will throw exception like this
what happed here?
code example: