Code compilation: How C# code magically becomes an EXE!
The question is how can I compile C# code on ‘the fly’ in .NET 9.0
As a demo, we will make a small programme that compiles any file with C# code and creates a DLL.
The basic framework of the programme is relatively simple.
Basically, we don't need much to integrate a C' compiler into an application.
We just need to install the Microsoft.CodeAnalysis package and we can compile in the code.
var syntaxTree = SyntaxFactory.ParseSyntaxTree(SourceText.From(sourceCode));
var assemblyPath = Path.ChangeExtension(Path.GetTempFileName(), "exe");
var compilation = CSharpCompilation.Create(Path.GetFileName(assemblyPath))
.WithOptions(new CSharpCompilationOptions(OutputKind.ConsoleApplication))
.AddSyntaxTrees(syntaxTree);
var result = compilation.Emit(assemblyPath);
if (result.Success)
{
Process.Start(assemblyPath);
}
else
{
Debug.Write(string.Join(
Environment.NewLine,
result.Diagnostics.Select(diagnostic => diagnostic.ToString())
));
}
Actually, this is all the code we need.
Unfortunately, errors occur because we have forgotten to add some important references.
We can easily add these references to CSharpCompilation. The property is aptly named references.
So let's add the most important assemblies
var references = new List<MetadataReference>
{
MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
MetadataReference.CreateFromFile(typeof(Console).Assembly.Location),
MetadataReference.CreateFromFile(typeof(Decimal).Assembly.Location),
MetadataReference.CreateFromFile(typeof(Object).Assembly.Location),
MetadataReference.CreateFromFile(Path.Combine(runtimePath, "mscorlib.dll")),
MetadataReference.CreateFromFile(typeof(System.Runtime.AssemblyTargetedPatchBandAttribute).Assembly.Location),
MetadataReference.CreateFromFile(typeof(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo).Assembly.Location),
MetadataReference.CreateFromFile(Path.Combine(runtimePath, "System.Runtime.dll")),
MetadataReference.CreateFromFile(Path.Combine(runtimePath, "System.Console.dll")),
MetadataReference.CreateFromFile(Path.Combine(runtimePath, "System.Core.dll")),
// Explizit CoreLib referenzieren
MetadataReference.CreateFromFile(Path.Combine(runtimePath, "System.Private.CoreLib.dll")),
// Weitere wichtige Basis-Assemblies
MetadataReference.CreateFromFile(Path.Combine(runtimePath, "System.Collections.dll")),
MetadataReference.CreateFromFile(Path.Combine(runtimePath, "System.Linq.dll")),
MetadataReference.CreateFromFile(Path.Combine(runtimePath, "System.Threading.dll")),
MetadataReference.CreateFromFile(typeof(System.Composition.Convention.AttributedModelProvider).Assembly.Location), //System.Composition.AttributeModel
MetadataReference.CreateFromFile(typeof(System.Composition.Convention.ConventionBuilder).Assembly.Location), //System.Composition.Convention
MetadataReference.CreateFromFile(typeof(System.Composition.Hosting.CompositionHost).Assembly.Location), //System.Composition.Hosting
MetadataReference.CreateFromFile(typeof(System.Composition.CompositionContext).Assembly.Location), //System.Composition.Runtime
MetadataReference.CreateFromFile(typeof(System.Composition.CompositionContextExtensions).Assembly.Location), //System.Composition.TypedParts
};
So far so good - but it's not quite working yet. The following error occurs:
Unhandled Exception: System.IO.FileNotFoundException: Could not load file or assembly ‘System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e’ or one of its dependencies. The system cannot find the file specified.
This is because a file is missing: *.runtimeconfig.json
We have to create this file and our programme will work.
You can find the whole source code at: https://github.com/gpiwonka/CompilerDemo