Build and Test .NET Runtime repository locally — Part 1 — Get something working
The dotnet repository hosted at Github has very good documentation around working with building the runtime to be able to experiment locally how internally it works.
This post contains notes of the most important things to do in order to be able to do a simple change the System.Private.CoreLib code and be able to test it out. The idea is to keep it as a reference to come back to this post and be able to quickly revisit how to work with the codebase and experiment.
Step 1 — Clone the default branch of repository which mostly is main branch
# Clone the repo with only default branch which is mostly the main branch
git clone https://github.com/dotnet/runtime.git --single-branch
Step 2 — Build both the coreclr and runtime libraries without building tests
To keep things simple just build both the coreclr and runtime. Once we are more familiar with process, we can optimize and see what options are provided by build process to focus on areas of interest.
For the scope of this post, we will make a simple change in the CoreLib (aka System.Private.CoreLib) codebase which is the lowest layer of managed code directly corresponding to unmanaged CoreClr code. So there should be no need to build most of the libraries.
We can build subset of code using the -subset argument. In this case we are building both the coreclr (clr) and managed libraries (libs) both. Default build mode is debug.
There are command line arguments to build each of the subset in different configuration depending on use case. Like if changing libs, we can build clr in release mode by using -runtimeConfiguration argument as Release and optional -librariesConfiguration argument as Debug. In command below, both of them are default to Debug.
# To get help and see all options
build.cmd -help
# Build the CoreClr and Libraries in Debug mode
build.cmd -subset clr+libs
This should take sometime to build and will generate artifacts folder in the root of the repository.
Step 3 — How to test with generated build output using Corerun.exe
There are various ways to test the generated build output and we can read about them here — https://github.com/dotnet/runtime/blob/main/docs/workflow/testing/using-corerun-and-coreroot.md
We will use the Corerun option here. Before going futher, lets create a simple console application and initialize a Task type. This assumes that you have a .NET SDK installed preferably .NET 9 to be able to run below command which generates a project.
# Create and Change to directory
mkdir TestConsoleApp && cd TestConsoleApp
# Create a console application
dotnet new console
# Build the app
dotnet build
Lets add the following code to initialize a Task in the Program.cs entry point method. Build code again using dotnet build -
var task = new Task(() => {});
We will basically add a console statement in the CoreLib code base when a task is initialized for testing purpose next but lets first setup CoreRun which needs us to set environment variables to correctly point to the build output containing CoreClr runtime and .NET runtime libraries.
CoreRun is generated by build and we will add its location to the Path environment variable.
# Note we are working in the TestConsoleApp source directory
# Add location of CoreRun.exe to existing Path variable
# Make sure to change <repo_root> below to correct directory
set PATH=%PATH%;<repo_root>\artifacts\bin\coreclr\windows.x64.Debug
Now we need to set the CORE_LIBRARIES environment variable to be able to find the runtime libraries. If we dont and skip directly trying to run the application, we will see that CoreRun.exe will not be able to find the runtime libraries and fail to load assemblies.
There are two ways to set it and tell CoreRun to find the runtime libraries -
- Set it to currently installed .NET SDK path in Program Files folder. When using this CoreRun somehow automatically figures how to load correct version of runtime libraries we built. Something to explore if required.
# Set env variable. Make sure to put the correct version that exists on machine
set CORE_LIBRARIES=%ProgramFiles%\dotnet\shared\Microsoft.NETCore.App\9.0.0
- Set it to build runtime folder from artifacts directory. We can use this approach also
# Set env variable to build runtime directory in artifacts
# In my case, its net10.0 build that runtime codebase targets hence the path below
set CORE_LIBRARIES=<repo_root>\artifacts\bin\runtime\net10.0-windows-Debug-x64
With this place we should be able to just go to the build output of our test application and use the CoreRun to run it -
# From the TestConsoleApp application root, navigate to the output.
# Navigate to net folder depending on version. Assuming .NET 9 as
# thats what I had installed on my machine when building code.
cd bin\Debug\net9.0\
# Run the application using CoreRun.exe which is already added in path
corerun.exe TestConsoleApp.dll
At this point we should be able to run the application using our build output.
Step 4 — Make change in CoreLib, build code again and test
In step 3, we added some code to initialize a Task. Lets update the source of Task type in the CoreLib to write a console message so we can test out. To do this you can open the CoreLib in Visual Studio using below command.
# Open the CoreLib in Visual Studio. This is in Command window at runtime repo root
build.cmd -vs System.Private.CoreLib
Lets search for Task.cs file and update the constructor with a simple console log using Internal Console type as mentioned in documentation.
Code change below -
/// <summary>
/// Initializes a new <see cref="Task"/> with the specified action.
/// </summary>
/// <param name="action">The delegate that represents the code to execute in the Task.</param>
/// <exception cref="ArgumentNullException">The <paramref name="action"/> argument is null.</exception>
public Task(Action action)
: this(action, null, null, default, TaskCreationOptions.None, InternalTaskOptions.None, null)
{
Internal.Console.WriteLine($"Creating task - {action.Method.Name}");
}
Note that we already build the code one in Debug mode in Step 2. So we dont need to build the complete source again to save time. Here is the command do it. This command automatically does all changes required to make CoreRun load the new changes -
# Just build the subsets actually required
build.cmd clr.corelib+clr.nativecorelib+libs.pretest -rc Debug
Now lets go back the application and simply try running it again using corerun. We dont need to rebuild the application. This time we should be able to see the console statement we added in Task type.
Here is sample output -
Will explore how to work with other 2 ways of testing the build artifacts as explained at in future https://github.com/dotnet/runtime/blob/main/docs/workflow/testing/using-corerun-and-coreroot.md