This is an unofficial port of the mono runtime to the switch homebrew toolchain. It can run dotnet 9.0 applications using the mono interpreter by loading the dll or exe files directly on console. It can also build .NET assemblies as static libraries using mono AOT.
explorer_demo.mp4
This is a clip of a C# file explorer with a GUI powered by SDL and imgui running on console. More clips: pad_demo and guess_number
If you're curious about the process of porting mono to a weird platform I documented some of the challenges I faced and my workarounds in this write-up.
While a few things do work this at the proof-of-concept stage with minimal testing, you probably don't want to use this to actually make homebrew. While I would love to continue working on this project, it requires too much effort for a single person. For the time being I don't plan active development.
- Common BCL classes such as
List,StringBuilderand so on - P/Invoke, but only with libraries that were statically linked beforehand
- Threads and async
- Most of filesystem APIs
- Sockets and http-only support for
HttpClient - .NET wrappers for SDL2 and dear imgui which are included as static libraries
- Unit tests with XHarness (some dotnet repo tests are even passing!)
Overall both the interpreter and AOT seem rather stable and can run complex programs or games.
- HTTPS and most of
System.Securitydoesn't work because we have no openssl port on switch - Arbitrary P/Invoke doesn't work due to the lack of dynamic linking, all native function entrypoints must be defined beforehand and statically linked
- Any other OS-dependant API that was not mentioned previously will likely not work because it was not explicitly implemented. Examples are
Console.Read,Console.Clear,Process,WinForms, Unity and many more.
The sd_files.zip release contains the mono interpreter, all the dotnet runtime libraries and the file .dll/.exe file association for hbmenu allowing you to copy .net dll programs to your switch folder and running them directly.
The following pre-built examples are included:
- aot_example.nro is a fully self-contained AOT-complied C# application. It generates a random number and asks you to guess it
- pad_input.dll is a C# program that reads the controls with the native libnx api
- example.dll is a C# program that shows most of the APIs that are currently implemented
- explorer_demo.dll is a C# program that shows a file explorer-like GUI using imgui and SDL2. This will also work on windows and linux with no code changes if you use my cimgui fork
- guess_number.exe is the source from aot_example built with plain
csc.exeon a windows vm to show off loading exe files as well. I'm not aware of any way to build this on linux so there's no build script for it.
If you want to try making a switch homebrew in C# you can follow the examples in the managed/ folder, you can build them using the normal dotnet 9.0 sdk for linux or windows and then copy the final dll files to your console.
You will probably want to setup one of the logging options in config.ini to catch unhandled exceptions.
DllImport/PInvoke for functions that are not statically defined in dl_shim.c requires to use a custom build of the interpreter, in such cases you should fork the native/ folder and add your external libraries to a dl_shim file, then build the NRO. For this you will need the mono static libraries distributed as part of the SDK release.
AOT requires additional steps
Important
Reminder for when you will hit things that do not work. this is an unsupported port, do NOT open issues on the real dotnet/runtime.. If you want to help document what is broken you can open an issue in this repo, but as of now there is no support.
Mono gets statically linked to our interpreter and aot binaries, this means before you're able to build them you need your own local dotnet_runtime build. The build steps are a little convoluted, I tried to split everything to its own bash script so it should be easier to understand what's needed for what.
On a default ubuntu image you will need to manually install devkita64, CMake 3.20 or higher, libclang and dotnet-sdk-9.0. On other distros refer to the documentation on the dotnet/runtime repo. For your convenience I prepared a docker container which already has all the required components. You can enter the docker environment using:
docker build . -t monobuild
docker run --rm -it -v $(pwd):/mono-nx monobuild
Now, if you feel lazy and bold at the same time you can try the prebuilt sdk release. This is produced by the gather_sdk.sh script by grabbing only the final headers and static libraries needed for building the nros. Extract it in the repo root and lets you skip building the dotnet runtime repo. However I don't think it will work across toolchain upgrades, this means you must use the same docker container that was used when the SDK was built.
If you get unexplainable issues, try to build the runtime yourself.
- Ensure the DEVKITPRO env var is set
- Run
source env.shto define the environment variables needed. This is done automatically in the docker image - Build libicu with
cd icu && ./build_icu.sh, this is a dependency for dotnet_runtime - Clone the
libnxbranch from my fork of the dotnet/runtime repogit clone --depth=1 -b libnx https://github.com/exelix11/dotnet_runtime.git - Build mono with
./build_mono.sh- If you're not using the docker container, fix all the inevitable dependency errors that appear and try again until it works
- You can clean the state of the dotnet build system using
cd dotnet_runtime && ./buld.sh --clean
If you want to modify mono or the runtime you should check out the build steps in build_mono.sh and only build the subsets you're working on.
You'll most likely want to start working with the interpreter in native/interpreter, build it with make.
You can build the C# examples in managed with ./managed_build.sh
For AOT cd native/aot then in order, ./build_aot.sh and finally make.
In case of build errors check out the github actions scripts which should be reproducible.
The sd card zip in releases is built with copy_sd_files.sh