Here a short how-to start coding and contributing to the Mono Base Class Library.

First thing you need to do if you haven’t already is to go read the coding guidelines.

General Directory Structure

Mono’s BCL implementation is located in mono/mono under the mcs/class subdirectory.

Each folder in this directory represent an assembly and is named like it. Inside each of those directory you will find 4 kinds of folders:

Namespace folders
Those are where the actual BCL class will live, obviously based on their namespace.
Test
This folder mirrors the parent namespace hierarchy for unit tests.
Documentation
This folder contains the ECMA documentation for the assembly classes.
Assembly
This is mainly for the AssemblyInfo.cs file.

Following a standard practice, every type and every test of this type must be in its own file e.g. the String type will be in class/corlib/System/String.cs and its tests in class/corlib/Test/System/StringTest.cs.

Build System

The build system for class libraries has been heavily abstracted in Makefile and is thus very simple to work with.

At the top-level, every assembly directory is registered in the mcs/class/Makefile file. This files contains several _dirs-suffixed variables for each type of framework (.NET 2, .NET 4, mobile, …) and a common one when the assembly is part of all framework versions. To add a new assembly to the build, you will simply add it to the correct variable.

At the assembly level, a Makefile is also present which always contains a couple of standard information like its current path and then includes the default rules.make and library.make that contains all the library building logic.

In this Makefile, the build can be customized using several Make variables among which the most important are:

  • LIBRARY to give the final DLL output name e.g. “System.Core.dll”.
  • LIB_MCS_FLAGS to append build arguments (like references) to the compiler when building the assembly.
  • TEST_MCS_FLAGS similarly when building the unit test assembly.
  • RESOURCE_FILES for files to add as embedded resources.

The Makefile can then contain custom processing if needs be or if the library build is more complicated than the default allows. Generally though you shouldn’t have to mess with it.

Here is a canonical example with the Makefile that builds the System.Net.Http assembly:

thisdir = class/System.Net.Http
SUBDIRS = 
include ../../build/rules.make

LIBRARY = System.Net.Http.dll

LIB_MCS_FLAGS = -r:System.Core.dll -r:System.dll

TEST_MCS_FLAGS = -r:System.dll -r:System.Core.dll

include ../../build/library.make

The most common interaction you will have with the build system is to add files and tests to it. For that purpose you don’t need to touch the Makefiles at all since the class list for each assembly is contained into dedicated files.

These files will always be named in the form of 'assembly name'[_test].dll.sources. Sometimes, they are also optionally prefixed with a profile name (like net_4_0) if relevant in which case its the content is appended when that profile is built.

In our System.Net.Http case, the assembly file list is stored in the System.Net.Http.dll.sources file and its unit tests in the System.Net.Http_test.dll.sources file

If your contribution is only for a specific framework version, you can use a couple of defines automatically declared by the build system.

There are defines for each framework version in the form of NET_{major version}_{minor version} like NET_2_0 or NET_4_5. You can use these defines in code with #if ... #endif blocks to add members that are specific to a .NET version.

Note that these defines are inclusive i.e. when building the 4.5 version of the assembly, all 4.0 and 2.0 #if blocks will also be included so that you only need to specify the framework version when the member was introduced.

For example, the System.IO.Path.Combine (params string[]) method was made public in .NET 4.0 when it was an internal implementation in Mono before. It’s thus declared like the following in the source:

#if NET_4_0
		public
#else
		internal
#endif
		static string Combine (params string [] paths)
		{

Building and Running the Tests

To build an individual assembly, simply cd to its directory and type make to build it with the latest profile available.

If you want to build a specific framework version of the assembly, you can use the PROFILE variable e.g. to build the 2.0 version of System.dll you would do:

cd mcs/class/System && make PROFILE=net_2_0

By default the build will shave most of the output but if you need to see what the final compilation command line looks like, you can activate verbose mode with the V variable like so:

cd mcs/class/System && make PROFILE=net_2_0 V=1

To execute the unit-tests for the assembly, you use the run-test Makefile target which will automatically compile NUnit, your assembly test and then run it. It also accepts the PROFILE variable to execute test in a specific profile.

By default the run-test target will run all the fixture for the assembly. If you are only interested in executing one fixture, you can use the aptly named FIXTURE variable like so:

cd mcs/class/System.Core && make run-test FIXTURE=System.Linq.EnumerableTest

If you need to further customize how NUnit runner is executed, you can use the TEST_HARNESS_FLAGS variable to directly pass argument to the process. This can be useful if you want to execute a single test case:

cd mcs/class/System.Core && make run-test TEST_HARNESS_FLAGS='-run=MonoTests.System.Linq.EnumerableTest.TestSelect'

Notice that in this case, you need to prefix the test namespace with MonoTests. as every tests in Mono is contained into a namespace of the form MonoTests.The.Assembly.Namespace (hint: your tests should too).

Check-list Before the Pull-Request

Before submitting your pull-request, make sure of the following:

  • Your code comply with the coding guidelines mentioned at the top of this how-to. This is most often the cause of initial refusal of the PR.
  • With your code included, you can do a full Mono build and running the entire test suite doesn’t exhibit new regressions (use make check at the top of your Mono checkout).
  • All new code added has corresponding unit tests. This is especially important if you are contributing a bugfix as it’s the only way you’ll know it won’t reappear.

That’s basically it, have fun!