I built an audio plugin. I target .app and .component.
I dynamically link against a brew-installed library, libfluidsynth.
I copied libfluidsynth into the .app / .component.
I used install_name_tool to re-link the binary to point to the bundled libfluidsynth.
libfluidsynth depends on glib, gthread, intl.
I copied those libraries into the bundle, re-linked libfluidsynth to prefer the bundled copy.
I also did the same for those libraries and their dependencies.
Here's a quick peek at what that looks like:
ls /Users/me/git/juicysfplugin/Builds/MacOSX/build/Release/juicysfplugin.app/Contents/Frameworks
    libfluidsynth.1.7.1.dylib
    libglib-2.0.0.dylib
    libgthread-2.0.0.dylib
    libintl.8.dylib
    libpcre.1.dylib
otool -L \
/Users/me/git/juicysfplugin/Builds/MacOSX/build/Release/juicysfplugin.app/Contents/MacOS/juicysfplugin \
/Users/me/git/juicysfplugin/Builds/MacOSX/build/Release/juicysfplugin.app/Contents/Frameworks/* \
| grep -vE '\t(/System/Library|/usr/lib)'
/Users/me/git/juicysfplugin/Builds/MacOSX/build/Release/juicysfplugin.app/Contents/MacOS/juicysfplugin:
    @executable_path/../Frameworks/libfluidsynth.1.7.1.dylib (compatibility version 1.0.0, current version 1.7.1)
/Users/me/git/juicysfplugin/Builds/MacOSX/build/Release/juicysfplugin.app/Contents/Frameworks/libfluidsynth.1.7.1.dylib:
    @loader_path/../Frameworks/libfluidsynth.1.dylib (compatibility version 1.0.0, current version 1.7.1)
    @loader_path/../Frameworks/libgthread-2.0.0.dylib (compatibility version 5401.0.0, current version 5401.3.0)
    @loader_path/../Frameworks/libglib-2.0.0.dylib (compatibility version 5401.0.0, current version 5401.3.0)
    @loader_path/../Frameworks/libintl.8.dylib (compatibility version 10.0.0, current version 10.5.0)
/Users/me/git/juicysfplugin/Builds/MacOSX/build/Release/juicysfplugin.app/Contents/Frameworks/libglib-2.0.0.dylib:
    @loader_path/../Frameworks/libglib-2.0.0.dylib (compatibility version 5401.0.0, current version 5401.3.0)
    @loader_path/../Frameworks/libpcre.1.dylib (compatibility version 4.0.0, current version 4.9.0)
    @loader_path/../Frameworks/libintl.8.dylib (compatibility version 10.0.0, current version 10.5.0)
/Users/me/git/juicysfplugin/Builds/MacOSX/build/Release/juicysfplugin.app/Contents/Frameworks/libgthread-2.0.0.dylib:
    @loader_path/../Frameworks/libgthread-2.0.0.dylib (compatibility version 5401.0.0, current version 5401.3.0)
    @loader_path/../Frameworks/libglib-2.0.0.dylib (compatibility version 5401.0.0, current version 5401.3.0)
    @loader_path/../Frameworks/libpcre.1.dylib (compatibility version 4.0.0, current version 4.9.0)
    @loader_path/../Frameworks/libintl.8.dylib (compatibility version 10.0.0, current version 10.5.0)
/Users/me/git/juicysfplugin/Builds/MacOSX/build/Release/juicysfplugin.app/Contents/Frameworks/libintl.8.dylib:
    @loader_path/../Frameworks/libintl.8.dylib (compatibility version 10.0.0, current version 10.5.0)
/Users/me/git/juicysfplugin/Builds/MacOSX/build/Release/juicysfplugin.app/Contents/Frameworks/libpcre.1.dylib:
    @loader_path/../Frameworks/libpcre.1.dylib (compatibility version 4.0.0, current version 4.9.0)
This worked perfectly for .app. Here, look at the file opens (opensnoop | grep 'dylib'):
EXECNAME      PATH
juicysfplugin  juicysfplugin.app/Contents/MacOS/../Frameworks/libfluidsynth.1.7.1.dylib
juicysfplugin  juicysfplugin.app/Contents/MacOS/../Frameworks/../Frameworks/libgthread-2.0.0.dylib
juicysfplugin  juicysfplugin.app/Contents/MacOS/../Frameworks/../Frameworks/libglib-2.0.0.dylib
juicysfplugin  juicysfplugin.app/Contents/MacOS/../Frameworks/../Frameworks/libintl.8.dylib
juicysfplugin  juicysfplugin.app/Contents/MacOS/../Frameworks/../Frameworks/../Frameworks/libpcre.1.dylib
The .app only looks for dylibs inside its bundled Frameworks folder. Perfect.
Then I did the same copies and re-links upon my .component target. This did not work.
I load the .component into an audio plugin host, and check the file opens:
EXECNAME     PATH
Plugin Host  /Users/me/Library/Audio/Plug-Ins/Components/juicysfplugin.component/Contents/MacOS/juicysfplugin
Plugin Host  /usr/local/lib/libfluidsynth.1.7.1.dylib
Plugin Host  /usr/local/opt/glib/lib/libgthread-2.0.0.dylib
Plugin Host  /usr/local/opt/glib/lib/libglib-2.0.0.dylib
Plugin Host  /usr/local/opt/gettext/lib/libintl.8.dylib
Plugin Host  /usr/local/opt/pcre/lib/libpcre.1.dylib
It's looking for libraries under /usr/local. Why? As a sanity-check, I used otool to confirm that I had indeed linked correctly:
otool -L \
/Users/me/Library/Audio/Plug-Ins/Components/juicysfplugin.component/Contents/MacOS/juicysfplugin \
/Users/me/Library/Audio/Plug-Ins/Components/juicysfplugin.component/Contents/Frameworks/* \
| grep -vE '\t(/System/Library|/usr/lib)'
/Users/me/Library/Audio/Plug-Ins/Components/juicysfplugin.component/Contents/MacOS/juicysfplugin:
    @executable_path/../Frameworks/libfluidsynth.1.7.1.dylib (compatibility version 1.0.0, current version 1.7.1)
/Users/me/Library/Audio/Plug-Ins/Components/juicysfplugin.component/Contents/Frameworks/libfluidsynth.1.7.1.dylib:
    @loader_path/../Frameworks/libfluidsynth.1.dylib (compatibility version 1.0.0, current version 1.7.1)
    @loader_path/../Frameworks/libgthread-2.0.0.dylib (compatibility version 5401.0.0, current version 5401.3.0)
    @loader_path/../Frameworks/libglib-2.0.0.dylib (compatibility version 5401.0.0, current version 5401.3.0)
    @loader_path/../Frameworks/libintl.8.dylib (compatibility version 10.0.0, current version 10.5.0)
/Users/me/Library/Audio/Plug-Ins/Components/juicysfplugin.component/Contents/Frameworks/libglib-2.0.0.dylib:
    @loader_path/../Frameworks/libglib-2.0.0.dylib (compatibility version 5401.0.0, current version 5401.3.0)
    @loader_path/../Frameworks/libpcre.1.dylib (compatibility version 4.0.0, current version 4.9.0)
    @loader_path/../Frameworks/libintl.8.dylib (compatibility version 10.0.0, current version 10.5.0)
/Users/me/Library/Audio/Plug-Ins/Components/juicysfplugin.component/Contents/Frameworks/libgthread-2.0.0.dylib:
    @loader_path/../Frameworks/libgthread-2.0.0.dylib (compatibility version 5401.0.0, current version 5401.3.0)
    @loader_path/../Frameworks/libglib-2.0.0.dylib (compatibility version 5401.0.0, current version 5401.3.0)
    @loader_path/../Frameworks/libpcre.1.dylib (compatibility version 4.0.0, current version 4.9.0)
    @loader_path/../Frameworks/libintl.8.dylib (compatibility version 10.0.0, current version 10.5.0)
/Users/me/Library/Audio/Plug-Ins/Components/juicysfplugin.component/Contents/Frameworks/libintl.8.dylib:
    @loader_path/../Frameworks/libintl.8.dylib (compatibility version 10.0.0, current version 10.5.0)
/Users/me/Library/Audio/Plug-Ins/Components/juicysfplugin.component/Contents/Frameworks/libpcre.1.dylib:
    @loader_path/../Frameworks/libpcre.1.dylib (compatibility version 4.0.0, current version 4.9.0)
I thought I linked it correctly. I used exactly the same script (it's automated and parameterised). What am I doing wrong? How did the audio plugin host know to lookup dependencies under /usr/local? Why were my library links ignored?
I've released the binaries to: https://github.com/Birch-san/juicysfplugin/releases/tag/1.0.1
My relinking script is here: https://github.com/Birch-san/juicysfplugin/blob/master/Builds/MacOSX/relink-build-for-distribution.sh
The main difference between how the .app and .component targets are used is:
- .appis standalone
- .componentis an Audio Unit plugin, that you load into a DAW / audio plugin host.
So, perhaps the runtime dependency resolution is different when a parent process is responsible for loading our executable?
