Search code examples
objective-ccxcodexcode4

How to properly wrap a C library in a Cocoa application


I want to include the GNU Scientific Library (GSL) in my Cocoa app so that the user needs not installing it locally first. Like most GNU packages, it's the standard configure / make / make install routine. But this won't work:

./configure --prefix ~/libgsl
make
make install

Since the prefix is local to my computer. And neither is this:

./configure --prefix (path to build folder)/libgsl
make
make install

What I want is essentially the GSL being contained entirely in my application, and I can call its functions without the users downloading anything else.

I'm rather new to Xcode 4 and the build system for Clang/GCC, having coming over from .NET. Any help is much appreciated.


Solution

  • Assuming there is not a framework-style build of the library, the way this is typically done when bundling with 3rd party libraries is to build the package as normal, install it in /usr/local, and configure your project to include and link from there. Building is the easy part though.

    The tricky part is bundling up the .app correctly. You need to add a custom build stage (after the others) which first copies all the dependent .dylib files into your app bundle's Frameworks folder (using the environment variables to help; see Xcode docs). Then you need to use install_name_tool to get the app binary to look in the framework dir (as the embedded soname still thinks it is in /usr/local). This part is very fiddly and not well documented.

    I've just extracted this from a working project where I use GSL. Just add this as an extra build phase in your XCode project as a Custom Script:

    # Framework folder for Example.app
    FRAMEWORKS_DIR=${TARGET_BUILD_DIR}/Example.app/Contents/Frameworks
    
    # Create path if it doesn't exsit
    mkdir -p ${FRAMEWORKS_DIR}
    
    # Find the original linked path for libgsl
    GSLLIB=`otool -L ${TARGET_BUILD_DIR}/Example.app/Contents/MacOS/Example | grep libgsl | cut -d" " -f1`
    GSLPATH=`dirname $GSLLIB`
    
    # Copy the dylibs into your app
    cp /usr/local/lib/lib{gsl,gslcblas}.0.dylib ${FRAMEWORKS_DIR}
    
    # Update embedded paths
    install_name_tool \
        -change ${GSLPATH}/libgsl.0.dylib \
        @executable_path/../Frameworks/libgsl.0.dylib \
        ${TARGET_BUILD_DIR}/Example.app/Contents/MacOS/Example
    

    This should work with a simple substitution of your app name.

    This is basically the same as what you need to do to build a standalone Qt app, so the docs here are very relevant:

    It is worth reading up on bundles, frameworks and packaging. For example:

    This post is also relevant:

    Note that GSL is published under the GPL, so your app would need to be similarly published in order to respect the license. Shipping the source is necessary, but not sufficient for compliance.