roslyn: Can't reference mscorlib in .Net Core

Here is sample code which is working on target 4.5.2, but doesn’t on Core 1.0.1.

using System;
using System.IO;
using System.Linq;
using System.Reflection;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;

class Program
{
    static void Main(string[] args)
    {
        var greetings = HelloWorlder.GetGreetings();
        // should write Hello World
        greetings();
    }
}

public static class HelloWorlder
{
    public static Action GetGreetings()
    {
        var tree = CSharpSyntaxTree.ParseText(@"
using System;
public class MyClass
{
    public static void Main()
    {
        Console.WriteLine(""Hello World!"");
        Console.ReadLine();
    }   
}");
        const string testAsmName = "testLib";

        var coreDir = Path.GetDirectoryName(typeof(object).GetTypeInfo().Assembly.Location);

        var mscorlib = MetadataReference.CreateFromFile(Path.Combine(coreDir, "mscorlib.dll"));
        var compilation = CSharpCompilation.Create(testAsmName,
            syntaxTrees: new[] { tree }, references: new[] { mscorlib });

        var emitResult = compilation.Emit($"{testAsmName}.dll");

        if (!emitResult.Success)
        {
            throw new Exception(string.Join(Environment.NewLine, emitResult.Diagnostics.Select((x, i) => $"{i + 1}. {x}")));
        }
        var ourAssembly = Assembly.Load(new AssemblyName(testAsmName));
        var type = ourAssembly.GetType("MyClass");

        var meth = type.GetRuntimeMethod("Main", Type.EmptyTypes);
        return () => meth.Invoke(null, null);
    }
}

As you can examine yourself, it writes Hello world on full framework but fails to compile on .Net Core with following errors:

  1. warning CS8021: No value for RuntimeMetadataVersion found. No assembly containing System.Object was found nor was a value for RuntimeMetadataVersion specified through options.
  2. (3,14): error CS0518: Predefined type ‘System.Object’ is not defined or imported
  3. (5,19): error CS0518: Predefined type ‘System.Void’ is not defined or imported
  4. (7,27): error CS0518: Predefined type ‘System.String’ is not defined or imported
  5. (7,9): error CS0518: Predefined type ‘System.Object’ is not defined or imported
  6. (7,9): error CS0103: The name ‘Console’ does not exist in the current context
  7. (8,9): error CS0518: Predefined type ‘System.Object’ is not defined or imported
  8. (8,9): error CS0103: The name ‘Console’ does not exist in the current context
  9. (3,14): error CS1729: ‘object’ does not contain a constructor that takes 0 arguments

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Comments: 24 (19 by maintainers)

Most upvoted comments

Let me try to put another spin on this. @Pzixel’s scenario is doing something quite specific: it’s blending runtime and design time. @Pzixel is using Roslyn to compile code on the fly and then wants to load & execute that program in the same process.

That’s different from “regular” design time. Normally, the Roslyn compiler simply takes text and compiles PE files on disk for a set of assemblies provided as the input. In that world, it doesn’t even matter what Roslyn itself is implemented in (in fact, in the past the compiler was a C++ app). And as @jaredpar said: the compiler is pretty dumb and pretends to know nothing about the assemblies your program is referencing. That’s why we usually pass the compiler “empty assemblies” that simply declare which APIs it has. At runtime, those assemblies need to be able to be resolved but where they live is irrelevant. We call these things reference assemblies.

However in @Pzixel’s scenario runtime and design time happen to be the same thing and that’s where things get a bit confusing. The reason being that people often start with the mindset of reflection which never had a separation of design time and runtime. So for example, doing something like this:

var coreDir = Path.GetDirectoryName(typeof(object).GetTypeInfo().Assembly.Location);
var mscorlib = MetadataReference.CreateFromFile(Path.Combine(coreDir, "mscorlib.dll"))

Seems fine but is actually an example where the code makes the assumption that things are laid out in a specific way at runtime which may not be the case.

So it’s not so much that Roslyn doesn’t support .NET Core – it does (first, Roslyn itself can run on .NET Core and you can use Roslyn to produce binaries for .NET Core). It’s about that when you’re using Roslyn at runtime to emit and load on the fly you’ve to understand how to do it correctly.

You should follow what Razor views in ASP.NET Core are doing. Require the developer to produce a set of reference assemblies that you can use at runtime to pass to Roslyn as references.

That being said, I understand that this isn’t super trivial today and that we lack good docs and features around this. We’ve discussed this recently with the MSBuild team who need to solve the same problem for their inline tasks. We’re thinking of ways to make it easier to get access to the set of assemblies that you be using for compilation.

I suggest that you open an issue in the dotnet/sdk repo as that’s where the request logically belongs.

So the answer is Roslyn is not working on .Net Core by design?

No. Roslyn is working as designed here. It’s being provided an mscorlib that is incomplete and is responding accordingly. Given a mscorlib that is a suitable reference assembly it will compile just fine.

The root problem here is that mscorlib, on Net Core, is not a stand alone reference library. That part is “By Design”. That’s not a decision that roslyn controls though.

I know about forwarders but I expected that if runtime can determine the right assembly (obviosly, because .Net Core code is runable),

The compiler is actually pretty dumb when it comes to resolving references. It makes literally no attempt at finding missing references. It requires that all the necessary references are passed to the compiler. If any are missing it will error. The job of finding the set of references (and resolving conflicts) is up to MSBuild, project.json, make, etc … This is how the basic build responsibilities are divided.

Also the actual mscorlib which contains Object (lets call it System.Private.Corlib) isn’t usable as a reference library. It has a number of inconsistencies and duplicate types that make it unsuitable as a reference assembly. Code which is normally compilable won’t compile when using System.Private.Corlib due to these changes. Hence even if Roslyn found it, the code isn’t guaranteed to compile. It really needs a referencable version of mscorlib in order to function here.

I don’t want to dismiss your concerns here. This is a problem we’ve faced ourselves in a number of areas: scripting + MSBuild in particular. Unfortunately there are no good answers here. Actually just met about this issue just a few weeks ago.

This issue is likely to get more traction in the corefx repo though. This is where the decisions around runtime / reference assemblies occur.

CC @terrajobst, @ericstj

I see. So that would require people to understand the implementation closure if they use Roslyn to generate code at runtime. We don’t have a good mechanism for this today. Look like this would need a properly designed feature.

@richlander we should talk about this and clarify who of us should drive this. I think it’s a clear gap in our offering we need to close.

@tmat That wiki page describes a fairly complicated way of gathering and distributing reference assemblies. Would it make sense to also mention the approach of using <PreserveCompilationContext> and Microsoft.Extensions.DependencyModel? It’s less versatile, but also much simpler.

For which the current offering sucks.

I agree.

@terrajobst Please include me in the loop.

@terrajobst You have to pass in all assemblies that are involved in the compilation. Type forwarders are followed but you need to specify both the assembly that defines the forwarder and the assembly that the forwarder forwards to.

In BenchmarkDotNet we have the same problem. Today we are using dotnet cli to do the compilation, but people start migration from project.json and instead of supporting both project.json and new csprojs I would like to switch to Roslyn.