7

Background: My MonoMac app uses a custom build of sqlite3.0.8.6.dylib.

I need the exact steps to have MyApp.app use this dylib.

Here are some steps I took:

  1. Copied the dylib to MyApp.app/Contents/SharedSupport. (Related question: is this the preferred location for 3rd party dylibs or is MyApp.app/Contents/Frameworks preferred?)

  2. Changed the installed name for the library so that it matches its new location.

    MyApp.app/Contents/SharedSupport> otool -L libsqlite3.0.8.6.dylib 
    libsqlite3.0.8.6.dylib:
        @executable_path/../SharedSupport/libsqlite3.0.8.6.dylib (compatibility version 9.0.0, current version 9.6.0)
        /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 159.1.0)
    

Problems

  1. MyApp.app/Contents/MacOS/MyApp does not reference the dylib directly, so I can't use install_name_tool to point to the new library location. I believe that the library is referenced by System.Data.Sqlite.dll.
  2. I thought of overriding DYLD_FALLBACK_LIBRARY_PATH in the launcher script but MonoMac now uses a binary launcher (MyApp.app/Contents/MacOS/MyApp), not a script, so I'm out of luck there.

Would the MonoMac gods please help with what must be a simple solution? I've spent a couple of months, on and off, trying to get this to work.

And please provide exact steps - this problem is all about the details.

bright
  • 4,700
  • 1
  • 34
  • 59

3 Answers3

4

Have a look at my answer to this question: Setting path of the Native Library for DllImport on Mono for Mac

The binary launcher comes from monodevelop/main/build/MacOSX/monostub.m.

You can use either MyApp.app/Contents/Frameworks or some other path, the important part is not to use any path names in your [DllImport] but instead add the <dllmap> using @executable_path to your app.config like I explained in that other answer.

There's a also a link to a test app on github in there.

Detailed Instructions

  1. Pick a path inside the MyApp.app to install your native dll, for instance Contents/SharedSupport/sqlite3.0.8.6.dylib.

  2. Compute the relative path from the directory where the managed assembly is located to the native .dll and prepend @executable_path to it.

    For instance, if your managed assembly is in Contents/MonoBundle/MyApp.exe and the native dll in Contents/SharedSupport/sqlite3.0.8.6.dylib, then it's @executable_path/../SharedSupport/sqlite3.0.8.6.dylib.

  3. Change the installed name of the library to this relative path using install_name_tool.

  4. Add a new MyApp.exe.config file to your project, containing

    <configuration>
      <dllmap dll="sqlite" target="@executable_path/../SharedSupport/sqlite3.0.8.6.dylib" />
    </configuration>
    

    Use the path that you computed in step 2. for the target field. Right-click the file in MonoDevelop, select "Quick Properties" from the context menu and enable "Copy to Output directory". This will copy the file into the Contents/MonoBundle directory, so it sits right next to your MyApp.exe.

  5. Use [DllImport ("sqlite")] to reference this in your code.

When another library references it

When another library, for instance Mono.Data.Sqlite.dll references it, it get a little bit more complicated.

Use the same steps as above, but you need to figure out which name that other library is using in its [DllImport] to reference the native library and put that into the <dllimport dll="..." />. You can either look for the [DllImport] statements in the source code or run monodis on the assembly and search for pinvokeimpl, for instance:

// method line 679
.method assembly static hidebysig pinvokeimpl ("sqlite3" as "sqlite3_create_function" cdecl )
       default int32 sqlite3_create_function (native int db, unsigned int8[] strName, int32 nArgs, int32 nType, native int pvUser, class Mono.Data.Sqlite.SQLiteCallback func, class Mono.Data.Sqlite.SQLiteCallback fstep, class Mono.Data.Sqlite.SQLiteFinalCallback ffinal)  cil managed preservesig 
{
    // Method begins at RVA 0x0
} // end of method UnsafeNativeMethods::sqlite3_create_function

So Mono.Data.Sqlite.dll is using "sqlite3" to reference the native dll, so your MyApp.exe.config file will look like this:

    <configuration>
      <dllmap dll="sqlite3" target="@executable_path/../SharedSupport/sqlite3.0.8.6.dylib" />
    </configuration>
Community
  • 1
  • 1
Martin Baulig
  • 3,010
  • 1
  • 17
  • 22
  • Martin, thanks. However, that answer does not provide exact steps. It would save a lot of time if you could really list the precise steps to add a dylib to an .app and to have the combination work. What are the precise paths relative to the app bundle? Exactly where should the config file go, how to use otool and install_name_tool to modify the dylib and so on. As mentioned in the question, we've spent a couple of months trying to get a working sequence of steps, so there must be something basic we're missing. Even better might be a shell script to automate the process. – bright Feb 01 '13 at 13:01
  • I have started a bounty for this as added incentive. – bright Feb 01 '13 at 13:09
  • Mmmh, a bounty ;-). Well, I'm too tired anyways to do this today and if you could wait a couple of days, then I could probably also think about something "real". Instead of writing some shell script for this, it may make more sense to just add this functionality to MonoDevelop. Something like "Add native reference" where you simply select some .dylib and MonoDevelop will automatically add it to the .app. – Martin Baulig Feb 01 '13 at 15:54
  • This needs to work with command line builds of generated .csproj files, hence the script. – bright Feb 02 '13 at 08:49
  • Unfortunately, I don't have much time this week to write and test a script, but hopefully the detailed instructions that I just added to my answer will help you. – Martin Baulig Feb 05 '13 at 14:36
  • Thanks. I haven't tested your technique yet, but it certainly looks detailed enough to warrant the bounty :) Thanks particularly for explaining how to use monodis to determine what name a library is using for a dylib. – bright Feb 08 '13 at 09:50
  • @bright Did this answer work for you? I'm trying to do the same thing, and my dllmap entry doesn't seem to be having any effect... – TheNextman Aug 28 '13 at 17:40
  • @MartinBaulig you seem to be very knowledgeable on this subject, can you please answer my related question here: http://stackoverflow.com/questions/19522244/bundling-dylib-files-with-mono-executable – Ilya Suzdalnitski Oct 23 '13 at 05:48
  • my dllmap entry does not seem to have effect either - has anyone had luck with this? I am using Xamarin.Mac – tofutim Mar 25 '14 at 05:24
  • In this case the dylib (set to Content, Copy if newer) ends up in the Resources folder and I have used but it still refers to the default one. How can I test that the dllmap is pointing to the right place? – tofutim Mar 25 '14 at 05:34
2

Be careful if you're using mono. @executable_path will return the path to the mono binary, instead of the actual .EXE executable. This should work correctly with Xamarin though.

Ilya Suzdalnitski
  • 52,598
  • 51
  • 134
  • 168
1

You can try manually loading the sqlite dylib.

[DllImport ("/usr/lib/libSystem.dylib")]
public static extern IntPtr dlopen (string path, int mode);
IntPtr p = dlopen("libsqlite3.0.8.6.dylib", 0);

Of course correctly resolving the path to your dylib.

Eun
  • 4,146
  • 5
  • 30
  • 51
TrustMe
  • 261
  • 1
  • 3