Fix macOS SDL3 workflow dependency

This commit is contained in:
bdm
2026-07-03 20:58:21 +08:00
parent 19523efdee
commit 9f329a5d67
+92 -26
View File
@@ -66,7 +66,7 @@ jobs:
- name: Install System Dependencies via Homebrew
run: |
brew install ninja boost catch2 cmake enet fmt ffmpeg glslang hidapi libvpx lld llvm nasm nlohmann-json openal-soft sdl2 molten-vk vulkan-headers vulkan-loader webp lz4 zstd openssl@3 pkg-config libusb opus python@3.12
brew install ninja boost catch2 cmake enet fmt ffmpeg glslang hidapi libvpx lld llvm nasm nlohmann-json openal-soft sdl2 sdl3 molten-vk vulkan-headers vulkan-loader webp lz4 zstd openssl@3 pkg-config libusb opus python@3.12
- name: Clone Citron Source
shell: bash
@@ -192,6 +192,8 @@ jobs:
"$MACDEPLOYQT_PATH" "$APP_BUNDLE_PATH" -libpath="$QT_LIB_PATH" -verbose=1 -always-overwrite
FRAMEWORKS_PATH="$APP_BUNDLE_PATH/Contents/Frameworks"
EXECUTABLE_PATH="$APP_BUNDLE_PATH/Contents/MacOS/citron"
mkdir -p "$FRAMEWORKS_PATH"
# Create Vulkan ICD json to ensure bundled MoltenVK is used (not homebrew's)
echo "Setting up Vulkan ICD for bundled MoltenVK..."
@@ -209,40 +211,62 @@ jobs:
ICDEOF
echo "Created MoltenVK ICD at $VULKAN_ICD_DIR/MoltenVK_icd.json"
# Copy missing transitive dependencies (like libsharpyuv from webp, liblcms2 from jxl)
# Copy missing transitive dependencies (like libsharpyuv from webp, liblcms2 from jxl).
# Scan the main executable too; macdeployqt can miss non-Qt Homebrew libraries linked by citron.
echo "Checking for missing transitive dependencies..."
copy_missing_deps() {
find_bundled_binaries() {
printf '%s\n' "$EXECUTABLE_PATH"
find "$FRAMEWORKS_PATH" -name "*.dylib" -type f | while read -r dylib; do
# Handle @rpath/ references
otool -L "$dylib" 2>/dev/null | grep -E "^\s+@rpath/" | awk '{print $1}' | sed 's|@rpath/||' | while read -r dep; do
if [ ! -f "$FRAMEWORKS_PATH/$dep" ]; then
HOMEBREW_LIB=$(find /opt/homebrew/lib /opt/homebrew/Cellar /usr/local/lib -name "$dep" 2>/dev/null | head -1)
if [ -n "$HOMEBREW_LIB" ] && [ -f "$HOMEBREW_LIB" ]; then
echo "Copying missing @rpath dependency: $dep"
cp "$HOMEBREW_LIB" "$FRAMEWORKS_PATH/"
chmod u+w "$FRAMEWORKS_PATH/$dep"
install_name_tool -id "@rpath/$dep" "$FRAMEWORKS_PATH/$dep" 2>/dev/null || true
fi
fi
done
# Handle absolute homebrew path references (critical for portability!)
otool -L "$dylib" 2>/dev/null | grep -E "^\s+(/opt/homebrew|/usr/local)" | awk '{print $1}' | while read -r abs_dep; do
dep_name=$(basename "$abs_dep")
if [ ! -f "$FRAMEWORKS_PATH/$dep_name" ] && [ -f "$abs_dep" ]; then
echo "Copying missing absolute-path dependency: $dep_name (from $abs_dep)"
cp "$abs_dep" "$FRAMEWORKS_PATH/"
chmod u+w "$FRAMEWORKS_PATH/$dep_name"
install_name_tool -id "@rpath/$dep_name" "$FRAMEWORKS_PATH/$dep_name" 2>/dev/null || true
fi
done
printf '%s\n' "$dylib"
done
find "$FRAMEWORKS_PATH" -name "*.framework" -type d | while read -r framework_dir; do
framework_name=$(basename "$framework_dir" .framework)
framework_binary="$framework_dir/Versions/A/$framework_name"
[ ! -f "$framework_binary" ] && framework_binary="$framework_dir/$framework_name"
[ -f "$framework_binary" ] && printf '%s\n' "$framework_binary"
done
}
copy_deps_for_binary() {
local binary="$1"
[ -f "$binary" ] || return 0
# Handle @rpath/ references
otool -L "$binary" 2>/dev/null | grep -E "^\s+@rpath/" | awk '{print $1}' | sed 's|@rpath/||' | while read -r dep; do
if [ ! -f "$FRAMEWORKS_PATH/$dep" ]; then
HOMEBREW_LIB=$(find /opt/homebrew/lib /opt/homebrew/Cellar /usr/local/lib -name "$dep" 2>/dev/null | head -1)
if [ -n "$HOMEBREW_LIB" ] && [ -f "$HOMEBREW_LIB" ]; then
echo "Copying missing @rpath dependency for $(basename "$binary"): $dep"
cp "$HOMEBREW_LIB" "$FRAMEWORKS_PATH/"
chmod u+w "$FRAMEWORKS_PATH/$dep"
install_name_tool -id "@rpath/$dep" "$FRAMEWORKS_PATH/$dep" 2>/dev/null || true
fi
fi
done
# Handle absolute homebrew path references (critical for portability!)
otool -L "$binary" 2>/dev/null | grep -E "^\s+(/opt/homebrew|/usr/local)" | awk '{print $1}' | while read -r abs_dep; do
dep_name=$(basename "$abs_dep")
if [ ! -f "$FRAMEWORKS_PATH/$dep_name" ] && [ -f "$abs_dep" ]; then
echo "Copying missing absolute-path dependency for $(basename "$binary"): $dep_name (from $abs_dep)"
cp "$abs_dep" "$FRAMEWORKS_PATH/"
chmod u+w "$FRAMEWORKS_PATH/$dep_name"
install_name_tool -id "@rpath/$dep_name" "$FRAMEWORKS_PATH/$dep_name" 2>/dev/null || true
fi
done
}
copy_missing_deps() {
find_bundled_binaries | while read -r binary; do
copy_deps_for_binary "$binary"
done
}
# Run multiple passes to catch nested dependencies
for i in 1 2 3 4 5; do
echo "Pass $i: checking dependencies..."
copy_missing_deps
done
EXECUTABLE_PATH="$APP_BUNDLE_PATH/Contents/MacOS/citron"
install_name_tool -add_rpath "@executable_path/../Frameworks" "$EXECUTABLE_PATH" 2>/dev/null || true
@@ -299,8 +323,50 @@ jobs:
[ -f "$framework_binary" ] && install_name_tool -id "@rpath/$framework_name.framework/Versions/A/$framework_name" "$framework_binary" 2>/dev/null || true
done
find_bundled_binaries | while read -r binary; do
otool -L "$binary" 2>/dev/null | grep -E "^\s+/" | awk '{print $1}' | while read -r dep; do
dep_name=$(basename "$dep")
if [ -f "$FRAMEWORKS_PATH/$dep_name" ]; then
install_name_tool -change "$dep" "@rpath/$dep_name" "$binary" 2>/dev/null || true
fi
done
done
# Some SDL3 consumers load "libSDL3.dylib" dynamically at runtime, so it will not
# necessarily appear in otool output. Homebrew/macdeployqt may only leave
# "libSDL3.0.dylib" in Frameworks, which makes that dlopen path fail.
SDL3_BREW_PREFIX="$(brew --prefix sdl3 2>/dev/null || true)"
if [ ! -f "$FRAMEWORKS_PATH/libSDL3.dylib" ]; then
if [ -n "$SDL3_BREW_PREFIX" ] && [ -f "$SDL3_BREW_PREFIX/lib/libSDL3.dylib" ]; then
echo "Copying libSDL3.dylib from Homebrew for runtime dlopen compatibility"
cp "$SDL3_BREW_PREFIX/lib/libSDL3.dylib" "$FRAMEWORKS_PATH/libSDL3.dylib"
elif [ -f "$FRAMEWORKS_PATH/libSDL3.0.dylib" ]; then
echo "Creating libSDL3.dylib from bundled libSDL3.0.dylib for runtime dlopen compatibility"
cp "$FRAMEWORKS_PATH/libSDL3.0.dylib" "$FRAMEWORKS_PATH/libSDL3.dylib"
fi
fi
if [ -f "$FRAMEWORKS_PATH/libSDL3.dylib" ]; then
chmod u+w "$FRAMEWORKS_PATH/libSDL3.dylib"
install_name_tool -id "@rpath/libSDL3.dylib" "$FRAMEWORKS_PATH/libSDL3.dylib" 2>/dev/null || true
fi
chmod -R u+w "$APP_BUNDLE_PATH"
codesign --force --deep -s - "$APP_BUNDLE_PATH"
codesign --verify --deep --strict --verbose=2 "$APP_BUNDLE_PATH"
echo "Final executable SDL dependencies:"
otool -L "$EXECUTABLE_PATH" | grep -i SDL || true
echo "Final executable rpaths:"
otool -l "$EXECUTABLE_PATH" | grep -A2 LC_RPATH || true
echo "Checking for remaining Homebrew library references..."
if find_bundled_binaries | while read -r binary; do
otool -L "$binary" 2>/dev/null | grep -E "^\s+(/opt/homebrew|/usr/local)" && echo " in $binary"
done | tee /tmp/citron-homebrew-refs.txt; [ -s /tmp/citron-homebrew-refs.txt ]; then
echo "::error::Packaged app still contains Homebrew library references."
exit 1
fi
# Step 8: Create the final DMG
echo "Creating final DMG..."