The Very Basic Guide to Contribute to Mono BCL
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:
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:
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:
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:
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:
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:
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!