Andry
Andry

Reputation: 16895

Cannot create a compilation in Roslyn from source code

For test purposes, I need to get a System.Reflection.Assembly from a string source which contains a source code. I am using Roslyn:

SyntaxTree tree = CSharpSyntaxTree.ParseText(source);
CSharpCompilation compilation = CSharpCompilation.Create("TestCompilation", new[] { tree });

Assembly assembly = null;
using (var stream = new MemoryStream())
{
    var emitResult = compilation.Emit(stream);
    if (!emitResult.Success)
    {
        var message = emitResult.Diagnostics.Select(d => d.ToString())
            .Aggregate((d1, d2) => $"{d1}{Environment.NewLine}{d2}");

        throw new InvalidOperationException($"Errors!{Environment.NewLine}{message}");
    }

    stream.Seek(0, SeekOrigin.Begin);
    assembly = Assembly.Load(stream.ToArray());
}

As you can see my attempt here is to emit a CSHarpCompilation object so that I can get the Assembly later. I am trying to do this with:

var source = @"
  namespace Root.MyNamespace1 {
    public class MyClass {
    }
  }
";

Emit errors

But I fail at var emitResult = compilation.Emit(stream) and enter the conditional which shows the error. I get 1 warning and 3 errors:

So it seems I need to add reference to mscorelib and it also seems like I need to tell Roslyn that I want to emit a class library, not an executable assembly. How to do that?

Upvotes: 6

Views: 2871

Answers (2)

LosManos
LosManos

Reputation: 7702

For creating a netstandard lib from not-netstandard code (in my case I create a netstandard lib from core3.1) the code should be

var compilation = CSharpCompilation.Create("TestCompilation",
    syntaxTrees: new[] { 
        tree 
    }, 
    references: new[] { 
        MetadataReference.CreateFromFile(@"C:\Users\YOURUSERNAME\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\netstandard.dll" 
    }, 
    options: 
        new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));

A crux here is the path.
As the host code is core3.1 one cannot use MetadataReference.CreateFromFile(typeof(object).Assembly.Location) as it references a core3.1 object and not a netcore2.0 object.
As referencing a nuget package (nowadays) downloads them to the %USERPROFILE%\.nuget\packages folder it can be loaded from there. This does not hold for any other user though so a different solution must be designed. One could utilise System.Environment.GetFolderPath(System.Environment.SpecialFolder.UserProfile) but that probably won't hold for CI/CD.

Update:
System.Environment.GetFolderPath(System.Environment.SpecialFolder.UserProfile) does hold for CI/CD.

MetadataReference.CreateFromFile( Path.Combine(
    UserProfilePath, ".nuget", "packages", "netstandard.library", "2.0.3", "build", 
    "netstandard2.0", "ref", "netstandard.dll"))

See LehmanLaidun builds.

Upvotes: 0

JoshVarty
JoshVarty

Reputation: 9426

You're missing a metadata reference to mscorlib and you can change the compilation options via CSharpCompilationOptions.

Create your compilation as follows:

var Mscorlib = MetadataReference.CreateFromFile(typeof(object).Assembly.Location);
var options = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary);
var compilation = CSharpCompilation.Create("TestCompilation",
    syntaxTrees: new[] { tree }, references: new[] { Mscorlib }, options: options);

Upvotes: 10

Related Questions