mirror of
https://git.quad4.io/RNS-Things/MeshChatX.git
synced 2026-05-11 09:36:54 +00:00
feat(build): add script to thin Mach-O binaries and improve macOS universal build process
This commit is contained in:
@@ -2,6 +2,14 @@
|
||||
# Build darwin-arm64 and darwin-x64 cx_Freeze backends, then electron-builder --mac --universal.
|
||||
# On Apple Silicon, the x64 backend must be built with an x86_64 Python (e.g. Homebrew in /usr/local).
|
||||
# Set PYTHON_CMD_X64 to that interpreter if Poetry's default env is arm64-only.
|
||||
#
|
||||
# Optional env vars:
|
||||
# MESHCHATX_MAC_UNIVERSAL_STRIP_AUDIO=1 Drop _miniaudio.abi3.so from both per-arch
|
||||
# backend trees before lipo. Use this when a
|
||||
# universal2 miniaudio wheel cannot be coerced
|
||||
# into a single-arch build on a given runner.
|
||||
# Audio decode falls back to wave + LXST/pyogg.
|
||||
# MESHCHATX_FRONTEND_PREBUILT=1 Reuse meshchatx/public/ instead of rebuilding.
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
@@ -46,12 +54,8 @@ else
|
||||
cross-env ARCH=x64 pnpm run build-backend
|
||||
fi
|
||||
|
||||
# @electron/universal v2.x checks SHA equality for every non-Mach-O file
|
||||
# and throws if any differ (no x64ArchFiles escape for PLAIN files).
|
||||
# cx_Freeze's library.zip contains only architecture-independent Python
|
||||
# bytecode; the per-arch native extensions (.dylib/.so) live outside the
|
||||
# zip. The two zips differ solely in .pyc header timestamps and zip
|
||||
# metadata, so copying one over the other is safe and makes the merge pass.
|
||||
bash scripts/thin-backend-mach-o.sh
|
||||
|
||||
bash scripts/unify-backend-plain-files.sh
|
||||
|
||||
exec pnpm exec electron-builder --mac --universal --publish=never
|
||||
|
||||
@@ -32,11 +32,56 @@ python -m poetry check --lock
|
||||
python -m poetry install --no-interaction --no-ansi
|
||||
python -m poetry run python scripts/patch_lxst_pyogg_ogg_ctypes.py
|
||||
|
||||
# Python 3.14 may install miniaudio from sdist; a mis-linked x86_64-only
|
||||
# _miniaudio.abi3.so in the arm64 cx_Freeze tree differs from the x64 slice and
|
||||
# breaks @electron/universal (lipo cannot merge two x86_64-only Mach-O files).
|
||||
if [[ "$(uname -s)" == "Darwin" && "$(uname -m)" == "arm64" ]]; then
|
||||
if ! poetry run python -c "
|
||||
if [[ "$(uname -s)" == "Darwin" ]]; then
|
||||
if poetry run python -c "import platform, sys; sys.exit(0 if platform.machine() == 'arm64' else 1)"; then
|
||||
_miniaudio_state="$(poetry run python -c "
|
||||
import importlib.util
|
||||
import pathlib
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
spec = importlib.util.find_spec('miniaudio')
|
||||
if not spec or not spec.origin:
|
||||
print('missing')
|
||||
sys.exit(0)
|
||||
so = pathlib.Path(spec.origin).resolve().parent / '_miniaudio.abi3.so'
|
||||
if not so.is_file():
|
||||
print('missing')
|
||||
sys.exit(0)
|
||||
out = subprocess.check_output(['file', str(so)], text=True)
|
||||
has_arm = 'arm64' in out
|
||||
has_x86 = 'x86_64' in out
|
||||
if not has_arm and has_x86:
|
||||
print('x86only')
|
||||
elif has_arm and has_x86:
|
||||
print('universal')
|
||||
elif has_arm and not has_x86:
|
||||
print('arm64only')
|
||||
else:
|
||||
print('unknown')
|
||||
" 2>/dev/null || echo "missing")"
|
||||
case "$_miniaudio_state" in
|
||||
x86only)
|
||||
echo "miniaudio _miniaudio.abi3.so is x86_64-only on arm64 venv; rebuilding from source." >&2
|
||||
_need_rebuild=1
|
||||
;;
|
||||
universal)
|
||||
echo "miniaudio _miniaudio.abi3.so is universal2; rebuilding as arm64-only so @electron/universal can lipo with the x64 slice." >&2
|
||||
_need_rebuild=1
|
||||
;;
|
||||
*)
|
||||
_need_rebuild=0
|
||||
;;
|
||||
esac
|
||||
if [[ "${_need_rebuild:-0}" == "1" ]]; then
|
||||
(
|
||||
export ARCHFLAGS="-arch arm64"
|
||||
export CFLAGS="-arch arm64"
|
||||
export CXXFLAGS="-arch arm64"
|
||||
poetry run python -m pip install --force-reinstall --no-cache-dir --no-binary miniaudio "miniaudio>=1.70,<2"
|
||||
)
|
||||
fi
|
||||
if ! poetry run python -c "
|
||||
import importlib.util
|
||||
import pathlib
|
||||
import subprocess
|
||||
@@ -49,17 +94,19 @@ so = pathlib.Path(spec.origin).resolve().parent / '_miniaudio.abi3.so'
|
||||
if not so.is_file():
|
||||
sys.exit(0)
|
||||
out = subprocess.check_output(['file', str(so)], text=True)
|
||||
if 'x86_64' in out and 'arm64' not in out:
|
||||
if 'arm64' not in out:
|
||||
sys.stderr.write(out)
|
||||
sys.exit(1)
|
||||
sys.exit(0)
|
||||
"; then
|
||||
echo "Rebuilding miniaudio for arm64 (was x86_64-only)." >&2
|
||||
(
|
||||
export ARCHFLAGS="-arch arm64"
|
||||
export CFLAGS="-arch arm64 ${CFLAGS:-}"
|
||||
export CXXFLAGS="-arch arm64 ${CXXFLAGS:-}"
|
||||
poetry run python -m pip install --force-reinstall --no-cache-dir --no-binary miniaudio "miniaudio>=1.70,<2"
|
||||
)
|
||||
if [[ "${MESHCHATX_MAC_UNIVERSAL_STRIP_AUDIO:-0}" == "1" ]]; then
|
||||
echo "miniaudio native extension is not arm64-capable, but MESHCHATX_MAC_UNIVERSAL_STRIP_AUDIO=1 is set; continuing (the build will drop _miniaudio.abi3.so before lipo)." >&2
|
||||
else
|
||||
echo "miniaudio native extension is not arm64-capable; universal macOS builds will fail at lipo." >&2
|
||||
echo "Re-run with MESHCHATX_MAC_UNIVERSAL_STRIP_AUDIO=1 to drop optional audio decoding for the DMG." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
Executable
+90
@@ -0,0 +1,90 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
|
||||
ARM64_DIR="$ROOT/build/exe/darwin-arm64"
|
||||
X64_DIR="$ROOT/build/exe/darwin-x64"
|
||||
|
||||
if [[ ! -d "$ARM64_DIR" || ! -d "$X64_DIR" ]]; then
|
||||
echo "thin-backend: one or both backend dirs missing, skipping"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "$(uname -s)" != "Darwin" ]]; then
|
||||
echo "thin-backend: not running on macOS (uname=$(uname -s)); skipping"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if ! command -v lipo >/dev/null 2>&1; then
|
||||
echo "thin-backend: lipo not found on PATH; skipping" >&2
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if ! command -v file >/dev/null 2>&1; then
|
||||
echo "thin-backend: file(1) not found on PATH; skipping" >&2
|
||||
exit 0
|
||||
fi
|
||||
|
||||
STRIP_AUDIO="${MESHCHATX_MAC_UNIVERSAL_STRIP_AUDIO:-0}"
|
||||
|
||||
drop_audio_natives() {
|
||||
local tree="$1"
|
||||
local removed=0
|
||||
while IFS= read -r -d '' f; do
|
||||
echo "thin-backend: stripping audio native (MESHCHATX_MAC_UNIVERSAL_STRIP_AUDIO=1): ${f#"$tree"/}" >&2
|
||||
rm -f "$f"
|
||||
removed=$((removed + 1))
|
||||
done < <(find "$tree" -type f \( -name "_miniaudio*.so" -o -name "_miniaudio*.dylib" \) -print0)
|
||||
if [[ $removed -gt 0 ]]; then
|
||||
echo "thin-backend: removed $removed _miniaudio file(s) from ${tree#"$ROOT"/}"
|
||||
fi
|
||||
}
|
||||
|
||||
if [[ "$STRIP_AUDIO" == "1" || "$STRIP_AUDIO" == "true" ]]; then
|
||||
drop_audio_natives "$ARM64_DIR"
|
||||
drop_audio_natives "$X64_DIR"
|
||||
fi
|
||||
|
||||
thin_tree() {
|
||||
local tree="$1" want_arch="$2"
|
||||
local thinned=0 already=0 skipped=0
|
||||
while IFS= read -r -d '' f; do
|
||||
local ft
|
||||
ft=$(file --brief --no-pad "$f" 2>/dev/null || true)
|
||||
if [[ "$ft" != Mach-O* ]]; then
|
||||
continue
|
||||
fi
|
||||
if [[ "$ft" != *universal* ]]; then
|
||||
already=$((already + 1))
|
||||
continue
|
||||
fi
|
||||
local archs
|
||||
archs=$(lipo -archs "$f" 2>/dev/null || true)
|
||||
if [[ -z "$archs" ]]; then
|
||||
skipped=$((skipped + 1))
|
||||
continue
|
||||
fi
|
||||
if ! grep -qw "$want_arch" <<<"$archs"; then
|
||||
echo "thin-backend: WARNING: $f is universal but lacks $want_arch (archs=$archs); leaving as-is" >&2
|
||||
skipped=$((skipped + 1))
|
||||
continue
|
||||
fi
|
||||
local tmp
|
||||
tmp="$(mktemp -t thin-backend.XXXXXX)"
|
||||
if lipo -thin "$want_arch" "$f" -output "$tmp" 2>/dev/null; then
|
||||
cat "$tmp" >"$f"
|
||||
rm -f "$tmp"
|
||||
thinned=$((thinned + 1))
|
||||
else
|
||||
rm -f "$tmp"
|
||||
echo "thin-backend: WARNING: lipo -thin $want_arch failed on $f" >&2
|
||||
skipped=$((skipped + 1))
|
||||
fi
|
||||
done < <(find "$tree" -type f \( -name "*.so" -o -name "*.dylib" -o -name "*.bundle" \) -print0)
|
||||
echo "thin-backend: ${tree#"$ROOT"/} -> $want_arch (thinned=$thinned, already-single=$already, skipped=$skipped)"
|
||||
}
|
||||
|
||||
thin_tree "$ARM64_DIR" arm64
|
||||
thin_tree "$X64_DIR" x86_64
|
||||
Reference in New Issue
Block a user