Back in 2014, Microsoft announced the formation of the .NET Foundation, an independent organization that aims to foster open-source development surrounding the .NET ecosystem.

Since then, the .NET Foundation has been open-sourcing key parts of the .NET runtime.

This has had the fantastic effect of bringing the .NET ecosystem to Linux and OS X, with the ability to run managed CIL (Common Intermediate Language, like bytecode of the Java VM) using an instance of the .NET CLR (Common Language Runtime) on OS X.

In this post, we’ll look at what it takes to build the CLR and .NET Core Runtime Libraries built on OS X.

Unfortunately, compiling managed code (easily) is still a bit out of reach for the current level of OS X support. To compile the C# code we write here, we will be relying on Mono, a long-standing project that has run for a decade, trying to bring a fully API-compatible runtime for .NET to Unix derivates.

In order to get our runtime ready, we will need to rely on Windows to do some of the compilation in order to get the “standard library” for .NET running (mscorlib.dll) and other foundation-like runtime objects.

Additionally, compiling parts of the runtime and standard library is still not fully supported on non-Windows environments, for the same reasons as stated above. The standard release version of Mono won’t quite do what we want here without patching so we will need to build these libraries on a Windows machine. I look forward to these gaps being closed, but for now, we will rely on the Windows environment to compile some of these needed libraries for us.

Some Setup before we get started:

  • Mac with OS X (I’m running 10.10 Yosemite, but 10.9 should also work)
    • A directory where all this will take place (I’ll be using “~/net/net-demo/” for the duration of this post), with the following subdirectories:
      • runtime, “~/net/net-demo/runtime”, where all the files we will build that are necessary for running managed code on OS X will reside
      • packages, ~/net/net-demo/packages, where we will download some runtime packages that are needed to run our compiled managed code.
    • Mono Installed
    • A copy of the git repo for CoreCLR
    • Homebrew installed
    • CMake (as installed via homebrew “brew install cmake”)
  • Windows Computer (I’m running Parallels with Windows 10 preview on my Mac)

Building CoreCLR on the Mac:

To start, we will need to compile the Common Language Runtime for the Mac. This step is fully supported on non-Windows environments, so there is no need for switching over to Windows yet.

  • First, you’ll need to make sure you have a copy of the the .NET coreCLR checked out.
  • From your “~/net/net-demo” directory, run `git clone https://github.com/dotnet/coreclr`
  • Then, make sure you have cmake installed:

brew install cmake

Next, we’ll actually compile the CoreCLR.

  • From the directory ~/net/net-demo/coreclr/ run ./build.sh
  • Grab a cup of coffee, because this may take a couple of minutes
  • Success!

Now, we will need to copy some files from our successful compilation to the runtime directory we created earlier, core:

  • Copy

~/net/net-demo/coreclr/bin/Product/OSX.x64.Debug/corerun
to
~/net/net-demo/runtime/corerun

  • Copy

~/net/net-demo/coreclr/bin/Product/OSX.x64.Debug/libcoreclr.dylib
to
~/net/net-demo/runtime/libcoreclr.dylib

Building the .NET Framework for the Mac (but on Windows)

Alright, time to fire up that Windows Virtual machine.

Since the bulk of the standard library for .NET programs (think things like Collection Types, XML parsing, File handles, and some other OS related functions) is written in managed code (code that needs the CLR to run) and the compilation of managed code on non-Windows platforms is not yet fully supported, we will have to use Windows for this step. However, expect this to change soon. The Roslyn compiler is currently open source and the team is actively working towards supporting Unix based compilation as I write this.

Digression: In fact, it is even currently possible to compile the Roslyn compiler for OS X using a patched version of the Mono compiler. You can then use this copy of the Roslyn compiler to compile managed code on OS X for OS X. Perhaps this will be the topic of a future post.

Building mscorlib (CoreCLR):

  • Make sure you have a checkout of the CoreCLR repository (“C:Usersejeffersnettestcoreclr”) and navigate to it.

windows_coreclr_clone

  • Run the following command

C:Usersejeffersnettestcoreclr>build.cmd osxmscorlib

  • Copy

C:UsersejeffersnettestcoreclrbinobjUnix.x64.Debugmscorlib.dll
to
C:Usersejeffersnet-bin

Building supporting runtime libraries (CoreFx):

  • Make sure you have a checkout of the CoreFX repository (“C:Usersejeffersnettestcorefx”) and navigate to it.windows_coreclr_clone
  • Run the following command:

C:Usersejeffersnettestcorefx>build.cmd /p:OSGroup=OSX /p:SkipTests=true

  • Copy

C:UsersejeffersnettestcorefxbinOSX.AnyCPU.DebugSystem.ConsoleSystem.Console.dll
to
C:Usersejeffersnet-bin 

  • Copy

C:UsersejeffersnettestcorefxbinOSX.AnyCPU.DebugSystem.Diagnostics.DebugSystem.Diagnostics.Debug.dll
to
C:Usersejeffersnet-bin

Transfer the Windows-built binaries to the Mac:

Now that we have compiled the OS X versions of the .NET core framework on Windows, we need to copy the output of that process back to the Mac.

  • The two files stored on the Windows Computer at “C:Usersejeffersnet-bin”
    • System.Diagnostics.Debug.dll
    • System.Console.dll
  • Copy to the Mac to: “~/net/net-demo/runtime/

Your “~/net/net-demo/runtime” directory should now look like the following:

mac_finder_runtime

 

Downloading additional Packages

In order to run even a relatively basic application, we will need to download some additional libraries that make up the core .NET Framework.

In the Windows world, package management is handled by an application called NuGet, which we will be using below.

We will be using this same package management system on our Mac in order to download the needed assemblies. Instructions here are modified from the Mac OS X instructions on the CoreCLR GitHub.

  • Navigate to “~/net/net-demo/” directory and run the following command:

curl -L -O https://nuget.org/nuget.exe

  • In the ~/net/net-demo/packages directory and create a packages.config file with the following contents
<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="System.Console" version="4.0.0-beta-22703" />
  <package id="System.Diagnostics.Contracts" version="4.0.0-beta-22703" />
  <package id="System.Diagnostics.Debug" version="4.0.10-beta-22703" />
  <package id="System.Diagnostics.Tools" version="4.0.0-beta-22703" />
  <package id="System.Globalization" version="4.0.10-beta-22703" />
  <package id="System.IO" version="4.0.10-beta-22703" />
  <package id="System.IO.FileSystem.Primitives" version="4.0.0-beta-22703" />
  <package id="System.Reflection" version="4.0.10-beta-22703" />
  <package id="System.Resources.ResourceManager" version="4.0.0-beta-22703" />
  <package id="System.Runtime" version="4.0.20-beta-22703" />
  <package id="System.Runtime.Extensions" version="4.0.10-beta-22703" />
  <package id="System.Runtime.Handles" version="4.0.0-beta-22703" />
  <package id="System.Runtime.InteropServices" version="4.0.20-beta-22703" />
  <package id="System.Text.Encoding" version="4.0.10-beta-22703" />
  <package id="System.Text.Encoding.Extensions" version="4.0.10-beta-22703" />
  <package id="System.Threading" version="4.0.10-beta-22703" />
  <package id="System.Threading.Tasks" version="4.0.10-beta-22703" />
  <package id="Microsoft.NETCore.Runtime.CoreCLR.ConsoleHost-x64" version="1.0.0-beta-1.0.0-beta-22819" />
  <package id="Microsoft.NETCore.Runtime.CoreCLR-x64" version="1.0.0-beta-1.0.0-beta-22819"/>
</packages>
  • Now, from the “~/net/net-demo/packages” directory, run the following command, which will download the assemblies we need to run some code:
    $ mono nuget.exe restore packages/packages.config -Source https://www.myget.org/F/dotnet-corefx/ -PackagesDirectory packages
    
  • The final step here is to make the newly downloaded assemblies available to the runtime. That means we will need to copy the downloaded assemblies to our “~/net/net-demo/runtime” directory. You can do this with the following command:
    find . -wholename '*/aspnetcore50/*.dll' -exec cp -n {} ~/coreclr-demo/runtime ;
    • This command will copy all the assemblies out of their subfolders and into our runtime directory. This command will also skip over copying the files we generated on Windows earlier, System.Console.dll and System.Diagnostics.Debug.dll

Compiling and Running Some Code

Now that we have gone through all that setup, it’s time to run some code. Below is a simple little Hello World program that simply outputs text, but you should be able to run almost any program that doesn’t rely on Windows specific API calls!

Create a file called HelloWorld.cs with the following contents in your “~/net/net-demo/” directory.

using System; public class Program { public static void Main (string[] args) { Console.WriteLine("Hello, World! From the CLR on OSX!"); } }


Compiling

  • Again, since managed code compilation is not yet fully supported on OS X, we need to rely on the Mono compiler. With mono installed, run the following command
    $ mcs /nostdlib /noconfig /r:packages/System.Console.4.0.0-beta-22703/lib/contract/System.Console.dll /r:packages/System.Runtime.4.0.20-beta-22703/lib/contract/System.Runtime.dll -out:runtime/HelloWorld.exe HelloWorld.cs

mono_compile

Running

  • Finally! Navigate to the runtime directory and execute the following command!
    ./corerun HelloWorld.exe

    hello_world_clr

So that’s it! As you can see, the process is still pretty daunting, and doing this kind of thing is still only really an academic exercise. I’m looking forward to future releases from the .NET Foundation!

Leave a Reply

Your email address will not be published. Required fields are marked *