views:

40

answers:

2

I'm using JNA and Java but I think this question affects any native-to-nonnative bridge.

I have a Java application which relies on lib1.dylib, and lib1.dylib relies on lib2.dylib.

I want to put everything inside of my .app file on Mac. I can easily put lib1.dylib inside and set java.classpath (or NativeLibrary.addSearchPath()) to tell the JVM where to find lib1.dylib. The trouble is, I don't know how to communicate that lib1.dylib's dependencies are also in the location I provided. The result is that lib1 is loaded fine, but then lib2 can't be found since it's not in the operating system's library path.

Anyone know how I can overcome this problem? I imagine it must come up plenty in big projects with large numbers of shared libraries.

+1  A: 

I've come across this problem before, and have just run into it again today. You may be able to get around it by adding the VM argument "-Djava.library.path=/path/to/other/libs", but I seem to remember Java only uses that to search for the intial library and then uses the system PATH to look for any dependencies.

A few solutions I've tried before:

1) Use System.load(absolutePath) on the dependent library before loading your library. Doesn't make your program ultra-portable though, unless you always know where that library is going to be.

2) In a case where lib1 depends on lib2, I actually used SetCurrentDirectory (Windows, not sure of the Mac equivalent) in the native code before it linked to any of the dependent libs, and that seemed to work. Again, requires knowing where the other libs are.

3) On Windows, could dump the dependent libraries in c:\windows\system32, and it finds them.

A few helpful posts on a similar topic (Windows-specific, but I think the problem is the same):

http://www.realityinteractive.com/rgrzywinski/archives/000219.html http://www.velocityreviews.com/forums/t387618-jni-library-path.html

Stew
Thank you! I'll check it out. Sorry for confusing classpath and java library path in my question. (1) or (2) may be worth the loss of portability. I'm trying to avoid (3) because it greatly complicates the installer, requires root access, risks overwriting existing old versions of libs that other apps need, etc. :-\
Yuvi Masory
After investigating these options, nothing but bad news.(1) Doesn't work with JNA. Apparently JNA doesn't care if Java has previously loaded the library, presumably because it has to do its own mapping stuff. It would work with JNI however.(2) Would require special code for different OSes, but most problematic is that there is no equivalent to dllmain in UNIX and no specific guarantee (that I know of) that the cwd switch will be executed before the dependency lookup is done.I see no alternative to (3) which I was trying to avoid with this question. Bummer.
Yuvi Masory
A: 

I've found a solution for MacOSX based on the idea in (2) from Stew:

Using Mac's JarBundler (or the Ant task of the same name) set the workingdirectory variable to $JAVAROOT and make sure your dylibs are in the Contents/Resources/Java part of the .app. If you do this the dynamic linker will find all the dependency dylibs because it will be the present directory. Java will also find the original dylib (the one that has all the dependencies) for the same reason.

Ant code:

<target name="package_mac_app" depends="package_jar, compile_native" description="bundle the runnable jar into a Mac Application -- requires JarBundler ANT Task">
    <taskdef name="jarbundler" classname="net.sourceforge.jarbundler.JarBundler"/>
    <echo message="CREATING MAC .app EXECUTABLE"/>
    <jarbundler dir="${dist}"
      name="${appname}"
      mainclass="myPackage.myMainClass"
      icon="${icon_location}"
      jvmversion="1.5+"
      infostring="${appname}"
      shortname="${appshortname}"
      bundleid="${com.mycompany.mydepartment.myprogram}"
      jar="${run_jar_location}"
      workingdirectory="$JAVAROOT">
      <javafilelist dir="${dylib_location}" files="my-lib.dylib"/>
      <javafilelist dir="${dylib_location}" files="dependent-lib.dylib"/>
    </jarbundler>

</target>
Yuvi Masory
Nice. I might try this out on one of my projects. It's pretty clean and still fits with the whole 'drag and drop installation'.
Stew