[Mono-osx] embedding mono apps in a cocoa app

Duane Wandless duane at wandless.net
Wed Jun 23 08:07:54 EDT 2010


So you want to embed a mono app within an existing obj-c app.  I have posted
about this before but thought it was a good time to repost.  Here are the
steps I do to make this work using Mono 2.6.1 and 2.6.4.  Or at least the
steps I am remembering now.  You can use the MonoDevelop soft debugger
against the C# code.  This is of course very useful.  But that is a topic
for a different day.

In your main.m add this line:

InitMono(argc, (char**)argv);

Create a new library project in xcode.  Change the project settings like
this:
OTHER_LDFLAGS = -pthread
-L/Library/Frameworks/Mono.framework/Versions/Current/lib -lmono -lpthread
-lm -lgthread-2.0 -lglib-2.0 -lintl -rpath at loader_path/../Libraries
LIBRARY_SEARCH_PATHS = Mono
OTHER_CFLAGS = -D_XOPEN_SOURCE -DPLATFORM_MACOSX -DUSE_MMAP -DUSE_MUNMAP
-DGC_MACOSX_THREADS -DGetCurrentProcess=MonoGetCurrentProcess
-DCreateEvent=MonoCreateEvent -DGetCurrentThread=MonoGetCurrentThread
-I/Library/Frameworks/Mono.framework/Versions/Current/lib/glib-2.0/include
-I/Library/Frameworks/Mono.framework/Versions/Current/include/glib-2.0
-D_THREAD_SAFE -D_REENTRANT
-I/Library/Frameworks/Mono.framework/Headers/mono-1.0
LD_DYLIB_INSTALL_NAME = @loader_path/../Libraries/InitMono.dylib

Add a .m file to the project, initmono.m.

#import <Cocoa/Cocoa.h>
#include <mono/jit/jit.h>
#include <mono/metadata/assembly.h>
#include <mono/metadata/mono-config.h>
#include <mono/metadata/mono-debug.h>
#include <mono/utils/mono-logger.h>

extern void mono_mkbundle_init();

void InitMono(int argc, char *argv[])
{
MonoDomain *domain;
NSString *libraryPath = [[[NSBundle mainBundle] bundlePath]
stringByAppendingPathComponent:@"Contents/Libraries/"];
NSString *sampleAssemblyPath = [libraryPath stringByAppendingPathComponent:@
"MacMonoClient.exe"];

        // Create a DLL mapping file
        // I do this at runtime in case the user relocates the app so that
mono can still find the dylib
NSString* monoconfig = @"<configuration><dllmap dll=\"mobjc-glue.dylib\"
target=\"";
monoconfig = [monoconfig stringByAppendingString:[libraryPath
stringByAppendingPathComponent:@"a.dylib"]];
monoconfig = [monoconfig stringByAppendingString:@"\" /></configuration>"];
[monoconfig writeToFile:[libraryPath
stringByAppendingPathComponent:@"monoconfig2"]
atomically:YES encoding:NSASCIIStringEncoding error:NULL];

NSString* configFile = [libraryPath stringByAppendingPathComponent:@
"monoconfig2"];
mono_config_parse ([configFile UTF8String]);

const gchar *pix_debug;
pix_debug = g_getenv ("EMBEDDED_MONO_DEBUG");
if (pix_debug != NULL) {
mono_debug_init (MONO_DEBUG_FORMAT_MONO);
}
const gchar *mono_trace_level;
mono_trace_level = g_getenv ("EMBEDDED_MONO_TRACE_LEVEL");
if (mono_trace_level != NULL)
mono_trace_set_level_string(mono_trace_level);

        // this is currently needed because in 2.6.1 fully embedding mono is
not working
NSString *mPath = [[[NSBundle mainBundle] bundlePath]
stringByAppendingPathComponent:@"Contents/Libraries/lib"];
mono_assembly_setrootdir([mPath UTF8String]);

domain = mono_jit_init ([sampleAssemblyPath UTF8String]);
MonoAssembly *monoAssembly = mono_domain_assembly_open(domain,
[sampleAssemblyPath UTF8String]);

mono_jit_exec (domain, monoAssembly, 1, argv);
}

Add InitMono.dylib to your existing xcode project.

Compile your C# code as an EXE, the name must match what is in initmono.m.
 At one point there were issues using a DLL, those may have been fixed but I
have not checked.  But using an EXE is better in my opinion.  If you are
using mobjc/mcocoa or monobjc you need to initialize the bindings.  The C#
main is the perfect place to do that.

Now if you are not fully embedding mono, meaning you are requiring your
users to install mono then you do not need these next steps.  These next
steps fully embed mono so that you only ship what you need.  My total app is
around 25 MB.  Now your set of DLLs and mono dylibs may be different
depending on what your app uses.  I use mkbundle to figure what to include.

Again I have not checked recently, but there were issues just using the
result of mkbundle.  But in theory instead of creating the directory
structure (see below) you can compile into InitMono.dylib the result of
mkbundle -o host.c.  And not need to add all of the lib*.dylibs, dlls, etc.
 That is a topic for a different day.

Now in your app you will need to have this directory structure:
Contents\Libraries
   InitMono.dylib
   MacMonoClient.exe
   MacMonoClient.exe.mdb
   a.dylib
   < any other DLLs your EXE needs >
   monoconfig2
   libmono.0.dylib
   libglib-2.0.0.dylib
   libintl.8.0.2.dylib
   libgthread-2.0.0.dylib
   System.Configuration.dll
   System.dll
   System.Core.dll
   System.Drawing.dll
   System.Drawing.dll.config
   Mono.Security.dll
   System.Xml.Linq.dll
   System.Xml.dll
   Mono.Posix.dll < needed for 2.6.4 >
   System.Security.dll < needed for 2.6.4 >

Contents\Libraries\lib\mono\2.0\
   mscorlib.dll                     < see mono_assembly_setrootdir >



Now do not think it is just that easy.  All of the mono lib*.dylibs have to
be otool-ed.  As does InitMono.dylib.  So
I have a script in the InitMono xcode project that does this:
install_name_tool -change
/Library/Frameworks/Mono.framework/Versions/2.6.4/lib/libmono.0.dylib
@loader_path/../Libraries/libmono.0.dylib
$TARGET_BUILD_DIR/$PRODUCT_NAME.dylib
install_name_tool -change
/Library/Frameworks/Mono.framework/Versions/2.6.4/lib/libgthread-2.0.0.dylib
@loader_path/../Libraries/libgthread-2.0.0.dylib
$TARGET_BUILD_DIR/$PRODUCT_NAME.dylib
install_name_tool -change
/Library/Frameworks/Mono.framework/Versions/2.6.4/lib/libglib-2.0.0.dylib
@loader_path/../Libraries/libglib-2.0.0.dylib
$TARGET_BUILD_DIR/$PRODUCT_NAME.dylib
install_name_tool -change
/Library/Frameworks/Mono.framework/Versions/2.6.4/lib/libintl.8.0.2.dylib
@loader_path/../Libraries/libintl.8.0.2.dylib
$TARGET_BUILD_DIR/$PRODUCT_NAME.dylib

And then for all of the lib*.dylibs you need to run these commands:
install_name_tool -id @loader_path/../Libraries/libmono.0.dylib
libmono.0.dylib
install_name_tool -id @loader_path/../Libraries/libgthread-2.0.0.dylib
libgthread-2.0.0.dylib
install_name_tool -id @loader_path/../Libraries/libglib-2.0.0.dylib
libglib-2.0.0.dylib
install_name_tool -id @loader_path/../Libraries/libintl.8.0.2.dylib
libintl.8.0.2.dylib

install_name_tool -change
/Library/Frameworks/Mono.framework/Versions/2.6.4/lib/libmono.0.dylib
@loader_path/../Libraries/libmono.0.dylib libintl.8.0.2.dylib
install_name_tool -change
/Library/Frameworks/Mono.framework/Versions/2.6.4/lib/libmono.0.dylib
@loader_path/../Libraries/libmono.0.dylib libgthread-2.0.0.dylib
install_name_tool -change
/Library/Frameworks/Mono.framework/Versions/2.6.4/lib/libmono.0.dylib
@loader_path/../Libraries/libmono.0.dylib libglib-2.0.0.dylib


install_name_tool -change
/Library/Frameworks/Mono.framework/Versions/2.6.4/lib/libintl.8.0.2.dylib
@loader_path/../Libraries/libintl.8.0.2.dylib libmono.0.dylib
install_name_tool -change
/Library/Frameworks/Mono.framework/Versions/2.6.4/lib/libintl.8.0.2.dylib
@loader_path/../Libraries/libintl.8.0.2.dylib  libgthread-2.0.0.dylib
install_name_tool -change
/Library/Frameworks/Mono.framework/Versions/2.6.4/lib/libintl.8.0.2.dylib
@loader_path/../Libraries/libintl.8.0.2.dylib libglib-2.0.0.dylib

install_name_tool -change
/Library/Frameworks/Mono.framework/Versions/2.6.4/lib/libgthread-2.0.0.dylib
@loader_path/../Libraries/libgthread-2.0.0.dylib libmono.0.dylib
install_name_tool -change
/Library/Frameworks/Mono.framework/Versions/2.6.4/lib/libgthread-2.0.0.dylib
@loader_path/../Libraries/libgthread-2.0.0.dylib  libintl.8.0.2.dylib
install_name_tool -change
/Library/Frameworks/Mono.framework/Versions/2.6.4/lib/libgthread-2.0.0.dylib
@loader_path/../Libraries/libgthread-2.0.0.dylib libglib-2.0.0.dylib

install_name_tool -change
/Library/Frameworks/Mono.framework/Versions/2.6.4/lib/libglib-2.0.0.dylib
@loader_path/../Libraries/libglib-2.0.0.dylib libmono.0.dylib
install_name_tool -change
/Library/Frameworks/Mono.framework/Versions/2.6.4/lib/libglib-2.0.0.dylib
@loader_path/../Libraries/libglib-2.0.0.dylib  libintl.8.0.2.dylib
install_name_tool -change
/Library/Frameworks/Mono.framework/Versions/2.6.4/lib/libglib-2.0.0.dylib
@loader_path/../Libraries/libglib-2.0.0.dylib libgthread-2.0.0.dylib

Hopefully I have not forgotten anything.  Best of luck.
Duane
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.ximian.com/pipermail/mono-osx/attachments/20100623/91e75694/attachment-0001.html 


More information about the Mono-osx mailing list