macoslinkercmakedyldrpath

How to set dylib search path OSX


I have a project in Xcode where I set DYLD_FALLBACK_LIBRARY_PATH in the environment variables pane to set where my application should look for libraries to link to (which works well for my purposes)

enter image description here

I am writing a CMakeLists.txt file to generate this project now and I would like to set this property from the script.

I am aware I can do this:

SET(ENV{DYLD_FALLBACK_LIBRARY_PATH} ${DYLD_FALLBACK_LIBRARY_PATH} /path/to/lib)

but when I run I get this error:

'dyld: Library not loaded ... Reason: image not found' 

when launching the application. I imagine the statement is not the same.

Does someone know how I can achieve the equivalent of what I have in Xcode from CMakeLists.txt?

I am vaguely aware of the CMake RPATH stuff but I'd rather avoid that if possible.

Thank you for your time!

Cheers!

Tom


Solution

  • I did finally get this working using a slightly different approach. This might not be the perfect solution and work in all cases, but hopefully this will help someone who is having similar problems to me.

    The dylib I was trying to link to was libSDL2-2.0.0

    If I navigate to where that file is located and run:

    otool -L libSDL2-2.0.0.dylib
    

    the top line I get in the output is this:

    /usr/local/lib/libSDL2-2.0.0.dylib
    

    if I then navigate to my built executable and run the same command, I see the same thing:

    /usr/local/lib/libSDL2-2.0.0.dylib
    

    (my executable links to SDL)

    My problem was libSDL2-2.0.0.dylib is not actually located there, it's in my project structure in a libs folder. In order to get the linker to find the lib at runtime, I had to run this command on the dylib

    install_name_tool -id "@executable_path/../path/to/lib/<lib_name>" <lib_name>
    

    where @executable_path is the location of the application to run - in my case this was in - build/debug

    The project structure:

    root/
        CMakeLists.txt
        project/
            lib/
                libSDL2-2.0.0.dylib
        build/
            debug/
                my_app
    

    This is an exact mapping for clarity:

    install_name_tool -id "@executable_path/../../project/lib/libSDL2-2.0.0.dylib" libSDL2-2.0.0.dylib
    

    If I run otool -L libSDL2-2.0.0.dylib I now see:

    @executable_path/../../sdl-test/lib/libSDL2-2.0.0.dylib
    

    in the first line of the output.

    If I now build my project again (I'm just building in Xcode), this updates my app with the new relative path to the dylib. You only have to run install_name_tool on the library, you do not also have to run it on your executable, it will be updated when you build it.

    If I run otool -L myapp I now see the same relative path to libSDL2-2.0.0.dylib

    With that, when launching the app it was able to successfully find the dylib!

    My understanding is that this is the way to achieve this on OSX and there isn't a brilliant alternative (other than messing with DYLD_FALLBACK_LIBRARY_PATH which I mentioned in my question)

    I hope this has been of some help to someone who had similar difficulties to me!

    Resources I found useful:

    http://osiris.laya.com/coding/dylib_linking.html

    https://www.fmod.org/questions/question/forum-23398/

    https://blogs.oracle.com/dipol/entry/dynamic_libraries_rpath_and_mac

    UPDATE:

    I actually found an even better way to do this using rpaths and thought I'd write up how to do this for future reference:

    In my CMakeLists.txt file I added these lines right at the end (after add_executable and target_link_libraries:

    # set @rpaths for libraries to link against
    set(CMAKE_SKIP_BUILD_RPATH  FALSE)
    set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) 
    set(CMAKE_INSTALL_RPATH "${PROJ_LIB_DIR}")
    set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
    

    (see https://cmake.org/Wiki/CMake_RPATH_handling for more info)

    where ${PROJ_LIB_DIR} is where my dylibs are located:

    set(PROJ_LIB_DIR ${CMAKE_CURRENT_LIST_DIR}/lib)
    

    I then ran:

    example:

    install_name_tool -id "@rpath/<my-dylib>.dylib" <my-dylib>.dylib
    

    actual:

    install_name_tool -id "@rpath/libSDL2-2.0.0.dylib" libSDL2-2.0.0.dylib
    

    in the directory where my dylib is located (in my case libSDL2-2.0.0.dylib)

    Now when I run cmake and then build my project, my new executable will search for the library in the location I set in the CMakeLists.txt file at runtime. @rpath will be substituted with the path specified in the CMakeLists.txt file and everything just works without explicitly having to set the @executable_path or @loader_path