Enhance macOS deployment with Sparkle integration and update build scripts
Some checks failed
Build / Build openSUSE (leap:15.6) (push) Has been cancelled
Build / Build openSUSE (leap:16.0) (push) Has been cancelled
Build / Build openSUSE (tumbleweed) (push) Has been cancelled
Build / Build Fedora (42) (push) Has been cancelled
Build / Build Fedora (43) (push) Has been cancelled
Build / Build Fedora (44) (push) Has been cancelled
Build / Build OpenMandriva (cooker) (push) Has been cancelled
Build / Build Mageia (9) (push) Has been cancelled
Build / Build Debian (bookworm) (push) Has been cancelled
Build / Build Debian (forky) (push) Has been cancelled
Build / Build Debian (trixie) (push) Has been cancelled
Build / Build Ubuntu (noble) (push) Has been cancelled
Build / Build Ubuntu (questing) (push) Has been cancelled
Build / Build Ubuntu (resolute) (push) Has been cancelled
Build / Upload Ubuntu PPA (noble) (push) Has been cancelled
Build / Upload Ubuntu PPA (questing) (push) Has been cancelled
Build / Upload Ubuntu PPA (resolute) (push) Has been cancelled
Build / Build FreeBSD (push) Has been cancelled
Build / Build OpenBSD (push) Has been cancelled
Build / Build macOS Public (release, macos-15) (push) Has been cancelled
Build / Build macOS Public (release, macos-15-intel) (push) Has been cancelled
Build / Build macOS Private (release, macos-arm64) (push) Has been cancelled
Build / Build Windows MinGW (i686, debug) (push) Has been cancelled
Build / Build Windows MinGW (i686, release) (push) Has been cancelled
Build / Build Windows MinGW (x86_64, debug) (push) Has been cancelled
Build / Build Windows MinGW (x86_64, release) (push) Has been cancelled
Build / Build Windows MSVC (arm64, debug, arm64 debug, windows-11-arm) (push) Has been cancelled
Build / Build Windows MSVC (arm64, release, arm64 release, windows-11-arm) (push) Has been cancelled
Build / Build Windows MSVC (x86, debug, x86 debug, windows-2022) (push) Has been cancelled
Build / Build Windows MSVC (x86, release, x86 release, windows-2022) (push) Has been cancelled
Build / Build Windows MSVC (x86_64, debug, x86_64 debug, windows-2022) (push) Has been cancelled
Build / Build Windows MSVC (x86_64, release, x86_64 release, windows-2022) (push) Has been cancelled
Build / Upload (push) Has been cancelled
Build / Attach to release (push) Has been cancelled
Some checks failed
Build / Build openSUSE (leap:15.6) (push) Has been cancelled
Build / Build openSUSE (leap:16.0) (push) Has been cancelled
Build / Build openSUSE (tumbleweed) (push) Has been cancelled
Build / Build Fedora (42) (push) Has been cancelled
Build / Build Fedora (43) (push) Has been cancelled
Build / Build Fedora (44) (push) Has been cancelled
Build / Build OpenMandriva (cooker) (push) Has been cancelled
Build / Build Mageia (9) (push) Has been cancelled
Build / Build Debian (bookworm) (push) Has been cancelled
Build / Build Debian (forky) (push) Has been cancelled
Build / Build Debian (trixie) (push) Has been cancelled
Build / Build Ubuntu (noble) (push) Has been cancelled
Build / Build Ubuntu (questing) (push) Has been cancelled
Build / Build Ubuntu (resolute) (push) Has been cancelled
Build / Upload Ubuntu PPA (noble) (push) Has been cancelled
Build / Upload Ubuntu PPA (questing) (push) Has been cancelled
Build / Upload Ubuntu PPA (resolute) (push) Has been cancelled
Build / Build FreeBSD (push) Has been cancelled
Build / Build OpenBSD (push) Has been cancelled
Build / Build macOS Public (release, macos-15) (push) Has been cancelled
Build / Build macOS Public (release, macos-15-intel) (push) Has been cancelled
Build / Build macOS Private (release, macos-arm64) (push) Has been cancelled
Build / Build Windows MinGW (i686, debug) (push) Has been cancelled
Build / Build Windows MinGW (i686, release) (push) Has been cancelled
Build / Build Windows MinGW (x86_64, debug) (push) Has been cancelled
Build / Build Windows MinGW (x86_64, release) (push) Has been cancelled
Build / Build Windows MSVC (arm64, debug, arm64 debug, windows-11-arm) (push) Has been cancelled
Build / Build Windows MSVC (arm64, release, arm64 release, windows-11-arm) (push) Has been cancelled
Build / Build Windows MSVC (x86, debug, x86 debug, windows-2022) (push) Has been cancelled
Build / Build Windows MSVC (x86, release, x86 release, windows-2022) (push) Has been cancelled
Build / Build Windows MSVC (x86_64, debug, x86_64 debug, windows-2022) (push) Has been cancelled
Build / Build Windows MSVC (x86_64, release, x86_64 release, windows-2022) (push) Has been cancelled
Build / Upload (push) Has been cancelled
Build / Attach to release (push) Has been cancelled
This commit refines the CMake configuration for macOS by finding the Sparkle framework early in the build process, allowing it to be bundled with the application. The Dmg.cmake script is updated to handle Sparkle's framework paths and ensure proper deployment. Additionally, the build_sign_notarize.sh script is improved to sign Sparkle's helper executables correctly and includes enhanced notarization feedback. The Brewfile and install_brew_deps.sh are also updated to include the new macdeploycheck dependency for better deployment checks.
This commit is contained in:
4
Brewfile
4
Brewfile
@@ -12,6 +12,9 @@ brew "cmake"
|
|||||||
brew "pkg-config"
|
brew "pkg-config"
|
||||||
brew "ninja"
|
brew "ninja"
|
||||||
|
|
||||||
|
# Optional (developer): unit tests
|
||||||
|
brew "googletest"
|
||||||
|
|
||||||
# Core runtime/build dependencies (required by CMakeLists.txt)
|
# Core runtime/build dependencies (required by CMakeLists.txt)
|
||||||
brew "qt" # Qt 6 (Core/Gui/Widgets/Network/Sql/Concurrent)
|
brew "qt" # Qt 6 (Core/Gui/Widgets/Network/Sql/Concurrent)
|
||||||
brew "vulkan-headers" # helps Qt6Gui's WrapVulkanHeaders dependency on some setups
|
brew "vulkan-headers" # helps Qt6Gui's WrapVulkanHeaders dependency on some setups
|
||||||
@@ -33,6 +36,7 @@ tap "strawberry/local", "file://#{Dir.pwd}"
|
|||||||
brew "strawberry/local/kdsingleapplication-qt6"
|
brew "strawberry/local/kdsingleapplication-qt6"
|
||||||
brew "strawberry/local/qtsparkle-qt6" # optional: QtSparkle integration
|
brew "strawberry/local/qtsparkle-qt6" # optional: QtSparkle integration
|
||||||
brew "strawberry/local/sparkle-framework" # optional: Sparkle integration (framework)
|
brew "strawberry/local/sparkle-framework" # optional: Sparkle integration (framework)
|
||||||
|
brew "strawberry/local/macdeploycheck" # optional: enables CMake target 'deploycheck' (sanity checks deployed .app)
|
||||||
|
|
||||||
# Recommended GStreamer plugin sets for broad codec support (matches README guidance)
|
# Recommended GStreamer plugin sets for broad codec support (matches README guidance)
|
||||||
brew "gst-plugins-base"
|
brew "gst-plugins-base"
|
||||||
|
|||||||
@@ -32,6 +32,18 @@ if(LINUX)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
|
# Find Sparkle early so cmake/Dmg.cmake (deploy target) can bundle it into the app.
|
||||||
|
# Sparkle is optional; if not found, update functionality is disabled.
|
||||||
|
find_library(SPARKLE Sparkle
|
||||||
|
PATHS
|
||||||
|
/Library/Frameworks
|
||||||
|
/System/Library/Frameworks
|
||||||
|
/opt/homebrew/Frameworks
|
||||||
|
/opt/homebrew/opt/sparkle-framework/Frameworks
|
||||||
|
/usr/local/Frameworks
|
||||||
|
/usr/local/opt/sparkle-framework/Frameworks
|
||||||
|
PATH_SUFFIXES Frameworks
|
||||||
|
)
|
||||||
include(cmake/Dmg.cmake)
|
include(cmake/Dmg.cmake)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@@ -251,21 +263,6 @@ endif()
|
|||||||
|
|
||||||
find_package(KDSingleApplication-qt${QT_VERSION_MAJOR} 1.1.0 REQUIRED)
|
find_package(KDSingleApplication-qt${QT_VERSION_MAJOR} 1.1.0 REQUIRED)
|
||||||
|
|
||||||
if(APPLE)
|
|
||||||
# Sparkle may be installed as a developer framework (e.g. via a package manager).
|
|
||||||
# Help CMake find it by searching typical Homebrew prefix locations as well.
|
|
||||||
find_library(SPARKLE Sparkle
|
|
||||||
PATHS
|
|
||||||
/Library/Frameworks
|
|
||||||
/System/Library/Frameworks
|
|
||||||
/opt/homebrew/Frameworks
|
|
||||||
/opt/homebrew/opt/sparkle-framework/Frameworks
|
|
||||||
/usr/local/Frameworks
|
|
||||||
/usr/local/opt/sparkle-framework/Frameworks
|
|
||||||
PATH_SUFFIXES Frameworks
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
find_package(getopt NAMES getopt getopt-win unofficial-getopt-win32 REQUIRED)
|
find_package(getopt NAMES getopt getopt-win unofficial-getopt-win32 REQUIRED)
|
||||||
if(TARGET getopt::getopt)
|
if(TARGET getopt::getopt)
|
||||||
|
|||||||
93
Formula/macdeploycheck.rb
Normal file
93
Formula/macdeploycheck.rb
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
class Macdeploycheck < Formula
|
||||||
|
desc "Sanity checks a macOS .app bundle for accidental Homebrew runtime dependencies"
|
||||||
|
homepage "https://github.com/strawberrymusicplayer/strawberry"
|
||||||
|
url "file://#{__FILE__}"
|
||||||
|
version "0.1.0"
|
||||||
|
sha256 :no_check
|
||||||
|
license "MIT"
|
||||||
|
|
||||||
|
depends_on :macos
|
||||||
|
|
||||||
|
def install
|
||||||
|
(bin/"macdeploycheck").write <<~'EOS'
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
app="${1:-}"
|
||||||
|
if [[ -z "$app" ]]; then
|
||||||
|
echo "Usage: macdeploycheck <path/to/App.app>" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
if [[ ! -d "$app" ]]; then
|
||||||
|
echo "Error: app bundle not found: $app" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
if [[ ! -d "$app/Contents" ]]; then
|
||||||
|
echo "Error: not a macOS app bundle (missing Contents/): $app" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
fail=0
|
||||||
|
tmp="$(mktemp -t macdeploycheck.XXXXXX)"
|
||||||
|
trap 'rm -f "$tmp"' EXIT
|
||||||
|
|
||||||
|
# Collect Mach-O files (executables + dylibs) inside the bundle.
|
||||||
|
while IFS= read -r -d '' f; do
|
||||||
|
if file "$f" | grep -q "Mach-O"; then
|
||||||
|
echo "$f" >>"$tmp"
|
||||||
|
fi
|
||||||
|
done < <(find "$app/Contents" -type f \( -perm -111 -o -name "*.dylib" -o -name "*.so" \) -print0 2>/dev/null)
|
||||||
|
|
||||||
|
if [[ ! -s "$tmp" ]]; then
|
||||||
|
echo "Warning: no Mach-O files found under $app/Contents" >&2
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "macdeploycheck: scanning for external (Homebrew) runtime deps..."
|
||||||
|
while IFS= read -r f; do
|
||||||
|
# otool -L prints:
|
||||||
|
# <file>:
|
||||||
|
# <dep> (compatibility version ..., current version ...)
|
||||||
|
deps="$(otool -L "$f" 2>/dev/null | tail -n +2 | awk '{print $1}' || true)"
|
||||||
|
while IFS= read -r dep; do
|
||||||
|
[[ -z "$dep" ]] && continue
|
||||||
|
|
||||||
|
# Ignore system and rpath/loader/executable paths.
|
||||||
|
case "$dep" in
|
||||||
|
/System/*|/usr/lib/*|@rpath/*|@loader_path/*|@executable_path/*) continue ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# These are the common accidental runtime deps that will break distribution.
|
||||||
|
if [[ "$dep" == /opt/homebrew/* || "$dep" == /usr/local/* || "$dep" == /opt/local/* ]]; then
|
||||||
|
echo "ERROR: $f links to external path: $dep" >&2
|
||||||
|
fail=1
|
||||||
|
fi
|
||||||
|
done <<<"$deps"
|
||||||
|
done <"$tmp"
|
||||||
|
|
||||||
|
if [[ "$fail" -ne 0 ]]; then
|
||||||
|
cat >&2 <<'EOM'
|
||||||
|
|
||||||
|
One or more binaries in your .app link to a Homebrew (or MacPorts) path.
|
||||||
|
That usually means the bundle is not self-contained and will fail on other machines,
|
||||||
|
or will fail notarization/codesigning validation.
|
||||||
|
|
||||||
|
Fix: re-run your deploy step (e.g. macdeployqt) so frameworks/dylibs are bundled and
|
||||||
|
their install names are rewritten to @rpath/@loader_path.
|
||||||
|
EOM
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "OK: no external Homebrew/MacPorts runtime deps detected."
|
||||||
|
exit 0
|
||||||
|
EOS
|
||||||
|
|
||||||
|
chmod 0755, bin/"macdeploycheck"
|
||||||
|
end
|
||||||
|
|
||||||
|
test do
|
||||||
|
# Basic smoke test: tool runs and prints usage.
|
||||||
|
system bin/"macdeploycheck"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
36
Formula/macdeploycheck/README.md
Normal file
36
Formula/macdeploycheck/README.md
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
# `macdeploycheck` (local Homebrew formula)
|
||||||
|
|
||||||
|
This repository includes a small helper tool called `macdeploycheck`, packaged as a local Homebrew formula.
|
||||||
|
|
||||||
|
## What it does
|
||||||
|
|
||||||
|
`macdeploycheck` scans a built `.app` bundle and flags common **accidental runtime dependencies** on:
|
||||||
|
|
||||||
|
- Homebrew paths like `/opt/homebrew/...` or `/usr/local/...`
|
||||||
|
- MacPorts paths like `/opt/local/...`
|
||||||
|
|
||||||
|
These dependencies usually mean the `.app` is **not self-contained** and may fail to run on other machines or fail notarization validation.
|
||||||
|
|
||||||
|
## Install (via this repo's tap)
|
||||||
|
|
||||||
|
From the Strawberry repo root:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
brew tap strawberry/local "file://$PWD"
|
||||||
|
brew install strawberry/local/macdeploycheck
|
||||||
|
```
|
||||||
|
|
||||||
|
Or use the repo `Brewfile`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
brew bundle --file Brewfile
|
||||||
|
```
|
||||||
|
|
||||||
|
## Use
|
||||||
|
|
||||||
|
```bash
|
||||||
|
macdeploycheck /path/to/Strawberry.app
|
||||||
|
```
|
||||||
|
|
||||||
|
It exits non-zero if it finds external runtime deps.
|
||||||
|
|
||||||
@@ -16,7 +16,8 @@ Common options:
|
|||||||
--run Perform build/sign/notarize (otherwise list identities/profiles)
|
--run Perform build/sign/notarize (otherwise list identities/profiles)
|
||||||
--release | --debug Build config (default: Release)
|
--release | --debug Build config (default: Release)
|
||||||
--clean Clean build dir before build
|
--clean Clean build dir before build
|
||||||
--deploy Run CMake 'deploy' target before signing (recommended for distributing)
|
--deploy Run CMake 'deploy' target before signing (default: on)
|
||||||
|
--no-deploy Do not run 'deploy' (not recommended for distribution)
|
||||||
--build-dir <path> Override build directory
|
--build-dir <path> Override build directory
|
||||||
|
|
||||||
Signing options:
|
Signing options:
|
||||||
@@ -42,8 +43,10 @@ list_identities_and_profiles() {
|
|||||||
security find-identity -p codesigning -v || true
|
security find-identity -p codesigning -v || true
|
||||||
|
|
||||||
echo
|
echo
|
||||||
echo "==> [$(ts)] notarytool profiles (keychain profiles)"
|
echo "==> [$(ts)] notarytool credential profiles"
|
||||||
xcrun notarytool list-profiles 2>/dev/null || echo "(none; create one with: xcrun notarytool store-credentials <name> ...)"
|
echo "Note: this Xcode notarytool version does not provide a 'list-profiles' command."
|
||||||
|
echo "If you forgot the profile name you created, check Keychain Access or re-run:"
|
||||||
|
echo " xcrun notarytool store-credentials \"<profile-name>\" --apple-id \"you@example.com\" --team-id \"TEAMID\""
|
||||||
|
|
||||||
echo
|
echo
|
||||||
echo "==> [$(ts)] Provisioning profiles (macOS)"
|
echo "==> [$(ts)] Provisioning profiles (macOS)"
|
||||||
@@ -69,7 +72,7 @@ fi
|
|||||||
do_run=0
|
do_run=0
|
||||||
config="Release"
|
config="Release"
|
||||||
do_clean=0
|
do_clean=0
|
||||||
do_deploy=0
|
do_deploy=1
|
||||||
build_dir=""
|
build_dir=""
|
||||||
identity=""
|
identity=""
|
||||||
entitlements=""
|
entitlements=""
|
||||||
@@ -83,6 +86,7 @@ while [[ $# -gt 0 ]]; do
|
|||||||
--debug) config="Debug"; shift ;;
|
--debug) config="Debug"; shift ;;
|
||||||
--clean) do_clean=1; shift ;;
|
--clean) do_clean=1; shift ;;
|
||||||
--deploy) do_deploy=1; shift ;;
|
--deploy) do_deploy=1; shift ;;
|
||||||
|
--no-deploy) do_deploy=0; shift ;;
|
||||||
--build-dir) build_dir="${2:-}"; shift 2 ;;
|
--build-dir) build_dir="${2:-}"; shift 2 ;;
|
||||||
--identity) identity="${2:-}"; shift 2 ;;
|
--identity) identity="${2:-}"; shift 2 ;;
|
||||||
--entitlements) entitlements="${2:-}"; shift 2 ;;
|
--entitlements) entitlements="${2:-}"; shift 2 ;;
|
||||||
@@ -139,12 +143,46 @@ if [[ -n "$entitlements" ]]; then
|
|||||||
codesign_args+=( --entitlements "$entitlements" )
|
codesign_args+=( --entitlements "$entitlements" )
|
||||||
fi
|
fi
|
||||||
|
|
||||||
find "$app_path" -type f \( -name "*.dylib" -o -name "*.so" -o -perm -111 \) -print0 | while IFS= read -r -d '' f; do
|
# Sign nested code first, then frameworks, then the main app bundle.
|
||||||
|
#
|
||||||
|
# Important: do NOT codesign individual files *inside* a .framework bundle (e.g. Sparkle.framework/Sparkle),
|
||||||
|
# because codesign expects frameworks to be signed as bundles and may error with
|
||||||
|
# "bundle format is ambiguous (could be app or framework)".
|
||||||
|
|
||||||
|
# 1) Sign dylibs and standalone executables that are NOT inside a .framework/.app/.xpc bundle.
|
||||||
|
find "$app_path" -type f \( -name "*.dylib" -o -name "*.so" -o -perm -111 \) \
|
||||||
|
! -path "*/Contents/Frameworks/*.framework/*" \
|
||||||
|
! -path "*/Contents/Frameworks/*.app/*" \
|
||||||
|
! -path "*/Contents/Frameworks/*.xpc/*" \
|
||||||
|
! -path "*/Contents/PlugIns/*.framework/*" \
|
||||||
|
! -path "*/Contents/PlugIns/*.app/*" \
|
||||||
|
! -path "*/Contents/PlugIns/*.xpc/*" \
|
||||||
|
-print0 | while IFS= read -r -d '' f; do
|
||||||
codesign "${codesign_args[@]}" "$f" >/dev/null
|
codesign "${codesign_args[@]}" "$f" >/dev/null
|
||||||
done
|
done
|
||||||
|
|
||||||
|
# 2) Sign nested helper apps and XPC services (Sparkle ships these inside its framework).
|
||||||
|
find "$app_path" -type d \( -name "*.xpc" -o -name "*.app" \) -print0 2>/dev/null | while IFS= read -r -d '' b; do
|
||||||
|
codesign "${codesign_args[@]}" "$b" >/dev/null
|
||||||
|
done
|
||||||
|
|
||||||
|
# 2b) Sparkle.framework contains a standalone helper executable "Autoupdate" under Versions/* that is
|
||||||
|
# not inside an .app or .xpc bundle. Notarization requires it be signed with Developer ID + timestamp.
|
||||||
|
sparkle_fw="$app_path/Contents/Frameworks/Sparkle.framework"
|
||||||
|
if [[ -d "$sparkle_fw" ]]; then
|
||||||
|
find "$sparkle_fw/Versions" -type f -perm -111 \
|
||||||
|
! -path "*/_CodeSignature/*" \
|
||||||
|
-print0 2>/dev/null | while IFS= read -r -d '' f; do
|
||||||
|
codesign "${codesign_args[@]}" "$f" >/dev/null
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 3) Sign frameworks as bundles.
|
||||||
find "$app_path/Contents/Frameworks" "$app_path/Contents/PlugIns" -type d -name "*.framework" -print0 2>/dev/null | while IFS= read -r -d '' fw; do
|
find "$app_path/Contents/Frameworks" "$app_path/Contents/PlugIns" -type d -name "*.framework" -print0 2>/dev/null | while IFS= read -r -d '' fw; do
|
||||||
codesign "${codesign_args[@]}" "$fw" >/dev/null
|
codesign "${codesign_args[@]}" "$fw" >/dev/null
|
||||||
done
|
done
|
||||||
|
|
||||||
|
# 4) Finally sign the main app.
|
||||||
codesign "${codesign_args[@]}" "$app_path" >/dev/null
|
codesign "${codesign_args[@]}" "$app_path" >/dev/null
|
||||||
|
|
||||||
echo "==> [$(ts)] Verifying codesign"
|
echo "==> [$(ts)] Verifying codesign"
|
||||||
@@ -156,7 +194,25 @@ ditto -c -k --sequesterRsrc --keepParent "$app_path" "$zip_path"
|
|||||||
|
|
||||||
if [[ "$skip_notarize" -eq 0 ]]; then
|
if [[ "$skip_notarize" -eq 0 ]]; then
|
||||||
echo "==> [$(ts)] Notarizing"
|
echo "==> [$(ts)] Notarizing"
|
||||||
xcrun notarytool submit "$zip_path" --keychain-profile "$notary_profile" --wait
|
# Use JSON output so we can reliably detect Invalid and fetch logs.
|
||||||
|
submit_json="$(xcrun notarytool submit "$zip_path" --keychain-profile "$notary_profile" --wait --output-format json --no-progress)"
|
||||||
|
submit_id="$(python3 -c 'import json,sys; print(json.load(sys.stdin).get("id",""))' <<<"$submit_json" 2>/dev/null || true)"
|
||||||
|
submit_status="$(python3 -c 'import json,sys; print(json.load(sys.stdin).get("status",""))' <<<"$submit_json" 2>/dev/null || true)"
|
||||||
|
|
||||||
|
if [[ -z "$submit_id" ]]; then
|
||||||
|
echo "Error: could not parse notarization submission id. Raw output:" >&2
|
||||||
|
echo "$submit_json" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "==> [$(ts)] Notary submission id: $submit_id"
|
||||||
|
echo "==> [$(ts)] Notary status: $submit_status"
|
||||||
|
|
||||||
|
if [[ "$submit_status" != "Accepted" ]]; then
|
||||||
|
echo "Error: notarization failed with status '$submit_status'. Fetching log..." >&2
|
||||||
|
xcrun notarytool log "$submit_id" --keychain-profile "$notary_profile" || true
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
echo "==> [$(ts)] Stapling"
|
echo "==> [$(ts)] Stapling"
|
||||||
xcrun stapler staple "$app_path"
|
xcrun stapler staple "$app_path"
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ run_with_heartbeat "Refreshing strawberry/local tap clone" bash -lc '
|
|||||||
git reset --hard -q "$default_ref"
|
git reset --hard -q "$default_ref"
|
||||||
'
|
'
|
||||||
|
|
||||||
for f in kdsingleapplication-qt6 qtsparkle-qt6 sparkle-framework libgpod; do
|
for f in kdsingleapplication-qt6 qtsparkle-qt6 sparkle-framework libgpod macdeploycheck; do
|
||||||
if ! brew info "strawberry/local/${f}" >/dev/null 2>&1; then
|
if ! brew info "strawberry/local/${f}" >/dev/null 2>&1; then
|
||||||
echo "Error: Missing formula strawberry/local/${f} in the tapped repo." >&2
|
echo "Error: Missing formula strawberry/local/${f} in the tapped repo." >&2
|
||||||
echo "If you recently added/changed formulae, ensure they are committed, then refresh the tap:" >&2
|
echo "If you recently added/changed formulae, ensure they are committed, then refresh the tap:" >&2
|
||||||
|
|||||||
@@ -21,6 +21,36 @@ else()
|
|||||||
message(WARNING "Missing create-dmg executable.")
|
message(WARNING "Missing create-dmg executable.")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
set(_SPARKLE_FRAMEWORK_DIR "")
|
||||||
|
set(_SPARKLE_ORIGINAL_BIN_LINK "")
|
||||||
|
set(_SPARKLE_ORIGINAL_BIN_REAL "")
|
||||||
|
if(SPARKLE)
|
||||||
|
# SPARKLE may be either the framework directory or the framework binary path.
|
||||||
|
get_filename_component(_sparkle_link "${SPARKLE}" ABSOLUTE)
|
||||||
|
get_filename_component(_sparkle_real "${SPARKLE}" REALPATH)
|
||||||
|
if(_sparkle_link MATCHES "Sparkle\\.framework$")
|
||||||
|
set(_SPARKLE_FRAMEWORK_DIR "${_sparkle_real}")
|
||||||
|
set(_SPARKLE_ORIGINAL_BIN_LINK "${_sparkle_link}/Versions/B/Sparkle")
|
||||||
|
set(_SPARKLE_ORIGINAL_BIN_REAL "${_sparkle_real}/Versions/B/Sparkle")
|
||||||
|
else()
|
||||||
|
# Assume it's the framework binary path:
|
||||||
|
# .../Sparkle.framework/Versions/B/Sparkle
|
||||||
|
set(_SPARKLE_ORIGINAL_BIN_LINK "${_sparkle_link}")
|
||||||
|
set(_SPARKLE_ORIGINAL_BIN_REAL "${_sparkle_real}")
|
||||||
|
get_filename_component(_sparkle_b_dir "${_SPARKLE_ORIGINAL_BIN_REAL}" DIRECTORY) # .../Versions/B
|
||||||
|
get_filename_component(_sparkle_versions_dir "${_sparkle_b_dir}" DIRECTORY) # .../Versions
|
||||||
|
get_filename_component(_SPARKLE_FRAMEWORK_DIR "${_sparkle_versions_dir}" DIRECTORY) # .../Sparkle.framework
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT EXISTS "${_SPARKLE_FRAMEWORK_DIR}" OR NOT EXISTS "${_SPARKLE_ORIGINAL_BIN_REAL}")
|
||||||
|
set(_SPARKLE_FRAMEWORK_DIR "")
|
||||||
|
set(_SPARKLE_ORIGINAL_BIN_LINK "")
|
||||||
|
set(_SPARKLE_ORIGINAL_BIN_REAL "")
|
||||||
|
else()
|
||||||
|
message(STATUS "Sparkle.framework found: ${_SPARKLE_FRAMEWORK_DIR}")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
if(MACDEPLOYQT_EXECUTABLE)
|
if(MACDEPLOYQT_EXECUTABLE)
|
||||||
|
|
||||||
if(APPLE_DEVELOPER_ID)
|
if(APPLE_DEVELOPER_ID)
|
||||||
@@ -31,12 +61,26 @@ if(MACDEPLOYQT_EXECUTABLE)
|
|||||||
set(CREATEDMG_SKIP_JENKINS_ARG "--skip-jenkins")
|
set(CREATEDMG_SKIP_JENKINS_ARG "--skip-jenkins")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_custom_target(deploy
|
set(_deploy_commands
|
||||||
COMMAND mkdir -p ${CMAKE_BINARY_DIR}/strawberry.app/Contents/{Frameworks,Resources}
|
COMMAND mkdir -p ${CMAKE_BINARY_DIR}/strawberry.app/Contents/Frameworks
|
||||||
|
COMMAND mkdir -p ${CMAKE_BINARY_DIR}/strawberry.app/Contents/Resources
|
||||||
COMMAND cp -v ${CMAKE_BINARY_DIR}/dist/macos/Info.plist ${CMAKE_BINARY_DIR}/strawberry.app/Contents/
|
COMMAND cp -v ${CMAKE_BINARY_DIR}/dist/macos/Info.plist ${CMAKE_BINARY_DIR}/strawberry.app/Contents/
|
||||||
COMMAND cp -v ${CMAKE_SOURCE_DIR}/dist/macos/strawberry.icns ${CMAKE_BINARY_DIR}/strawberry.app/Contents/Resources/
|
COMMAND cp -v ${CMAKE_SOURCE_DIR}/dist/macos/strawberry.icns ${CMAKE_BINARY_DIR}/strawberry.app/Contents/Resources/
|
||||||
|
)
|
||||||
|
|
||||||
|
if(_SPARKLE_FRAMEWORK_DIR)
|
||||||
|
list(APPEND _deploy_commands
|
||||||
|
COMMAND ${CMAKE_SOURCE_DIR}/dist/macos/bundle_sparkle.sh ${CMAKE_BINARY_DIR}/strawberry.app ${_SPARKLE_FRAMEWORK_DIR} ${_SPARKLE_ORIGINAL_BIN_LINK} ${_SPARKLE_ORIGINAL_BIN_REAL}
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
list(APPEND _deploy_commands
|
||||||
COMMAND ${CMAKE_SOURCE_DIR}/dist/macos/macgstcopy.sh ${CMAKE_BINARY_DIR}/strawberry.app
|
COMMAND ${CMAKE_SOURCE_DIR}/dist/macos/macgstcopy.sh ${CMAKE_BINARY_DIR}/strawberry.app
|
||||||
COMMAND ${MACDEPLOYQT_EXECUTABLE} strawberry.app -verbose=3 -executable=${CMAKE_BINARY_DIR}/strawberry.app/Contents/PlugIns/gst-plugin-scanner ${MACDEPLOYQT_CODESIGN}
|
COMMAND ${MACDEPLOYQT_EXECUTABLE} strawberry.app -verbose=3 -executable=${CMAKE_BINARY_DIR}/strawberry.app/Contents/PlugIns/gst-plugin-scanner ${MACDEPLOYQT_CODESIGN}
|
||||||
|
)
|
||||||
|
|
||||||
|
add_custom_target(deploy
|
||||||
|
${_deploy_commands}
|
||||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||||
DEPENDS strawberry
|
DEPENDS strawberry
|
||||||
)
|
)
|
||||||
|
|||||||
85
dist/macos/bundle_sparkle.sh
vendored
Executable file
85
dist/macos/bundle_sparkle.sh
vendored
Executable file
@@ -0,0 +1,85 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
bundledir="${1:-}"
|
||||||
|
sparkle_framework_dir="${2:-}"
|
||||||
|
sparkle_bin_link="${3:-}"
|
||||||
|
sparkle_bin_real="${4:-}"
|
||||||
|
|
||||||
|
if [[ -z "$bundledir" || -z "$sparkle_framework_dir" ]]; then
|
||||||
|
echo "Usage: $0 <bundledir> <sparkle_framework_dir> [sparkle_bin_link] [sparkle_bin_real]" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -d "$sparkle_framework_dir" ]]; then
|
||||||
|
echo "Sparkle.framework dir not found: $sparkle_framework_dir" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
src_framework_dir="$sparkle_framework_dir"
|
||||||
|
|
||||||
|
# Homebrew often provides /opt/homebrew/Frameworks/Sparkle.framework where Versions/* are symlinks
|
||||||
|
# pointing back into the Cellar. Copying that verbatim breaks inside an app bundle.
|
||||||
|
# Resolve to the real Cellar framework root via Versions/Current.
|
||||||
|
if [[ -e "${sparkle_framework_dir}/Versions/Current" ]]; then
|
||||||
|
current_real="$(cd "${sparkle_framework_dir}/Versions/Current" && pwd -P)"
|
||||||
|
# current_real is .../Sparkle.framework/Versions/B (or similar)
|
||||||
|
src_framework_dir="$(cd "${current_real}/../.." && pwd -P)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
dst_framework="${bundledir}/Contents/Frameworks/Sparkle.framework"
|
||||||
|
main_bin="${bundledir}/Contents/MacOS/strawberry"
|
||||||
|
qtsparkle_dylib="${bundledir}/Contents/Frameworks/libqtsparkle-qt6.dylib"
|
||||||
|
|
||||||
|
mkdir -p "${bundledir}/Contents/Frameworks"
|
||||||
|
|
||||||
|
echo "Bundling Sparkle.framework -> ${dst_framework}"
|
||||||
|
rm -rf "${dst_framework}"
|
||||||
|
# Use ditto to preserve the framework's internal symlinks/structure.
|
||||||
|
ditto "${src_framework_dir}" "${dst_framework}"
|
||||||
|
|
||||||
|
# Prefer the canonical framework binary path.
|
||||||
|
dst_bin="${dst_framework}/Versions/Current/Sparkle"
|
||||||
|
if [[ ! -e "${dst_bin}" ]]; then
|
||||||
|
echo "Error: Sparkle binary missing at ${dst_bin}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
sparkle_rpath="@rpath/Sparkle.framework/Versions/Current/Sparkle"
|
||||||
|
|
||||||
|
# Sanity check: top-level Sparkle entry should be a symlink (not a copied Mach-O file).
|
||||||
|
if [[ -e "${dst_framework}/Sparkle" && ! -L "${dst_framework}/Sparkle" ]]; then
|
||||||
|
echo "Warning: ${dst_framework}/Sparkle is not a symlink (unexpected). This can confuse codesign." >&2
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Fixing Sparkle.framework install name"
|
||||||
|
install_name_tool -id "${sparkle_rpath}" "${dst_bin}"
|
||||||
|
|
||||||
|
echo "Ensuring main binary has Frameworks rpath"
|
||||||
|
install_name_tool -add_rpath "@executable_path/../Frameworks" "${main_bin}" || true
|
||||||
|
|
||||||
|
echo "Rewriting Sparkle.framework references to @rpath"
|
||||||
|
# Try to rewrite a few common Homebrew Sparkle install names as well, because the
|
||||||
|
# recorded install name may differ from the path returned by CMake's find_library.
|
||||||
|
old_candidates=(
|
||||||
|
"${sparkle_bin_link}"
|
||||||
|
"${sparkle_bin_real}"
|
||||||
|
"/opt/homebrew/opt/sparkle-framework/Frameworks/Sparkle.framework/Versions/A/Sparkle"
|
||||||
|
"/opt/homebrew/opt/sparkle-framework/Frameworks/Sparkle.framework/Versions/B/Sparkle"
|
||||||
|
"/opt/homebrew/Frameworks/Sparkle.framework/Versions/A/Sparkle"
|
||||||
|
"/opt/homebrew/Frameworks/Sparkle.framework/Versions/B/Sparkle"
|
||||||
|
"/usr/local/opt/sparkle-framework/Frameworks/Sparkle.framework/Versions/A/Sparkle"
|
||||||
|
"/usr/local/opt/sparkle-framework/Frameworks/Sparkle.framework/Versions/B/Sparkle"
|
||||||
|
"/usr/local/Frameworks/Sparkle.framework/Versions/A/Sparkle"
|
||||||
|
"/usr/local/Frameworks/Sparkle.framework/Versions/B/Sparkle"
|
||||||
|
)
|
||||||
|
|
||||||
|
for old in "${old_candidates[@]}"; do
|
||||||
|
if [[ -n "${old}" ]]; then
|
||||||
|
install_name_tool -change "${old}" "${sparkle_rpath}" "${main_bin}" || true
|
||||||
|
if [[ -f "${qtsparkle_dylib}" ]]; then
|
||||||
|
install_name_tool -change "${old}" "${sparkle_rpath}" "${qtsparkle_dylib}" || true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
Reference in New Issue
Block a user