Apr 11, 2011

Building 3rd party stuff for linking inside your .app wrapper / bundle

I posted this in response to a discussion thread on the Homebrew mailing list and thought I would post it to my blog for SEO and for myself when I need to look into this more at a later date. - sean

I spent a little time trying to figure relative linking out with [Home]brew as apposed to linking a hardcoded '/use/local/...'. I have not spent much time at this yet, but when building libraries with Xcode for including in my OS X App bundle's '@executable_path' is used instead of a full hard system path. You can get info about your compiled libraries and dependencies with 'otool -L':
$ otool -L libboost_filesystem-mt.dylib libboost_filesystem-mt.dylib:
    @executable_path/../Frameworks/libboost_filesystem-mt.dylib (compatibility version 0.0.0, current version 0.0.0)
    @executable_path/../Frameworks/libboost_system-mt.dylib (compatibility version 0.0.0, current version 0.0.0)
    /usr/lib/libstdc++.6.dylib (compatibility version 7.0.0, current version 7.4.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.1.4)
You can change your dylib's dependency path's with 'install_name_tool'. Given a binary 'MyBinary':
$ otool -L MyBinary MyBinary:
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 111.1.4)
    /usr/local/lib/myLib.dylib (compatibility version 1.0.0, current version 1.0.0) 
$ install_name_tool -change /usr/local/lib/myLib.dylib @executable_path/../Frameworks/myLib.dylib MyBinary
Whenever we recompile however we'll need to re-run install_name_tool. It turns out the root issue is how myLib.dylib was built... To fix this update the Makefile that generated myLib.dylib, and change it, adding the -install_name flag to the libtool line which might then look something like this:
libtool -dynamic -flat_namespace -install_name @executable_path/../Frameworks/myLib.dylib \ -lSystem -compatibility_version 1.0 -current_version 1.0.0 \ -o myLib.dylib -undefined suppress $(OBJS)
I'm still interested in a fairly generic way to shoehorn this into common configure scripts, but I haven't looked into it much at this point; it's just on my to-do list. I don't have an easily available comprehensive list of all the options I used, but these might assist others a bit:
CFLAGS="-arch x86_64 -arch i386" [-arch ppc] <- If you are building for PPC (or even can)
LDFLAGS="-arch x86_64 -arch i386" [-arch ppc]
building for backwards compatibility to an older SDK:
CFLAGS="-isysroot /Developer/SDKs/MacOSX10.4u.sdk -arch x86_64 -arch i386"
You may need to add this flag to configure (when building fat binaries):
./configure --disable-dependency-tracking
You can also use the 'file' command or 'lipo -info' to check if 'fat' or 'slim' and or what architectures it's build for:
$ file libboost_filesystem-mt.dylib     libboost_filesystem-mt.dylib: Mach-O universal binary with 2 architectures
    libboost_filesystem-mt.dylib (for architecture x86_64): Mach-O 64-bit executable x86_64
    libboost_filesystem-mt.dylib (for architecture i386): Mach-O executable i386
[ update 2011.04.11 @ 19:15 PT ]
Some more related info I found... "@rpath" may be better than "@executable_path"
http://www.dribin.org/dave/blog/archives/2009/11/15/rpath/