mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 19:45:09 +00:00
add other folder
This commit is contained in:
Executable
+249
@@ -0,0 +1,249 @@
|
||||
#!/usr/bin/env bash
|
||||
# Creates two self-contained macOS .app bundles:
|
||||
# EWSDR.app — normal launch
|
||||
# EWSDR_opengl.app — launches with --opengl flag via wrapper script
|
||||
#
|
||||
# Usage: ./bundle_app.sh [path/to/Source.app] [output/dir]
|
||||
# If no arguments given, auto-detects the .app in bin/aarch64-darwin/
|
||||
#
|
||||
# Skips system libs: /System/, /usr/lib/, @executable_path, @loader_path, @rpath
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
|
||||
# ── Locate source .app ────────────────────────────────────────────────────────
|
||||
if [[ $# -ge 1 ]]; then
|
||||
SRC_APP="$(cd "$(dirname "$1")" && pwd)/$(basename "$1")"
|
||||
else
|
||||
SRC_APP="$(find "$SCRIPT_DIR/bin" -maxdepth 3 -name "*.app" | head -1)"
|
||||
[[ -n "$SRC_APP" ]] || { echo "ERROR: no .app found under bin/"; exit 1; }
|
||||
fi
|
||||
|
||||
# ── Output directory ──────────────────────────────────────────────────────────
|
||||
if [[ $# -ge 2 ]]; then
|
||||
OUT_DIR="$2"
|
||||
else
|
||||
OUT_DIR="$SCRIPT_DIR/dist"
|
||||
fi
|
||||
|
||||
mkdir -p "$OUT_DIR"
|
||||
|
||||
# ── Generate .icns from icon/ folder ─────────────────────────────────────────
|
||||
ICNS_PATH=""
|
||||
ICON_SRC="$SCRIPT_DIR/icon"
|
||||
if [[ -d "$ICON_SRC" ]]; then
|
||||
echo "Generating EWSDR.icns..."
|
||||
ICONSET_TMP="$(mktemp -d)/EWSDR.iconset"
|
||||
mkdir -p "$ICONSET_TMP"
|
||||
|
||||
# iconset naming: icon_WxH.png and icon_WxH@2x.png (where @2x = double pixels)
|
||||
[[ -f "$ICON_SRC/icon16.png" ]] && cp "$ICON_SRC/icon16.png" "$ICONSET_TMP/icon_16x16.png"
|
||||
[[ -f "$ICON_SRC/icon32.png" ]] && cp "$ICON_SRC/icon32.png" "$ICONSET_TMP/icon_16x16@2x.png"
|
||||
[[ -f "$ICON_SRC/icon32.png" ]] && cp "$ICON_SRC/icon32.png" "$ICONSET_TMP/icon_32x32.png"
|
||||
[[ -f "$ICON_SRC/icon64.png" ]] && cp "$ICON_SRC/icon64.png" "$ICONSET_TMP/icon_32x32@2x.png"
|
||||
[[ -f "$ICON_SRC/icon128.png" ]] && cp "$ICON_SRC/icon128.png" "$ICONSET_TMP/icon_128x128.png"
|
||||
[[ -f "$ICON_SRC/icon256.png" ]] && cp "$ICON_SRC/icon256.png" "$ICONSET_TMP/icon_128x128@2x.png"
|
||||
[[ -f "$ICON_SRC/icon256.png" ]] && cp "$ICON_SRC/icon256.png" "$ICONSET_TMP/icon_256x256.png"
|
||||
if [[ -f "$ICON_SRC/icon.png" ]]; then
|
||||
sips -z 512 512 "$ICON_SRC/icon.png" --out "$ICONSET_TMP/icon_256x256@2x.png" >/dev/null
|
||||
sips -z 512 512 "$ICON_SRC/icon.png" --out "$ICONSET_TMP/icon_512x512.png" >/dev/null
|
||||
cp "$ICON_SRC/icon.png" "$ICONSET_TMP/icon_512x512@2x.png"
|
||||
fi
|
||||
|
||||
ICNS_PATH="$OUT_DIR/EWSDR.icns"
|
||||
iconutil -c icns "$ICONSET_TMP" -o "$ICNS_PATH"
|
||||
rm -rf "$(dirname "$ICONSET_TMP")"
|
||||
echo " -> $ICNS_PATH"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# ── Resolve source executable name ───────────────────────────────────────────
|
||||
SRC_EXEC_NAME="$(defaults read "$SRC_APP/Contents/Info" CFBundleExecutable 2>/dev/null || true)"
|
||||
[[ -z "$SRC_EXEC_NAME" ]] && SRC_EXEC_NAME="$(basename "$SRC_APP" .app)"
|
||||
|
||||
# Resolve the real binary path (Lazarus may place it alongside the .app)
|
||||
SRC_BIN=""
|
||||
CANDIDATE="$(dirname "$SRC_APP")/$SRC_EXEC_NAME"
|
||||
if [[ -f "$CANDIDATE" ]]; then
|
||||
SRC_BIN="$CANDIDATE"
|
||||
fi
|
||||
if [[ -z "$SRC_BIN" ]]; then
|
||||
SRC_EXEC="$SRC_APP/Contents/MacOS/$SRC_EXEC_NAME"
|
||||
if [[ -L "$SRC_EXEC" ]]; then
|
||||
RAW="$(readlink "$SRC_EXEC")"
|
||||
if [[ "$RAW" == /* ]]; then
|
||||
SRC_BIN="$RAW"
|
||||
else
|
||||
SRC_BIN="$(cd "$(dirname "$SRC_EXEC")" && cd "$(dirname "$RAW")" && pwd)/$(basename "$RAW")"
|
||||
fi
|
||||
elif [[ -f "$SRC_EXEC" ]]; then
|
||||
SRC_BIN="$SRC_EXEC"
|
||||
fi
|
||||
fi
|
||||
[[ -f "$SRC_BIN" ]] || { echo "ERROR: executable not found for $SRC_EXEC_NAME"; exit 1; }
|
||||
|
||||
# ── Helpers ───────────────────────────────────────────────────────────────────
|
||||
|
||||
is_system_lib() {
|
||||
local path="$1"
|
||||
[[ "$path" == /System/* ]] && return 0
|
||||
[[ "$path" == /usr/lib/* ]] && return 0
|
||||
[[ "$path" == @executable_path/* ]] && return 0
|
||||
[[ "$path" == @loader_path/* ]] && return 0
|
||||
[[ "$path" == @rpath/* ]] && return 0
|
||||
return 1
|
||||
}
|
||||
|
||||
# ── Build a self-contained .app bundle ───────────────────────────────────────
|
||||
# Args: <out_app_path> <CFBundleName> <CFBundleIdentifier>
|
||||
build_bundle() {
|
||||
local OUT_APP="$1"
|
||||
local BUNDLE_NAME="$2"
|
||||
local BUNDLE_ID="$3"
|
||||
local FWK_DIR="$OUT_APP/Contents/Frameworks"
|
||||
|
||||
echo "==> Building $OUT_APP"
|
||||
|
||||
rm -rf "$OUT_APP"
|
||||
cp -R "$SRC_APP" "$OUT_APP"
|
||||
mkdir -p "$FWK_DIR"
|
||||
rm -f "$OUT_APP/Contents/MacOS/wdspWisdom"*
|
||||
|
||||
# Install the real binary (replace symlink left by Lazarus)
|
||||
local EXEC_PATH="$OUT_APP/Contents/MacOS/$SRC_EXEC_NAME"
|
||||
rm -f "$EXEC_PATH"
|
||||
cp "$SRC_BIN" "$EXEC_PATH"
|
||||
chmod +x "$EXEC_PATH"
|
||||
|
||||
# Update bundle metadata
|
||||
/usr/libexec/PlistBuddy -c "Set :CFBundleName $BUNDLE_NAME" \
|
||||
"$OUT_APP/Contents/Info.plist"
|
||||
/usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier $BUNDLE_ID" \
|
||||
"$OUT_APP/Contents/Info.plist"
|
||||
|
||||
# Install icon
|
||||
if [[ -n "${ICNS_PATH:-}" && -f "$ICNS_PATH" ]]; then
|
||||
mkdir -p "$OUT_APP/Contents/Resources"
|
||||
cp "$ICNS_PATH" "$OUT_APP/Contents/Resources/EWSDR.icns"
|
||||
/usr/libexec/PlistBuddy -c "Set :CFBundleIconFile EWSDR" \
|
||||
"$OUT_APP/Contents/Info.plist" 2>/dev/null || \
|
||||
/usr/libexec/PlistBuddy -c "Add :CFBundleIconFile string EWSDR" \
|
||||
"$OUT_APP/Contents/Info.plist"
|
||||
fi
|
||||
|
||||
# Recursive dylib collector
|
||||
declare -A PROCESSED=()
|
||||
|
||||
collect_and_copy() {
|
||||
local binary="$1"
|
||||
local deps
|
||||
deps="$(otool -L "$binary" | tail -n +2 | awk '{print $1}')"
|
||||
|
||||
while IFS= read -r dep; do
|
||||
[[ -z "$dep" ]] && continue
|
||||
is_system_lib "$dep" && continue
|
||||
local libname
|
||||
libname="$(basename "$dep")"
|
||||
local dst="$FWK_DIR/$libname"
|
||||
|
||||
if [[ -f "$dst" || -f "$dep" ]]; then
|
||||
install_name_tool -change "$dep" \
|
||||
"@executable_path/../Frameworks/$libname" \
|
||||
"$binary" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
[[ -n "${PROCESSED[$libname]:-}" ]] && continue
|
||||
|
||||
if [[ ! -f "$dep" ]]; then
|
||||
echo " WARNING: $dep not found, skipping"
|
||||
continue
|
||||
fi
|
||||
|
||||
echo " Bundling $libname"
|
||||
cp "$dep" "$dst"
|
||||
chmod 755 "$dst"
|
||||
PROCESSED[$libname]=1
|
||||
install_name_tool -id "@executable_path/../Frameworks/$libname" "$dst"
|
||||
collect_and_copy "$dst"
|
||||
done <<< "$deps"
|
||||
}
|
||||
|
||||
echo " Scanning dependencies..."
|
||||
collect_and_copy "$EXEC_PATH"
|
||||
|
||||
while IFS= read -r existing; do
|
||||
local libname
|
||||
libname="$(basename "$existing")"
|
||||
[[ -n "${PROCESSED[$libname]:-}" ]] && continue
|
||||
echo " Re-scanning bundled $libname"
|
||||
PROCESSED[$libname]=1
|
||||
collect_and_copy "$existing"
|
||||
done < <(find "$FWK_DIR" -name "*.dylib" -type f)
|
||||
|
||||
# Verify
|
||||
local BAD=0
|
||||
while IFS= read -r binary; do
|
||||
while IFS= read -r dep; do
|
||||
[[ -z "$dep" ]] && continue
|
||||
is_system_lib "$dep" && continue
|
||||
echo " UNRESOLVED in $(basename "$binary"): $dep"
|
||||
BAD=1
|
||||
done < <(otool -L "$binary" | tail -n +2 | awk '{print $1}')
|
||||
done < <(find "$OUT_APP" -type f \( -name "*.dylib" -o -perm +0111 \))
|
||||
[[ $BAD -ne 0 ]] && echo " WARNING: some dependencies are still unresolved."
|
||||
|
||||
# Sign
|
||||
echo " Signing (ad-hoc)..."
|
||||
find "$FWK_DIR" -name "*.dylib" -type f | while IFS= read -r lib; do
|
||||
codesign --sign - --force "$lib" 2>/dev/null || true
|
||||
done
|
||||
codesign --sign - --force --deep "$OUT_APP"
|
||||
|
||||
echo " Done: $OUT_APP"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# ── EWSDR.app ─────────────────────────────────────────────────────────────────
|
||||
echo "Source: $SRC_APP ($SRC_BIN)"
|
||||
echo ""
|
||||
|
||||
build_bundle "$OUT_DIR/EWSDR.app" "EWSDR" "com.company.ewsdr"
|
||||
|
||||
# ── EWSDR_opengl.app ──────────────────────────────────────────────────────────
|
||||
OPENGL_APP="$OUT_DIR/EWSDR_opengl.app"
|
||||
echo "==> Building $OPENGL_APP"
|
||||
|
||||
rm -rf "$OPENGL_APP"
|
||||
cp -R "$OUT_DIR/EWSDR.app" "$OPENGL_APP"
|
||||
|
||||
# Update metadata
|
||||
/usr/libexec/PlistBuddy -c "Set :CFBundleName EWSDR OpenGL" \
|
||||
"$OPENGL_APP/Contents/Info.plist"
|
||||
/usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier com.company.ewsdr-opengl" \
|
||||
"$OPENGL_APP/Contents/Info.plist"
|
||||
/usr/libexec/PlistBuddy -c "Set :CFBundleExecutable EWSDR_opengl" \
|
||||
"$OPENGL_APP/Contents/Info.plist"
|
||||
|
||||
# Wrapper script that passes --opengl to the real binary
|
||||
cat > "$OPENGL_APP/Contents/MacOS/EWSDR_opengl" << EOF
|
||||
#!/usr/bin/env bash
|
||||
DIR="\$(cd "\$(dirname "\$0")" && pwd)"
|
||||
exec "\$DIR/$SRC_EXEC_NAME" --opengl "\$@"
|
||||
EOF
|
||||
chmod +x "$OPENGL_APP/Contents/MacOS/EWSDR_opengl"
|
||||
|
||||
# Re-sign with updated contents
|
||||
echo " Signing (ad-hoc)..."
|
||||
find "$OPENGL_APP/Contents/Frameworks" -name "*.dylib" -type f | while IFS= read -r lib; do
|
||||
codesign --sign - --force "$lib" 2>/dev/null || true
|
||||
done
|
||||
codesign --sign - --force --deep "$OPENGL_APP"
|
||||
|
||||
echo " Done: $OPENGL_APP"
|
||||
echo ""
|
||||
echo "All bundles ready in $OUT_DIR/"
|
||||
|
||||
# ── Clean up dist artifacts that are not part of the bundles ─────────────────
|
||||
rm -f "$ICNS_PATH"
|
||||
find "$OUT_DIR" -maxdepth 1 -name ".DS_Store" -delete
|
||||
Reference in New Issue
Block a user