OSX, frameworks, @rpath, and image not found

Here’s a rough outline of a project that creates an app bundle with frameworks:

set(CMAKE_BUILD_WITH_INSTALL_RPATH YES)

add_executable(MyApp MACOSX_BUNDLE ...)
add_library(Fmwk1 SHARED ...)
add_library(Fmwk2 SHARED ...)

# Dependencies: MyApp --> Fmwk1 --> Fmwk2
# Direct linking like this assumes CMake 3.19 or later
target_link_libraries(MyApp PRIVATE Fmwk1)
target_link_libraries(Fmwk1 PRIVATE Fmwk2)

# Not all required properties shown below, just those related to these discussions
set_target_properties(MyApp PROPERTIES
    INSTALL_RPATH @executable_path/../Frameworks  # Assumes macOS bundle layout
    # CMake 3.20 or later required for the following
    XCODE_EMBED_FRAMEWORKS Fmwk1 Fmwk2
    XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY TRUE
    XCODE_EMBED_FRAMEWORKS_REMOVE_HEADERS_ON_COPY TRUE
)
set_target_properties(Fmwk1 Fmwk2 PROPERTIES
    FRAMEWORK TRUE
    INSTALL_RPATH @loader_path/../../..  # Assumes macOS bundle layout
    XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED FALSE
)

Some key observations:

  • BUILD_WITH_INSTALL_RPATH is usually what you want in the Apple world. An app bundle should be laid out with exactly the same directory structure whether in the build tree or whether installed (and workflows using the Xcode generator are quite likely to have no install step). You should be able to use the same relative @rpath locations even in your build tree. You don’t want/need absolute paths embedded in the binaries if you’ve got things set up correctly.
  • The above linking is taking advantage of improvements added in CMake 3.19. With earlier versions, linking frameworks presents problems when it comes time to embed them in the app bundle. CMake 3.20 completed that work and now directly supports framework embedding when using the Xcode generator. I’ve shown key steps for that in the above as well.

The above is for an app bundle, which your test appears to not be. I’ve shown the above mostly because it highlights how a typical arrangement may present complexities for test executables because those executables lack an app bundle structure. That would mean frameworks won’t be found at the places the embedded paths indicate. The test target should probably not set BUILD_WITH_INSTALL_RPATH to true because the test won’t be installed. You specifically want to embed absolute paths there. Perhaps in your project you’ve set the CMAKE_BUILD_WITH_INSTALL_RPATH variable like in my example and it is being applied to your test targets? If you prevent that, does it give you a working test? You would probably have to make the test executable link directly to all frameworks it needs, even if it doesn’t directly refer to any symbols from some of those frameworks (the linker may defeat you here if it optimises out some of those linker dependencies in the test binary).

You may want to look at XCTest. While I don’t have direct experience with it myself, it does appear to be potentially related to your situation (testing frameworks). CMake has a FindXCTest module which may help setting that up. You may want CMake 3.19.5 or later for that due to this issue.