Technik Empire Technik Empire - 3 months ago 58
C# Question

.Net Core - CS0012 'Object' is defined in an assembly that is not referenced

I'm brand new to .Net Core and I'm trying to build a build system based on it. As part of this project, I've created an abstract class that spells out what a build task should implement, and I've stuffed this into a shared library.

The executable project references this library and scans project directories for a specially named directory, then checks to see if there are any

.cs
files in there. These scripts are loaded and then compilation is attempted using the tools provided through
Microsoft.CodeAnalysis
and friends.

With that background, here's an odd issue I'm hitting at the compilation stage:

If I attempt to supply the shared library containing the abstract class to the compilation process as a reference, I get the following error:


Failed CS0012: The type 'Object' is defined in an assembly that is not
referenced. You must add a reference to assembly 'mscorlib,
Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e'.


Followed by a bunch complaints about predefined types:


CS0518: Predefined type 'System.Boolean' is not defined or imported
CS0518: Predefined type 'System.Boolean' is not defined or imported
CS0518: Predefined type 'System.Void' is not defined or imported ...
etc etc.


However, if I omit the reference and instead parse each of the shared library's source files into syntax trees and pass them to the compilation process, the whole process succeeds and I get a returned in-memory assembly I can pull types out of and instantiate.

I've read everything on this error that Google has to offer, and I'm at a loss. Can somebody enlighten me as to why this is happening and bonus internet points for how I can accomplish my original goal of simply linking in a common shared library?

Relevant code

CSharpParseOptions parseOptions = CSharpParseOptions.Default;

SyntaxTree jobSyntaxTree = CSharpSyntaxTree.ParseText(scriptContents, parseOptions);

string generatedAssemblyName = Path.GetRandomFileName();
var referencedAssemblies = Assembly.GetEntryAssembly().GetReferencedAssemblies();

foreach(var referencedAssembly in referencedAssemblies)
{
var rfas = Assembly.Load(referencedAssembly);
references.Add(MetadataReference.CreateFromFile(rfas.Location));
}

var op = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary);

CSharpCompilation compilation = CSharpCompilation.Create(
generatedAssemblyName,
syntaxTrees: new[] { jobSyntaxTree },
references: references,
options: op);

var ms = new MemoryStream();

EmitOptions emitOptions = new EmitOptions();

EmitResult result = compilation.Emit(ms);

if(result.Success)
{
// Yay
}
else
{
// Boo-hoo
}


Project.json file for the shared library

{
"title": "BuildBotCore",
"version": "1.0.0-*",
"buildOptions": {
"emitEntryPoint": false,
"preserveCompilationContext": true
},
"dependencies": {
"Microsoft.NETCore.App": {
"type": "platform",
"version": "1.0.0"
},
"System.Runtime": "4.1.0",
"System.Runtime.Loader": "4.0.0",
"Microsoft.NETCore.Portable.Compatibility": "1.0.1"
},
"frameworks": {
"netcoreapp1.0": {
"imports": "netcore50",
"buildOptions": {
"preserveCompilationContext": true
}
}
},
"configurations": {
"Debug": {
"buildOptions": {
"define": [
"DEBUG",
"TRACE"
],
"optimize": false,
"preserveCompilationContext": true
}
},
"Release": {
"buildOptions": {
"define": [
"RELEASE",
"TRACE"
],
"optimize": true,
"preserveCompilationContext": true
}
}
}
}


Project.json for main executable

{
"version": "1.0.0-*",
"buildOptions": {
"emitEntryPoint": true,
"preserveCompilationContext": true
},
"dependencies": {
"Microsoft.NETCore.App": {
"type": "platform",
"version": "1.0.0"
},
"System.Runtime": "4.1.0",
"Microsoft.CodeAnalysis.CSharp": "1.3.2",
"System.Runtime.Loader": "4.0.0",
"Microsoft.NETCore.Portable.Compatibility": "1.0.1",
"BuildBotCore": "1.0.0-*"
},
"frameworks": {
"netcoreapp1.0": {
"imports": "netcore50",
"buildOptions": {
"preserveCompilationContext": true
}
}
},
"configurations": {
"Debug": {
"buildOptions": {
"define": [
"DEBUG",
"TRACE"
],
"optimize": false,
"preserveCompilationContext": true
}
},
"Release": {
"buildOptions": {
"define": [
"RELEASE",
"TRACE"
],
"optimize": true,
"preserveCompilationContext": true
}
}
}
}

Answer

TL;DR
At the end of the day you need to pass a reference to "mscorlib.dll" and its private variant. This will make the above compilation errors go away. This is also cross-platform in .net core, as I've tested this solution on Linux and Windows.

Full version & code samples
Since I'm waiting for account deletion, I'll post this last answer.

The solution is to manually include mscorlib.dll based on the location of some other type's assembly. Like this:

// Get the directory of a core assembly. We need this directory to
// build out our platform specific reference to mscorlib. mscorlib
// and the private mscorlib must be supplied as references for
// compilation to succeed. Of these two assemblies, only the private
// mscorlib is discovered via enumerataing assemblies referenced by
// this executing binary.
var dd = typeof(Enumerable).GetTypeInfo().Assembly.Location;
var coreDir = Directory.GetParent(dd);            

List<MetadataReference> references = new List<MetadataReference>
{   
    // Here we get the path to the mscorlib and private mscorlib
    // libraries that are required for compilation to succeed.
    MetadataReference.CreateFromFile(coreDir.FullName + Path.DirectorySeparatorChar + "mscorlib.dll"),
    MetadataReference.CreateFromFile(typeof(object).GetTypeInfo().Assembly.Location)           
};

There is a second mscorelib, but its name is prefixed with "private". This is automatically referenced by the executing assembly, so there's no need to manually generate its path like above.

You get this private mscorlib when you pull references from the executing assembly which is doing the dynamic code compilation itself. Like so:

// Enumerate all assemblies referenced by this executing assembly
// and provide them as references to the build script we're about to
// compile.
var referencedAssemblies = Assembly.GetEntryAssembly().GetReferencedAssemblies();
foreach(var referencedAssembly in referencedAssemblies)
{
    var loadedAssembly = Assembly.Load(referencedAssembly);   

    references.Add(MetadataReference.CreateFromFile(loadedAssembly.Location)); 
}

So now with these references assembled, you can simply pass them to your code compilation:

// Initialize the compilation with our options, references and the
// already parsed syntax tree of the build script.
CSharpCompilation compilation = CSharpCompilation.Create(
    generatedAssemblyName,
    syntaxTrees: new[] { jobSyntaxTree },
    references: references,
    options: op);           

// Compile and emit new assembly into memory.
var ms = new MemoryStream();
EmitResult result = compilation.Emit(ms);

... and voila, you can dynamically compile in-code with Roslyn cross-platform in .Net core.

Comments