Files
kte/cmake/fix_bundle.cmake
T
kyleandClaude Opus 5.5 fe4e2abb3a Bundle SDL3 for Homebrew's sdl2-compat
Homebrew's sdl2 is now sdl2-compat, which dlopen()s SDL3 at startup
instead of linking it, so fixup_bundle never copied SDL3 into kge.app
and kge aborted at launch with "Failed loading SDL3 library". When the
bundled libSDL2 is sdl2-compat, copy SDL3 to
Contents/Frameworks/libSDL3.dylib, the first place it looks.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-24 12:26:53 -07:00

107 lines
4.4 KiB
CMake

cmake_minimum_required(VERSION 3.15)
# Fix up a macOS .app bundle by copying non-Qt dylibs into
# Contents/Frameworks and rewriting install names to use @rpath/@loader_path.
#
# Usage:
# cmake -DAPP_BUNDLE=/path/to/kge.app -P cmake/fix_bundle.cmake
if (NOT APP_BUNDLE)
message(FATAL_ERROR "APP_BUNDLE not set. Invoke with -DAPP_BUNDLE=/path/to/App.app")
endif ()
get_filename_component(APP_DIR "${APP_BUNDLE}" ABSOLUTE)
set(EXECUTABLE "${APP_DIR}/Contents/MacOS/kge")
if (NOT EXISTS "${EXECUTABLE}")
message(FATAL_ERROR "Executable not found at: ${EXECUTABLE}")
endif ()
include(BundleUtilities)
# Directories to search when resolving prerequisites. We include Homebrew so that
# if any deps are currently resolved from there, fixup_bundle will copy them into
# the bundle and rewrite install names to be self-contained.
set(DIRS
"/usr/local/lib"
"/opt/homebrew/lib"
"/opt/homebrew/opt"
)
# Note: We pass empty plugin list so fixup_bundle scans the executable and all
# libs it references recursively. Qt frameworks already live in the bundle after
# macdeployqt; this step is primarily for non-Qt dylibs (glib, icu, pcre2, zstd,
# dbus, etc.).
# fixup_bundle often fails if copied libraries are read-only.
# We also try to use the system install_name_tool and otool to avoid issues with Anaconda's version.
# Note: BundleUtilities uses find_program(gp_otool "otool") internally, so we might need to set it differently.
set(gp_otool "/usr/bin/otool")
set(CMAKE_INSTALL_NAME_TOOL "/usr/bin/install_name_tool")
set(CMAKE_OTOOL "/usr/bin/otool")
set(ENV{PATH} "/usr/bin:/bin:/usr/sbin:/sbin")
execute_process(COMMAND chmod -R u+w "${APP_DIR}/Contents/Frameworks")
fixup_bundle("${APP_DIR}" "" "${DIRS}")
# Homebrew's sdl2 is sdl2-compat, which dlopen()s SDL3 at startup instead of
# linking it, so fixup_bundle never sees it. Without SDL3 in the bundle kge
# stops at launch with "Failed loading SDL3 library". sdl2-compat looks for
# @loader_path/libSDL3.dylib first; put SDL3 there.
set(SDL2_IN_BUNDLE "${APP_DIR}/Contents/Frameworks/libSDL2-2.0.0.dylib")
if (EXISTS "${SDL2_IN_BUNDLE}")
file(STRINGS "${SDL2_IN_BUNDLE}" SDL2_COMPAT_REFS REGEX "libSDL3\\.dylib")
if (SDL2_COMPAT_REFS)
find_file(SDL3_DYLIB NAMES libSDL3.0.dylib libSDL3.dylib
PATHS "/opt/homebrew/opt/sdl3/lib" "/opt/homebrew/lib" "/usr/local/opt/sdl3/lib" "/usr/local/lib"
NO_DEFAULT_PATH)
if (NOT SDL3_DYLIB)
message(FATAL_ERROR "libSDL2 in the bundle is sdl2-compat, which needs SDL3 at run time, "
"but libSDL3 was not found (brew install sdl3)")
endif ()
file(REAL_PATH "${SDL3_DYLIB}" SDL3_REAL)
set(SDL3_IN_BUNDLE "${APP_DIR}/Contents/Frameworks/libSDL3.dylib")
message(STATUS "Bundling SDL3 for sdl2-compat: ${SDL3_REAL}")
file(COPY_FILE "${SDL3_REAL}" "${SDL3_IN_BUNDLE}")
execute_process(COMMAND chmod u+w "${SDL3_IN_BUNDLE}")
execute_process(COMMAND /usr/bin/install_name_tool -id "@loader_path/libSDL3.dylib" "${SDL3_IN_BUNDLE}"
RESULT_VARIABLE SDL3_ID_RESULT)
if (NOT SDL3_ID_RESULT EQUAL 0)
message(FATAL_ERROR "install_name_tool -id failed on ${SDL3_IN_BUNDLE}")
endif ()
endif ()
endif ()
# On Apple Silicon (and modern macOS in general), modifications by fixup_bundle
# invalidate code signatures. We must re-sign the bundle (at least ad-hoc)
# for it to be allowed to run.
# We sign deep, but sometimes explicit signing of components is more reliable.
message(STATUS "Re-signing ${APP_DIR} after fixup...")
# 1. Sign dylibs in Frameworks
file(GLOB_RECURSE DYLIBS "${APP_DIR}/Contents/Frameworks/*.dylib")
foreach (DYLIB ${DYLIBS})
message(STATUS "Signing ${DYLIB}...")
execute_process(COMMAND /usr/bin/codesign --force --sign - "${DYLIB}")
endforeach ()
# 2. Sign nested executables
message(STATUS "Signing nested kte...")
execute_process(COMMAND /usr/bin/codesign --force --sign - "${APP_DIR}/Contents/MacOS/kte")
# 3. Sign the main executable explicitly
message(STATUS "Signing main kge...")
execute_process(COMMAND /usr/bin/codesign --force --sign - "${APP_DIR}/Contents/MacOS/kge")
# 4. Sign the main bundle
execute_process(
COMMAND /usr/bin/codesign --force --deep --sign - "${APP_DIR}"
RESULT_VARIABLE CODESIGN_RESULT
)
if (NOT CODESIGN_RESULT EQUAL 0)
message(FATAL_ERROR "Codesign failed with error: ${CODESIGN_RESULT}")
endif ()
message(STATUS "fix_bundle.cmake completed for ${APP_DIR}")