Step-by-step example of using a C dynamic library with dependencies in Xamarin.Mac or MonoMac

The quick answer

Fix up the shared library install names for all of the libraries. Specifically, use install_name_tool -change (see the man page) to set all of the non-system dependencies to match one of the following two options:

Abridged stack trace

Get more information by running the app from the command line

In this case the DllNotFoundException is misleading. The problem is not actually that libb.dylib cannot be found. We can get more information about what's really happening if we run the app from the command line:

Thanks to the MONO_LOG_LEVEL=debug environment variable, the standard output now tells us that the real reason we're getting the DllNotFoundException for libb is that the program can't find lib/liba.dylib.

Side note: if you prefer, you can set the MONO_LOG_LEVEL environment variable to debug by creating an Environment variables (aka LSEnvironment) property in the Info.plist. Then the verbose output will appear in Xamarin Studio's Application Output pad when you debug the app.

Example step 5: lib/liba.dylib is not found because there is no file at that path

We can see that both liba.dylib and libb.dylib are in the MonoBundle/ folder. And more importantly, there is no lib/ folder. This is the correct behavior.

Example step 6: The incorrect reference to lib/liba.dylib is within libb.dylib itself. It is not related to MonoMac.

We can immediately see that libb.dylib contains an incorrect reference if we check the dynamic library dependencies using otool:

$ otool -L libb.dylib
libb.dylib:
lib/libb.dylib (compatibility version 0.0.0, current version 0.0.0)
lib/liba.dylib (compatibility version 0.0.0, current version 0.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 169.3.0)

We can also confirm that attempting to load libb from a C program hits the same problem as the MonoMac app:

Example step 9: Use -install_name at compile time instead of using install_name_tool

If we pass the -install_name argument to clang, we can set the install name for liba.dylib to @loader_path/liba.dylib right in the Makefile. That way we won't have to worry about fixing it again if we later rebuild the native libraries.

Addendum: alternatives to including libraries as Native References

It is not strictly necessary to include dynamic libraries as Native References when using them in a MonoMac app. It is the easiest option when using single-file libraries. But for frameworks a better option is to mimic the layout of Objective-C apps and copy each framework folder directly into the Content directory of the app bundle. In most cases, this approach will require a few additional steps to tell the MonoMac app where to find the libraries.

Another update with more details soon.

Found a mistake?

Links

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.