Exporting libraries for inclusion in `.xcframework`s
Some projects will want to install(EXPORT)
in such a way that the exported file can set the target's IMPORTED_LOCATION
to an .xcframework
if need be. A .xcframework
can contain libraries for multiple operating systems, so one .xcframework
can stand in place of multiple .framework
s.
However, such an export would need to fall back to the .framework
if the .xcframework
doesn't exist. CMake can only build for one operating system at a time, so each .framework
would have to be built in a different CMake build tree. Thus, the .xcframework
would have to be created manually after all the builds have finished. CMake can't create it automatically, and thus can't guarantee its existence, necessitating the need for the fallback.
I imagine a project like this would look something like the following:
cmake_minimum_required(VERSION 3.27)
project(xcfw_example C)
include(CMakePackageConfigHelpers)
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/ExampleFramework.c
[[void ExampleFramwork(void)
{
}
]])
add_library(ExampleFramework STATIC ${CMAKE_CURRENT_BINARY_DIR}/ExampleFramework.c)
set_property(TARGET ExampleFramework PROPERTY FRAMEWORK 1)
# $<APPLE_PLATFORM_NAME> can evaluate to one of:
# "macos"
# "ios"
# "tvos"
# "watchos"
# "visionos"
# based on the value of ${CMAKE_SYSTEM_NAME} at configure time.
# If EXPORT_XCFRAMEWORK is used, EXPORT DESTINATION must contain $<APPLE_PLATFORM_NAME>. When the EXPORT_XCFRAMEWORK DESTINATION file is generated, it includes lib/$<APPLE_PLATFORM_NAME>/cmake/XcfwExample/XcfwExampleTargets.cmake.
# In that case, $<APPLE_PLATFORM_NAME> doesn't get evaluated as a genex, but rather is a marker for which part of the path should be recalculated based on the value of ${CMAKE_SYSTEM_NAME} at import time.
install(TARGETS ExampleFramework DESTINATION lib/$<APPLE_PLATFORM_NAME> EXPORT xcfw_example_exports)
install(EXPORT xcfw_example_exports DESTINATION lib/$<APPLE_PLATFORM_NAME>/cmake/XcfwExample FILE XcfwExampleTargets.cmake
EXPORT_XCFRAMEWORKS DESTINATION lib/cmake/XcfwExample FILE XcfwExampleTargets.cmake
TARGET ExampleFramework XCFRAMEWORK_LOCATION lib/ExampleFramework.xcframework
)
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/XcfwExampleConfig.cmake.in [[@PACKAGE_INIT@
include(${CMAKE_CURRENT_LIST_DIR}/XcfwExampleTargets.cmake)
]])
configure_package_config_file(${CMAKE_CURRENT_BINARY_DIR}/XcfwExampleConfig.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/XcfwExampleConfig.cmake INSTALL_DESTINATION lib/cmake/XcfwExample)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/XcfwExampleConfig.cmake DESTINATION lib/cmake/XcfwExample)
This file demonstrates two new mechanisms that would need to be added:
-
An
$<APPLE_PLATFORM_NAME>
genex, which evaluates tomacos
,iphoneos
,iphonesimulator
,appletvos
, etc. based on which system CMake is building for. -
An
EXPORT_XCFRAMEWORK
mode that's been added toinstall(EXPORT)
. The file that gets installed tolib/cmake/XcfwExample/XcfwExampleTargets.cmake
would look something like the following:if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") set(_apple_platform_name "macos") elseif(CMAKE_SYSTEM_NAME STREQUAL "iOS") set(_apple_platform_name "iphoneos") # etc... endif() include(${_IMPORT_PREFIX}/lib/${_apple_platform_name}/cmake/XcfwExample/XcfwExampleTargets.cmake)
If the
EXPORT_XCFRAMEWORK
mode is used, theninstall(EXPORT ... DESTINATION)
must contain$<APPLE_PLATFORM_NAME>
and not be within any other genexes, otherwise CMake will throw an error. This is to ensure that the export file actually selects a platform-specific file to include.lib/$<APPLE_PLATFORM_NAME>/cmake/XcfwExample/XcfwExampleTargets.cmake
will then check to see ifXCFRAMEWORK_LOCATION
exists for each target in the export. If it does, then that's what theIMPORTED_LOCATION
will be. If not, then it falls back to the.framework
.
This approach does have the disadvantage that the .xcframework
must manually be created after all of the builds, but I don't see a way around this.