I did ask this on StackOverflow already without much luck, so apologies for the redundancy if you’ve already seen this.
It seems that dylibs linked directly in target_link_libraries
get linked by absolute path, but those coming in transitively through CMake target dependencies are dropping the absolute path and linking by name only. This breaks the MacOS fixup bundle functionality that normally post-processes an .app
directory to include the libraries in relocatable fashion because it can’t find the dylib(s).
For example, I have a dependency on DCMTK, which itself has a dependency on libicu. DCMTK is built by for static linking by CMake, so it has a targets config file and it lists the dependency on the libicu dylib by absolute path:
$ grep libicu DCMTKTargets-release.cmake
IMPORTED_LINK_INTERFACE_LIBRARIES_RELEASE "/usr/local/app-libs/icu-67.1/lib/libicuuc.dylib;/usr/local/app-libs/icu-67.1/lib/libicudata.dylib;pthread"
Here is a minimal CMake to reproduce, although you of course need to build libicu
for dynamic linking and DCMTK
for static linking in the paths shown.
CMakeLists.txt
cmake_minimum_required(VERSION 3.11)
project(CatalinaTest)
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.14" CACHE STRING "" FORCE)
set(CMAKE_FIND_ROOT_PATH ${CMAKE_OSX_SYSROOT})
list(APPEND CMAKE_PREFIX_PATH
/usr/local/app-libs/vscdcmtk-3.6.3/lib/cmake/dcmtk
)
find_package(LibXml2 REQUIRED)
find_package(DCMTK REQUIRED CONFIG)
add_executable(CatalinaTest main.cpp)
target_link_libraries(CatalinaTest
${LIBXML2_LIBRARIES}
ofstd
)
The linker command that runs is:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++ -target x86_64-apple-macos10.14 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk -L/Users/jake/devel/cmake-test.build/Release -F/Users/jake/devel/cmake-test.build/Release -filelist /Users/jake/devel/cmake-test.build/CatalinaTest.build/Release/CatalinaTest.build/Objects-normal/x86_64/CatalinaTest.LinkFileList -Wl,-search_paths_first -Wl,-headerpad_max_install_names /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr/lib/libxml2.tbd /usr/local/app-libs/vscdcmtk-3.6.3/lib/libofstd.a /usr/local/app-libs/icu-67.1/lib/libicuuc.dylib /usr/local/app-libs/icu-67.1/lib/libicudata.dylib -lpthread -Xlinker -dependency_info -Xlinker /Users/jake/devel/cmake-test.build/CatalinaTest.build/Release/CatalinaTest.build/Objects-normal/x86_64/CatalinaTest_dependency_info.dat -o /Users/jake/devel/cmake-test.build/Release/CatalinaTest
So it passes both direct and transitive dependencies by absolute path in the same section without any other linker modification flags between. Now, in the linked executable:
$ otool -L Release/CatalinaTest
Release/CatalinaTest:
/usr/lib/libxml2.2.dylib (compatibility version 10.0.0, current version 10.9.0)
libicuuc.67.dylib (compatibility version 67.0.0, current version 67.1.0)
libicudata.67.dylib (compatibility version 67.0.0, current version 67.1.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1281.100.1)
/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 902.1.0)
We can see libxml2
's dylib has the absolute path but libicu
's two dylibs do not. Although this example has a tbd
file for libxml2
, the behavior is the same if we use another library that passes a dylib
directly. The difference seems to be all indirect/transitive dylibs are name-only, and all direct dylibs get the absolute path.
What is causing this difference and how can I get the transitive dylibs to link by full path so that bundle post processing can proceed as expected?