Different framework linking behaviors
There seems to be some differences and minor inconveniences in how mac framework linking happens.
Here's a sample project to demonstrate them:
cmake_minimum_required(VERSION 3.15.0)
project(cmake_macos_framework_linkage LANGUAGES CXX)
find_library(FWAppKit NAMES AppKit)
find_library(FWAVFoundation NAMES AVFoundation)
find_library(FWCoreFoundation NAMES CoreFoundation)
find_library(FWCoreText NAMES CoreText)
message(">>>> find_library(AppKit): ${FWAppkit}")
message(">>>> find_library(AVFoundation): ${FWAVFoundation}")
message(">>>> find_library(CoreFoundation): ${FWCoreFoundation}")
message(">>>> find_library(CoreText): ${FWCoreText}")
add_library(AppKit::AppKit SHARED IMPORTED)
set_target_properties(AppKit::AppKit PROPERTIES
IMPORTED_LOCATION "${FWAppKit}/AppKit.tbd") # How do i know it should have a .tbd suffix?
#set_target_properties(AppKit::AppKit PROPERTIES
# IMPORTED_LOCATION "${FWAppKit}") # Not actual file, won't work.
add_library(AVFoundation::AVFoundation INTERFACE IMPORTED)
set_target_properties(AVFoundation::AVFoundation PROPERTIES
INTERFACE_LINK_LIBRARIES "${FWAVFoundation}")
set(source_path "${CMAKE_CURRENT_BINARY_DIR}/main.cpp")
file(GENERATE OUTPUT "${source_path}" CONTENT "int main(int argc, char**argv) {return 0;}")
add_executable(app "${source_path}")
target_link_libraries(app PRIVATE AVFoundation::AVFoundation)
target_link_libraries(app PRIVATE AppKit::AppKit)
target_link_libraries(app PRIVATE "${FWCoreFoundation}")
target_link_libraries(app PRIVATE "${FWCoreText}/CoreText.tbd")
$ cmake .. -GNinja
>>>> find_library(AppKit): /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/System/Library/Frameworks/AppKit.framework
>>>> find_library(AVFoundation): /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/System/Library/Frameworks/AVFoundation.framework
>>>> find_library(CoreFoundation): /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/System/Library/Frameworks/CoreFoundation.framework
>>>> find_library(CoreText): /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/System/Library/Frameworks/CoreText.framework
-- Configuring done
-- Generating done
$ ninja clean && ninja -v
# ...
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/c++
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk
-Wl,-search_paths_first
-Wl,-headerpad_max_install_names
CMakeFiles/app.dir/main.cpp.o -o app
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/System/Library/Frameworks/AppKit.framework/AppKit.tbd
-framework CoreFoundation
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/System/Library/Frameworks/CoreText.framework/CoreText.tbd
-framework AVFoundation
The above project finds 4 frameworks, and uses them:
- via an imported target with
IMPORTED_LOCATION
- via an imported target with
INTERFACE_LINK_LIBRARIES
, - directly links the result of
find_library
withtarget_link_libraries
with full framework path - directly links the result of
find_library
withtarget_link_libraries
with full library path within framework
The observed behaviour is that INTERFACE_LINK_LIBRARIES
and target_link_libraries
with a framework path end up using a -framework
flag, and the others directly link to a file by absolute path.
Afaik these differences are not documented anywhere.
One inconvenience is that using IMPORTED_LOCATION
with a full library path can't generally be known because the file might be a true mach-o library, or have a .tbd
suffix. You have to do a manual if(EXISTS "Foo.tbd")
, and populate the IMPORTED_LOCATION
property accordingly.
Another inconvenience is that if a Find module creates an imported target that uses IMPORTED_LOCATION
instead of INTERFACE_LINK_LIBRARIES
, it's not possible to link via a -framework flag
instead of the absolute file path (an example of this is the CMake FindGL.cmake
find module). It would be nice if this were somehow configurable.