The moving parts of Xamarin.Mac
When a developer hits “Run” on a Xamarin.Mac application, a large number of moving parts engage to provide the “magic” necessary to produce a final application. Understanding where the different components (compiler, class library, runtime, bindings, packaging) all fit together can be a bit overwhelming. Let’s take a somewhat simplified 10,000 foot view of the process to get a rough idea of the lay of the land.
- Run Button — Visual Studio for Mac does a quick check, realizes that the application is either not built or stale, and asks msbuild to handle it.
- msbuild — msbuild, previously xbuild, uses xml target files to describe the steps needed to process and build applications. You can see them on disk for Xamarin.Mac here: /Library/Frameworks/Mono.framework/External/xbuild/Xamarin/Mac/ and the source code here. msbuild will invoke the compiler, some resource tooing, then the packager (which we will cover later) before completing.
- csc (Compilation) — One of the first steps msbuild takes is to invoke csc, previously mcs, to compile your C# or F# code into .NET assemblies. Xamarin.Mac applications have a “normal” .NET exe entry point and reference macOS bindings just like another class library.
- Resource tooling — Depending on your project, one or more resource files will need to be processed using Apple’s tooling. For example, msbuild will invoke ibtool to process xib and storyboard files.
- mmp (Packaging) — A final Xamarin.Mac application is not simply a set of raw assemblies on disk, but a “real” macOS package. msbuild invokes mmp to consume the compiled assemblies and resources and generate a final package. Beyond creating folders and copying files to their final location, a native launcher application must be compiled. This launcher starts up the mono runtime engine, configures handlers for some error conditions, and invokes your entry point.
- Runtime — Linked into the launcher application are two different “runtime” components:
- libmono — The mono runtime engine, which JIT compiles your code into machine code and executes it, is linked into your launcher. This way mono is not required to be installed on customer machines.
- libxammac — A set of trampolines and glue code which provides the “magic” necessary to invoke objective-c and be called back.
- Class Library — All .NET applications depend upon a wealth of functionality provided by the class library. Base types such as strings and lists and fundamental functionality such as threading, tasks, and LINQ are all implemented in a set of class libraries. These are copied into your application’s “MonoBundle” folder by mmp. While their implementation is provided by mono, Xamarin.Mac stores copies to match each Target Framework:
- Modern /Library/Frameworks/Xamarin.Mac.framework/Versions/Current/lib/mono/Xamarin.Mac/
- Full /Library/Frameworks/Xamarin.Mac.framework/Versions/Current/lib/mono/4.5/
- Code Signing — One of the last steps msbuild invokes, after packaging is to invoke Apple’s code signing toolchain. This cryptographically signs your application with the keys and certificates you provide.
- Now that the application bundle is complete, msbuild will return to the IDE to launch or debug, depending on configuration.
As you can see, there are a large number of moving parts, and not all of them are provided by Xamarin.Mac directly. One can roughly divide the components into three categories:
- Components that Xamarin.Mac directly provides: mmp, msbuild tasks, the binding assembly (Xamarin.Mac.dll) and libxammac runtime library
- Tooling invoked from mono installed on system: csc, the msbuild tool itself and its “common” target files
- Components from mono that Xamarin.Mac packages internally: Class library and libmono
For a vast majority of cases all of these are implementation details and magic happening behind the curtain. However, a high level understanding can help explain what’s going on when trouble arises:
- I upgraded Xamarin.Mac but not mono and now I’m seeing different behavior in this class library invocations — Since Xamarin.Mac packages the class libraries directly, upgrading Xamarin.Mac will provide new class libraries even if you don’t upgrade your system mono.
- I upgraded everything and now I’m hitting a compile crash. I can’t downgrade as I need the new bindings provided by Xamarin.Mac — Since the compiler is provided by the system installed mono, you can downgrade it independent of your Xamarin.Mac.
- The release notes state that my runtime bug is fixed in mono 5.4 but I’ve upgraded my mono to it, recompiled, and it still is happening — Since Xamarin.Mac ships libmono internally, you’ll need a Xamarin.Mac compiled against a mono with that runtime fix.