I am trying to create an mac application in XCode that has some of its implementation in a dynamic library.
I added a new target (dynamic library) to my XCode cocoa
I believe you are not copying the dynamic library into the application bundle at compile time.
The application is looking for a point in the filesystem, but it only does this after not finding it inside the application bundle. You need to use the otool
command to modify the binary after it is compiled (using a build script).
You will find you answer in this previously asked question: How Do I Create a Dynamic Library in Xcode.
Good luck.
After a lot of research I got it, I was trying to package some openCV dylib's with my application because I was receiving the "Library not loaded:...." error.
Dylibs have an install name that indicates where you installed them when you did it the first time (macports installs them on /opt/local/lib/). They also have the path to other dylib's that they need to work. For example, if you have the dylib called "libopencv_core.2.4.8.dylib" you can check the install name and the paths to other dylibs that it uses with this command:
otool -L libopencv_core.2.4.8.dylib
The output of the command is:
libopencv_core.2.4.8.dylib:
/opt/local/lib/libopencv_core.2.4.dylib (compatibility version 2.4.0, current version 2.4.8)
/opt/local/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.8)
/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 120.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1197.1.1)
1- First line: name of the file you are checking.
2- Second line: Install name of the dylib you are checking. (macports installs them in /opt/local/lib/)
3- Third line: Path to other dylibs that it needs to work that are not in all OSX systems. (macports installs them in /opt/local/lib/ and you have them because you previously installed, thats why if you distribute your app to other macs you get the "library not found" error)
4 & 5 - Lines 4 and 5: dylibs inside all macOSX systems. you don't need to copy them in your project.
Now we know this, our goals are:
1- Change all the paths inside dylibs (install names and paths to other dylibs that are needed) because we are going to put our dylibs inside the app in the "Frameworks" directory, and when we move the app or copy it to other mac we want the dylibs to look for each other inside "frameworks" folder inside the directory of our app: the path we will use is:
@executable_path/../Frameworks/nameOFTheDylib.dylib
(NOTE THE TWO POINTS ARE REALLY IMPORTANT!)
2- Copy to xCode all the dylibs that we need, but not all the dylibs inside the /opt/local/lib folder, JUST THE ONES WE NEED:(all of them are too much, maybe 500Mb) we will need to follow the links from the dylibs we know we need (usually 4 or 5) and the dylibs that are included because those 4 or 5 need them for working.
Y finally did it this way:
a) To change the paths:
1- Copy all the /opt/local/lib file to the desktop.
2- Remove all files inside the lib folder that are NOT .dylib archives.
3-Put this script inside the lib folder (together with the dylibs) we have just copied in the desktop:
(Copy this text in textEdit and save it as theNameYouPrefer.sh)
#!/bin/bash
echo empieza el script
EXECFILE=${BUILT_PRODUCTS_DIR}/${EXECUTABLE_PATH}
OLDLIBPATH="/opt/local/lib/"
NEWLIBPATH="@executable_path/../Frameworks/"
ACTUALPATH=`pwd`
# space separated list of libraries
for TARGET in ${ACTUALPATH}/* ; do
TARGET=$(basename $TARGET)
if [ ${TARGET: -6} == ".dylib" ]
then
echo otool -DX ${TARGET}
TARGETID=`otool -DX ${TARGET}`
echo ${TARGETID}
echo install_name_tool -id ${NEWLIBPATH}${TARGET} ${TARGET}
echo `install_name_tool -id ${NEWLIBPATH}${TARGET} ${TARGET}`
for DYLIB in ${ACTUALPATH}/* ; do
DYLIB=$(basename $DYLIB)
if [ ${DYLIB: -6} == ".dylib" ]
then
echo install_name_tool -change ${TARGETID} ${NEWLIBPATH}${TARGET} ${DYLIB}
echo `install_name_tool -change ${TARGETID} ${NEWLIBPATH}${TARGET} ${DYLIB}`
else
if [ ${DYLIB: -3} == ".sh" ]
then
echo soyYo
else
echo mkdir ${ACTUALPATH}/eliminadas/
echo `mkdir ${ACTUALPATH}/eliminadas/`
echo mv ${DYLIB} ${ACTUALPATH}/eliminadas
echo `mv ${TARGET} ${ACTUALPATH}/eliminadas`
fi
fi
done
else
if [ ${TARGET: -3} == ".sh" ] || ![ -h ${TARGET} ]
then
echo noMeElimino
else
echo mkdir ${ACTUALPATH}/eliminadas/
echo `mkdir ${ACTUALPATH}/../eliminadas/`
echo mv ${TARGET} ${ACTUALPATH}/eliminadas/${TARGET}
echo `mv ${TARGET} ${ACTUALPATH}/../eliminadas`
fi
fi
done
echo finaliza el script
4- Execute it:
cd Desktop/lib
sudo ./theNameYouPrefer.sh
It is REALLY IMPORTANT to run it as sudo, because the dylibs could be write-protected.
At this point you should have all the dylibs paths changed. You can check it in some dylibs with the command:
otool -L theDylibYouPrefer.dylib
b) Select just the dylibs needed, not all of the lib folder:
1-Rename the lib folder to lib2: lib =====> lib2
2- Create another folder (with the name you prefer, for example exampleFolder) in the desktop, NEXT TO lib2 folder.
3- Copy inside exampleFolder the important dylibs that you know you will need them for sure, in my case they are:libopencv_core.2.4.8.dylib, libopencv_highgui.2.4.8.dylib, libopencv_imgproc.2.4.8.dylib, libopencv_objdetect.2.4.8.dylib
4- Script: copy it inside exampleFolder, next to the important dylibs. I do not know much about scripts, so this one is a bit more dirty:
#!/bin/bash
echo empieza el script
EXECFILE=${BUILT_PRODUCTS_DIR}/${EXECUTABLE_PATH}
OLDLIBPATH="/opt/local/lib/"
NEWLIBPATH="@executable_path/../Frameworks/"
ACTUALPATH=`pwd`
# space separated list of libraries
for TARGET in ${ACTUALPATH}/* ; do
TARGET=$(basename $TARGET)
if [ ${TARGET: -6} == ".dylib" ]
then
echo otool -DX ${TARGET}
TARGETID=`otool -L ${TARGET}`
echo ${TARGETID}
echo :::::::::::::::::::::::::::
ELIM=`echo ${TARGETID} | sed 's/([^)]*)/UUU/g'`
echo $ELIM
#echo ${TARGETID} | sed 's/([^)]*)/UUU/g'
ELIM=`echo $(awk '{gsub("@executable_path/../Frameworks/", "");print}' <<< ${ELIM})`
echo $ELIM
ELIM=`echo $(awk '{gsub(" UUU", "");print}' <<< ${ELIM})`
echo $ELIM
ELIM=`echo $(awk '{gsub(":", "");print}' <<< ${ELIM})`
echo $ELIM
IFS=' ' read -a ARRAY <<< $ELIM
for element in ${ARRAY[@]}
do
echo $element
if [[ ${element} == */* ]]
then
echo "NO FRAMEWORK";
else
echo `mkdir ${ACTUALPATH}/../copy`
echo `cp ${ACTUALPATH}/../lib2/${element} ${ACTUALPATH}/${element}`
fi
done
else
if [ ${TARGET: -3} == ".sh" ] || ![ -h ${TARGET} ]
then
echo noMeElimino
else
echo mkdir ${ACTUALPATH}/eliminadas/
echo `mkdir ${ACTUALPATH}/../eliminadas/`
echo mv ${TARGET} ${ACTUALPATH}/eliminadas/${TARGET}
echo `mv ${TARGET} ${ACTUALPATH}/../eliminadas`
fi
fi
done
echo finaliza el script
5- Execute this script a lot of times, until you appreciate that the number of files inside exampleFolder does not change.
6- The dylibs you need are all of them inside exampleFolder! In my case, I got 61 dylibs. (remember when we started I placed there 4 dylibs)
7- The last thing you need to do is drop the inside the "Frameworks" folder in xCode. After that REMEMBER TO GO TO "BUILD PHASES" AND ADD A "copy files" PHASE: "editor"=>"add build phase" => "add copy files build phase" AND INSIDE THE "copy files" PHASE CLICK IN THE PLUS ICON AND ADD THERE ALL THE DYLIBS YOU FIND IN "Framewroks" FOLDER. The destination you need is Frameworks.
And thats it, I got it this way more or less.
At last. Found some official documentation. Its possible to do this entirely within XCode.
I was halfway there modifying the Installed Directory setting of the library: It needed to be:
@executable_path/../Frameworks
Then I needed to add a "Copy Files" build step to the library to actually copy it to the Frameworks folder.
Simple. Automatic.