Mac OS X application packages allow you to embed frameworks and dynamic libraries within your application, making it easy to comply with the LGPL or use a third-party library without infringing on your users' convenience. However, actually making this work isn't as straightforward as perhaps it should be...
Dynamic libraries (and frameworks, same thing) on Mac OS X include an "install_name". This is the absolute path that the library expects to reside at. When a program is linked, the install_names from any dynamic libraries it links against are copied into the program, and dyld looks for the libraries at these paths when the program is run.
That means that in order to embed a dynamic library into your application bundle, your program must contain an install_name for the library which points to a path inside the application bundle, and the easiest way to get the correct path into your program is to have the correct install_name in the library itself.
Wait, didn't I just say that the install_name is an absolute path? How can it point into the application bundle, when you don't know where the application bundle is? Fortunately there's a special token @executable_path that can appear at the beginning of an install_name that refers to the absolute path of the program loading the library.
Where does all this leave us?
If possible, you'll build your dynamic library with the appropriate install_name
$ gcc foo.c -dynamiclib -o libfoo.dylib -install_name @executable_path/../Libraries/libfoo.dylib
$ otool -L libfoo.dylib
libfoo.dylib:
@executable_path/../Libraries/libfoo.dylib (compatibility version 0.0.0, current version 0.0.0)
/usr/lib/libgcc_s.1.dylib (compatibility version 1.0.0, current version 1.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 111.0.0)
Then you'll link your application as normal, and when you put the library in Contents/Libraries everything will just work™
If building the dynamic library appropriately isn't an option (a closed-source third-party library for example), don't worry. It's the install_name copied to your program that's really important, and that can be fudged. You'll link your application with the -headerpad_max_install_names flag, then edit it with install_name_tool.
$ gcc main.c -L. -lfoo -headerpad_max_install_names
$ otool -L a.out
a.out:
/usr/local/lib/libfoo.dylib (compatibility version 0.0.0, current version 0.0.0)
/usr/lib/libgcc_s.1.dylib (compatibility version 1.0.0, current version 1.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 111.0.0)
$ install_name_tool -change /usr/local/lib/libfoo.dylib @executable_path/../Libraries/libfoo.dylib a.out
$ otool -L a.out
a.out:
@executable_path/../Libraries/libfoo.dylib (compatibility version 0.0.0, current version 0.0.0)
/usr/lib/libgcc_s.1.dylib (compatibility version 1.0.0, current version 1.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 111.0.0)
1 comment:
Hey OneSadCookie. Hello from Perth, Australia!
It Looks like you and me have come across a lot of the same problems.
I'm glad I found your article. I don't know if you are aware, but what you are doing on the command line can be accomplished through a build phase script in Xcode. This makes the build process a one click process.
I have written an article about it here: http://brockwoolf.com/how-to-use-dynamic-libraries-in-xcode-31-using-fmod
Let me know if this helps!
Cheers
Brock
Post a Comment