How to set dylib search path OSX - macos

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)
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

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

Related

How to create a MacOS app bundle with cmake

This can be considered as a follow-up to CMake MacOS X bundle with BundleUtiliies for Qt application
I want to create a MACOS bundle on CI which can be used by users for an open source project.
What I have:
Main executable
Updater executable
icon file
helper script calling updater then main
data files in a folder (translations etc, some generated at build time)
plugin shared libs
What I've done so far:
add MACOSX_BUNDLE to the executable
add icon to its sources and to RESOURCE property
set MACOSX_BUNDLE_* properties
install everything in a cross-platform way (regular install(TARGETS calls and install(FILES for the resources)
But now I'm stuck on how to get those into the bundle w/o to much manual work.
From the linked question I got something like this:
set(APPS "\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}.app")
set(LIBS )
set(DIRS "${CMAKE_BINARY_DIR}")
# Path used for searching by FIND_XXX(), with appropriate suffixes added
if(CMAKE_PREFIX_PATH)
foreach(dir ${CMAKE_PREFIX_PATH})
list(APPEND DIRS "${dir}/bin" "${dir}/lib")
endforeach()
endif()
install(CODE "include(BundleUtilities)
fixup_bundle(\"${APPS}\" \"${LIBS}\" \"${DIRS}\")")
But:
Why do I need to pass the *.app path manually? CMake does already know it, doesn't it?
LIBS should contain my plugins, shouldn't it? But what? Paths? Target names?
DIRS is also a mystery to me. No documentation even in CMake 3.12 (I'm still using 2.8.12 though :( )
How to add my generated and regular data files? Probably same or similar to the icon? But what about the generated ones?
Help, pointers to examples, full CMakeLists doing that etc. very welcome.
Note: I'm cross-compiling from linux on the CI and NOT using Qt so e.g. macdeployqt or so is out of question.
Just got stuck on the same issue and google brought me here.
This worked for me:
set(CUR_TARGET myappname)
add_executable(${CUR_TARGET} MACOSX_BUNDLE ${MY_SRC})
set_target_properties(${CUR_TARGET} PROPERTIES
BUNDLE True
MACOSX_BUNDLE_GUI_IDENTIFIER my.domain.style.identifier.${CUR_TARGET}
MACOSX_BUNDLE_BUNDLE_NAME ${CUR_TARGET}
MACOSX_BUNDLE_BUNDLE_VERSION "0.1"
MACOSX_BUNDLE_SHORT_VERSION_STRING "0.1"
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/cmake/customtemplate.plist.in
)
The file customtemplate.plist.in is based on the cmake template from the cmake dir subfolder at your_cmake_install_dir/share/cmake/Modules/MacOSXBundleInfo.plist.in

How to package several dylibs as a single OS-X Framework using Xcode

I am trying to package a set of google boost libraries together as an OS-X private framework, and fail miserably.
I succeeded in using boosts build-tools on the Mac (b2, bjam) to build both static lib (.a) and dynamic lib (.dylib) versions of the boost libraries I need.
I created in Xcode a framework target (configured as a private framework). I linked it against the 7 dylibs I need, and I managed to copy the Headers into place, and copy the actual dylibs into the framework's executables directory, so that they will be brought together with the new "boost" framework.
However, the "top" dynamic library of the framework - (named "boost" on diet) does not export any of the symbols in those 7 libraries.
So I can build my framework - but not use it.
I found an attempt to automatically (using script) create an iOS "framework" (with internal static lib) from boost, but this script is old and doesn't work on later boost versions, and also - it is not an Xcode project, just a script.
I tried to find how to re-export the symbols of linked .dylibs through my "boost" dynamic lib, but could not understand how.
Ideas anyone?
I'm really frustrated.
This is complicated. You can try, if this build script brings you closer to a solution:
https://gist.github.com/JanX2/80721c30192e64ed124e
Well, not without sweat and tears, I finally got it to work, and here are the tricks involved.
After doing all the things I mentioned in the original question, you must also:
In the build-settings, find "Re-Exported Library Names", and add the dylibs, named like thus: boost_iostreams boost_locale boost_regex boost_signals boost_system boost_thread boost_filesystem (this took a day - why you need to drop the "lib" and the ".dylib" and what it means to re-export a dylib).
In the build-phases, add a run-script phase before Compilation, (you can name it Run Script - Fix boost dylibs, and add the following script (might need changes depending where you placed your boost build dir)
#!/bin/bash
src_boost_dylib_dir=$SRCROOT/../../../boost_1_59_0_lib/dylib
dest_boost_dylib_dir=$CONFIGURATION_BUILD_DIR/$EXECUTABLE_FOLDER_PATH
boost_dylibs=( libboost_filesystem.dylib libboost_iostreams.dylib libboost_locale.dylib libboost_regex.dylib libboost_signals.dylib libboost_system.dylib libboost_thread.dylib)
for bdl in "${boost_dylibs[#]}"; do
echo Fixing boost dylib:$bdl
# at runtime, 'boost' loads its re-exported dylibs for other clients (such as AP) linked to boost. To find them, it must look in the same directory where boost is.
install_name_tool -id #loader_path/$bdl $src_boost_dylib_dir/$bdl
# make all dependent boost dylibs also relative to #loader_path, so that they can find each other in runtime.
dependencies=`otool -L $src_boost_dylib_dir/$bdl | grep "^\slibboost_" | awk -F' ' '{ print $1 }'`
for dependency_dylib in "${dependencies[#]}"; do
if [ "${dependency_dylib}" == "" ]; then
continue
fi
echo Dependency:$dependency_dylib
install_name_tool -change $dependency_dylib #loader_path/$dependency_dylib $src_boost_dylib_dir/$bdl
done
done
As in the Question, add a copy-files build-phase, after Linkage phase, to copy the dylibs into the "Executables" directory, so they reside with the built product (boost dylib).
After that phase, copy another run-script phase, called Run Script - Create symlink to reexported dylibs. Add the following script:
# Create symlinks for all re-exported dylibs, at the framework's top level. They must be also relative to the framework directory.
cd $CONFIGURATION_BUILD_DIR/$WRAPPER_NAME
ln -Ffsv Versions/Current/libboost_*.dylib .
You should be done.

Tutorial on embedding libclang in a Cocoa app

Does anybody have a good tutorial handy on embedding libclang in a Cocoa app?
In particular I get issues with the rpath at launch saying the dylib couldn't be found.
Error was the lovely
dyld: Library not loaded: #rpath/libclang.dylib
What this error is sadly trying to tell you is that lib clang thinks its #rpath is at the root of your app bundle, but it's not.
I figured this out the hard way just the other day.
It's hard.
You have to do some crazy stuff with libclang.a using Build Phase settings.
In your target's Build Phases, you will need to add a few Run Script Phases.
One will be this:
echo "warning: OTOOL BEGIN1";
pwd;
otool -L ${SRCROOT}/Clangwrap/ClangAndLLVM/lib/libclang.dylib;
echo "Warning: OTOOL END1";
This just does a few things.
First it nicely sandwiches things with a log to see what happened.
The second is it really is just confirming the path to your lib once it's copied. (and yes, you should be copying it into your project to keep things sane.)
otool does this.
SRCROOT is the root of your project, then anything after that is your relative path within you project's folder in Finder to the lib you are going to use.
Again, this just confirms it is where you think it is.
Ok, next step. This is the real doozy.
echo "BEGIN install_name_tool";
install_name_tool -id #executable_path/../Frameworks/libclang.dylib ${SRCROOT}/Clangwrap/ClangAndLLVM/lib/libclang.dylib;
echo "END install_name_tool";
This runs the obscure command line tool install_name_tool and that is used to set the path where the lib thinks it is located within your app's bundle. Without doing this, the lib will think it is in some other path. You will be setting this path to what you intend it to be. In a Mac app, I used the Frameworks folder inside the bundle so I set it as such. The first argument to install_name_tool is the relative path in your app bundle where the lib is going to be. The second argument is where the lib is currently in your project so that install_name_tool can set its executable path.
This literally modifies a bit of the lib so it is loadable. libs must know their own load path.
Note the first step is only optional for your own sanity.
The second step is required, and both should occur before the Compile Sources Build Phase.
Click and drag to move them up.
Now the final step. Add a new Copy Files Phase and this will remain last in your build phases.
Set the destination to the same as the first arg of install_name_tool so you know your relative path is set and that's where you're going to put the lib.
I used Frameworks.
Now add the files to this Build Phase, for iPhone, add libclang.a (I don't think you can use libclang.dylib on iOS)
Note, you saw me use libclang.dylib and that's because I did this on a Mac app project. It should be the same process for just about anything.
this is not an easy or discoverable process, I pieced it together from several blogs and docs.

How to set dyld_library_path in Xcode

I am new to Xcode and Mac environment. I am using some dynamic and static libraries like boost, Clucene, etc. I have all the libraries under
MyApp.app/Contents/Resources
I want to set this path as the app's dyld_library_path. I tried editing XXX.plist file like
DYLD_LIBRARY_PATH /mypath/xxx
and setting the environment variable and argument in Xcode Nothing work.
but if I run a shell script like below without double clicking the app in my .dmg it works
#!/bin/bash
clear
cd /Volumes/xxx/myapp.app/Contents/MacOS
export DYLD_LIBRARY_PATH="/Volumes/xxx/myapp.app/Contents/Resources"
./myapp
I am sure this is not the proper way to do this. Is there proper way to set dyld_library_path every time I execute my app?
EDIT:
It also works if u mannualy copy all ur library to clients /usr/lib path... i guess this is also not a proper way to do it.
Setting DYLD_LIBRARY_PATH isn't the best way to solve this problem. It's working around the fact that you've misinformed dyld as to where to find your libraries.
If you run otool -L MyApp.app/Contents/MacOS/MyApp you'll see the paths to the libraries that MyApp wants to load. If any library isn't found at the specified path then dyld will look for the library in the locations specified by DYLD_FALLBACK_LIBRARY_PATH. Setting DYLD_LIBRARY_PATH causes dyld to look for the library in the given locations ahead of the path that the otool command above returned.
The best way to solve this problem is to have your application specify the correct location of the libraries to start with so that setting DYLD_LIBRARY_PATH is not necessary. To do this you need to do the following:
Set the library identifier of each of the libraries that you're bundling inside your application to an #rpath-relative value. You can do this using install_name_tool -id #rpath/libFoo.dylib libFoo.dylib.
Add a Copy Files build phase to copy the libraries in to your application wrapper. MyApp.app/Contents/Frameworks is a typical location. MyApp.app/Contents/Resources should be avoided since binaries aren't resources in the usual sense of the term.
Specify a run path search path when linking your application. This gives the linker a list of paths to use to resolve any #rpath variables that it encounters in any load commands. If you're copying the libraries to MyApp.app/Contents/Frameworks you'll want to specify a run path search path of #loader_path/../Frameworks. You can do this via the LD_RUNPATH_SEARCH_PATHS (Runpath Search Paths) configuration setting in Xcode on your application target.
After doing all this you should be able to re-run the otool command mentioned above and see that the paths to your library are using #rpath-relative paths. You should then be able to run otool -lV MyApp.app/Contents/MacOS/MyApp and see an LC_RPATH load command specified with a value of #loader_path/../Frameworks. Finally, you should be able to run your application and see that it finds the libraries within its Frameworks directory without having DYLD_LIBRARY_PATH set!

Xcode, building and dylibs

I've looked at a few related questions and cannot seem to find a solution for myself.
Basically I'm using the libmp3lame.dylib in my Xcode project. The install process for lame produced the .dylib and placed it in usr/local/lib and to get Xcode to build and run the project I changed the Library Search Paths to include the above folder. This works fine.
Now it's come to producing a release version and I want to include the .dylib in the bundle so that the user doesn't have to put up with an install phase or anything similar. I created a copy files phase of the target and this dumps the .dylib into the Frameworks folder in the contents of the bundle. However, running otool on the binary shows that instead of using the .dylib inside the bundle, the compiler has linked it to the usr/local/lib version (even if I delete that version).
Looking at the other results in the otool output I have other frameworks being linked to inside the bundle, just not the .dylib. Looking at the properties for both these frameworks and the .dylib in Xcode I can't see any differences other than file type.
My searching suggests I need to use rpath or similar, but I've no clue where to apply this in Xcode.
My workaround for this issue was to do the following in terminal (I've used Automator to, well, automate this):
install_name_tool -id #executable_path/../Frameworks/libmp3lame.0.0.0.dylib ~/path/to/lib/in/app/libmp3lame.0.0.0.dylib
install_name_tool -change /usr/local/lib/libmp3lame.0.dylib #executable_path/../Frameworks/libmp3lame.0.0.0.dylib ~/path/to/app/Contents/MacOS/AppName
Basically swapping the path to the library from the one in the usr local lib to the one included in the bundle.
My tip is to look at the build output, how does it actually run ld?

Resources