How to compile a C# file with Roslyn programmatically?
Asked Answered
N

2

45

I read that you can't compile C# 6.0 with CSharpCodeProvider and therefor trying to do with with Roslyn. But I can't find a good example how to load a file and then compile it to a dll.

How should I write something similar to this code with Roslyn? Or is there some other way to do it? Now when I try to compile files that contain reference to projects with C# 6.0 code it just say "The type or namespace name 'x' does not exist in the namespace 'y' (are you missing an assembly reference?)"

    public string CompileCode()
    {
        var provider = new CSharpCodeProvider();
        var outputPath = Path.Combine(Path.GetDirectoryName(_path), $"Code.dll");
        var compilerparams = new CompilerParameters(_referencedAssemblies, outputPath);
        CompilerResults results = provider.CompileAssemblyFromFile(compilerparams, _path);
        var dllPath = results.PathToAssembly;
        if (!results.Errors.HasErrors)
            return dllPath;
        PrintError(results.Errors);
        return ""; 
    }

In summary I want to:

  • Load a C# file
  • Compile it to a dll so I can load it later.
Nore answered 24/9, 2015 at 19:42 Comment(1)
Another post on a similar topic: trying to compile and execute C# code programmaticallyMosenthal
B
42

I have created a sample for you to work with. You need to tweak it to use the run time for .Net 4.6 so that CSharp6 version is availble to you. I have added little details so that you can choose the options of compilations.

Changes required - Change the path of runtime to target .Net 4.6 Change the LanguageVersion.Csharp5 to LanguageVersion.Csharp6 in below sample.

 class Program
    {
        private static readonly IEnumerable<string> DefaultNamespaces =
            new[]
            {
                "System", 
                "System.IO", 
                "System.Net", 
                "System.Linq", 
                "System.Text", 
                "System.Text.RegularExpressions", 
                "System.Collections.Generic"
            };

        private static string runtimePath = @"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5.1\{0}.dll";

        private static readonly IEnumerable<MetadataReference> DefaultReferences =
            new[]
            {
                MetadataReference.CreateFromFile(string.Format(runtimePath, "mscorlib")),
                MetadataReference.CreateFromFile(string.Format(runtimePath, "System")),
                MetadataReference.CreateFromFile(string.Format(runtimePath, "System.Core"))
            };

        private static readonly CSharpCompilationOptions DefaultCompilationOptions =
            new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)
                    .WithOverflowChecks(true).WithOptimizationLevel(OptimizationLevel.Release)
                    .WithUsings(DefaultNamespaces);

        public static SyntaxTree Parse(string text, string filename = "", CSharpParseOptions options = null)
        {
            var stringText = SourceText.From(text, Encoding.UTF8);
            return SyntaxFactory.ParseSyntaxTree(stringText, options, filename);
        }

        static void Main(string[] args)
        {
            var fileToCompile = @"C:\Users\DesktopHome\Documents\Visual Studio 2013\Projects\ConsoleForEverything\SignalR_Everything\Program.cs";
            var source = File.ReadAllText(fileToCompile);
            var parsedSyntaxTree = Parse(source, "", CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp5));

            var compilation
                = CSharpCompilation.Create("Test.dll", new SyntaxTree[] { parsedSyntaxTree }, DefaultReferences, DefaultCompilationOptions);
            try
            {
                var result = compilation.Emit(@"c:\temp\Test.dll");

                Console.WriteLine(result.Success ? "Sucess!!" : "Failed");
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }
            Console.Read();
        }

This would need little tweaks but it should give you desired results. Change it as you may wish.

Behre answered 24/9, 2015 at 21:9 Comment(4)
Can yo utell me what references/packages I need to get this to work? Thanks.Miguelinamiguelita
Microsoft.CodeAnalysis.CSharp this package will install other dependencies as well.Behre
Do not put .dll on the CSharpCompilation.Create, had a lot of work today to figure out this, thanks for the answer.Louisville
What about compiling multiple cs files to one dll?Gadmann
B
26

You have to use the NuGet package Microsoft.CodeAnalysis.CSharp.

var syntaxTree = CSharpSyntaxTree.ParseText(source);

CSharpCompilation compilation = CSharpCompilation.Create(
    "assemblyName",
    new[] { syntaxTree },
    new[] { MetadataReference.CreateFromFile(typeof(object).Assembly.Location) },
    new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));

using (var dllStream = new MemoryStream())
using (var pdbStream = new MemoryStream())
{
    var emitResult = compilation.Emit(dllStream, pdbStream);
    if (!emitResult.Success)
    {
        // emitResult.Diagnostics
    }
}
Bowers answered 24/9, 2015 at 20:38 Comment(4)
Why do we have to get an external NuGet package for this? Is Roslyn not part of the .NET framework?Sesquicarbonate
Roslyn in not part of the .NET Framework, so you have to get it with a NuGet package. CodeDom is part of the .NET Framework, but I think it supports only c# 5.Bowers
@Bowers how to support some default libraries with this code? I have tried to do using System.Linq; and got compilation error The type or namespace name 'Linq' does not exist in the namespace 'System' (are you missing an assembly reference?)Mortise
You can download the package Microsoft.NETCore.App.Ref or Microsoft.NETFramework.ReferenceAssemblies.net48 and reference the dll from the lib folder. You can find useful packages here github.com/meziantou/Meziantou.Analyzer/blob/…Bowers

© 2022 - 2024 — McMap. All rights reserved.