Question

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 project and after a bit of fighting with frameworks and precompiled headers, have the dynlib compiling - and running successfully from the debugger.

When run standalone however its apparent that the dynlib is in the wrong place. "Library not loaded: /usr/local/lib/testlib.dynlib". On Windows - my more usual platform - Dlls can be placed in the same folder as the exe, and anywhere on the system path.

I would rather like my application to look for its dynlib somewhere in its application bundle (and certain documents seem to confirm this as the correct approach), but I don't know how.

Is it possible to configure the project in XCode so that the dylib actually is copied into the app bundle, in a place where the app will look for it?


It seems I don't need to use otool to reset the search path. If I edit the Target Info -> Build -> Deployment -> Installation Directory I can change, from inside XCode, the path that dyld is going to look at for the dylib. By default for a new dynamic library project, the path was set to /usr/local/lib. Which I have changed to ./ - otool confirms that ./ is now where dyld is going to be looking for this specific dynamic module. Unfortunately ./ doesn't seem to actually refer to any directory in my application bundle :(

So, my questions (not answered in the other thread) now are: 1. How do I enter a meaningful relative path in the Target Info -> ... -> Installation Directory setting, and 2. How can I get XCode to automatically copy the dylib 'target' into the application bundle target's bundle folder at the relative location?

Was it helpful?

Solution

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.

OTHER TIPS

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top