.NET Compiler Platform, also known by its nickname Roslyn, is a set of open-source compilers and code analysis APIs for C# and Visual Basic .NET languages. The primary features of Roslyn are to provide compilers and APIs for code analysis and refactoring.
There is a splendid must-read article about Roslyn entitled How Microsoft rewrote its C# compiler in C# and made it open source. Furthermore, Roslyn is open source so you can check out source code on GitHub: https://github.com/dotnet/roslyn.
I firmly believe that every software engineer should know the basics of software craft. For example, what is compiler, how source code is “converted” to IL/executable code, etc… With fancy tools and modern IDEs (e.i. Visual Studio) we sometimes forget how things are done under the hood. We just simple create new project, hit F5, and we have running application.
Furthermore, sometimes I am amazed how some developers do not even distinct or know distinction between Visual Studio, msbuild, dotnet tool, C# compiler, runtime, SDKs, etc…
In this blog post I will focus on some of the basics – compiling code into executables. I will play around a bit with Roslyn C# compiler, which is modern, completely managed (C# & .NET) compiler & code analyzer. I will show how to use Roslyn C# compiler to compile C# source code to target .NET 5 (which is still in preview as of this writing).
Let’s do some code
For my experiment, I created two C# source files and one batch file to automate some compile and run tasks.. Let’s take a look.
First file, Program.cs
is main entry point of my “one-line application”. It’s using new C#9 feature called Top-level statements. You can read more about this in my blog post here: C# 9: Top-level statements.
1 |
Jenx.CscDemo.PrintConsole.Print($"Jenx.si executed on {(System.Runtime.InteropServices.RuntimeInformation.OSDescription)} with {(System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription)} today :) at: {(System.DateTime.Now.ToLongTimeString())}"); |
Second file, just to some extra complexity in compilation process (by compiling multiple c# files) is PrintConsole.cs
. This file contains simple logic for printing text to console, e.g.:
1 2 3 4 5 6 7 8 9 10 11 12 |
using System; namespace Jenx.CscDemo { public static class PrintConsole { public static void Print(string text) { Console.WriteLine(text); } } } |
In order to compile my app, I will use Roslyn C# compiler which comes with .NET 5.0 SDK. Roslyn C# compiler can be started by executing csc.dll
. In the following section, compile command with full paths are presented for better explicit reference overview (but on the other hand, command is less transparent and simple).
1 2 3 4 5 6 7 |
dotnet "C:\Program Files\dotnet\sdk\5.0.100-rc.1.20452.10\Roslyn\bincore\csc.dll" -r:"C:\Program Files\dotnet\shared\Microsoft.NETCore.App\5.0.0-rc.1.20451.14\System.Private.CoreLib.dll" -r:"C:\Program Files\dotnet\shared\Microsoft.NETCore.App\5.0.0-rc.1.20451.14\System.Console.dll" -r:"C:\Program Files\dotnet\shared\Microsoft.NETCore.App\5.0.0-rc.1.20451.14\System.Runtime.dll" -r:"C:\Program Files\dotnet\shared\Microsoft.NETCore.App\5.0.0-rc.1.20451.14\System.Runtime.InteropServices.RuntimeInformation.dll" *.cs -out:Jenx.CscDemo.dll |
If you will look, there is no csc.exe
in .NET 5 SDK. This is due to fact that Roslyn C# compiler is cross-platform C# application and therefore implemented as csc.dll
. Therefore, it must be started with dotnet tool. This way it can be used on Windows, Linux, MacOS, etc…
Anyway, If I run upper command I compile my source code and I get Jenx.CscDemo.dll assembly which is basically runtime dependent .NET console application. Again, I can run it with dotnet
tool, e.g.
dotnet Jenx.CscDemo.dll
But wait, when I execute my app I get:
1 2 3 4 5 6 |
D:\jenx\Jenx.netcore.csc\demo>dotnet Jenx.CscDemo.dll Cannot use file stream for [D:\jenx\Jenx.netcore.csc\demo\Jenx.CscDemo.deps.json]: No such file or directory A fatal error was encountered. The library 'hostpolicy.dll' required to execute the application was not found in 'D:\jenx\Jenx.netcore.csc\demo\'. Failed to run as a self-contained app. - The application was run as a self-contained app because 'D:\jenx\Jenx.netcore.csc\demo\Jenx.CscDemo.runtimeconfig.json' was not found. - If this should be a framework-dependent app, add the 'D:\jenx\Jenx.netcore.csc\demo\Jenx.CscDemo.runtimeconfig.json' file and specify the appropriate framework. |
Therefore, I need to create new file named Jenx.CscDemo.runtimeconfig.json
with following content:
1 2 3 4 5 6 7 8 9 |
{ "runtimeOptions": { "tfm": "net5.0", "framework": { "name": "Microsoft.NETCore.App", "version": "5.0.0-rc.1.20451.14" } } }> |
This is basically runtime info telling which framework is my app targeting. Now everything works as expected:
1 2 |
D:\jenx\Jenx.netcore.csc\demo>dotnet Jenx.CscDemo.dll Jenx.si executed on Microsoft Windows 10.0.18362 with .NET 5.0.0-rc.1.20451.14 today :) at: 10:56:02 |
I also did some compile automation batch script. I will share as an extra resource, maybe it will be interesting for someone. Script below checks/display .NET version, then compiles the sources and starts the app.
1 2 3 4 5 6 7 8 9 10 11 12 |
cls @ECHO OFF echo Active dotnet version... dotnet --version echo. echo Compiling... @ECHO ON dotnet "C:\Program Files\dotnet\sdk\5.0.100-rc.1.20452.10\Roslyn\bincore\csc.dll" -r:"C:\Program Files\dotnet\shared\Microsoft.NETCore.App\5.0.0-rc.1.20451.14\System.Private.CoreLib.dll" -r:"C:\Program Files\dotnet\shared\Microsoft.NETCore.App\5.0.0-rc.1.20451.14\System.Console.dll" -r:"C:\Program Files\dotnet\shared\Microsoft.NETCore.App\5.0.0-rc.1.20451.14\System.Runtime.dll" -r:"C:\Program Files\dotnet\shared\Microsoft.NETCore.App\5.0.0-rc.1.20451.14\System.Runtime.InteropServices.RuntimeInformation.dll" *.cs -out:Jenx.CscDemo.dll @ECHO OFF echo Running... echo. dotnet Jenx.CscDemo.dll |
The fact that I compiled my code runtime (.NET 5) dependent (on the contrary to the platform-specific publishing) I can simply copy this assembly to some other platform and run it with dotnet
tool. Let’s quickly check if my app is running on MacOS:
Nice, it works!
Summary
Although, dotnet .NET Core CLI tool is preferable way to build .NET (Core) application, “old school” C# compiler way is also possible.
Of course, real complex projects almost always requires to use IDEs like Visual Studio which use complex solutions combined out of tenths or even hundreds of C# projects. But, sometimes engineer must look back and turn to the basics. Compiling simple C# is one of those things.
Happy coding!