Skip to content

contrib/osx: add native arm64 (Apple Silicon) build support#10745

Open
ssotomayor wants to merge 1 commit into
spesmilo:masterfrom
ssotomayor:macos-arm64
Open

contrib/osx: add native arm64 (Apple Silicon) build support#10745
ssotomayor wants to merge 1 commit into
spesmilo:masterfrom
ssotomayor:macos-arm64

Conversation

@ssotomayor

Copy link
Copy Markdown

Native Apple Silicon (arm64) macOS build

Ref #7557. Adds an arm64 macOS build, cross-compiled on the existing x86_64 host. The x86_64 path is unchanged (byte-identical) when ELECTRUM_MACOS_ARCH is unset.

Approach

  • New ELECTRUM_MACOS_ARCH env var (x86_64 default, arm64 opt-in). In arm64 mode, native deps + C extensions build universal2 (so PyInstaller can import them on the x86_64 host); pyinstaller.spec then sets PyInstaller's target_arch (--target-architecture) to thin the bundle down to arm64.
  • PyQt6-Qt6 has no universal2 wheel, so make_universal_qt.py lipo-merges the two hash-pinned thin wheels in the venv.
  • Ships two thin per-arch .dmgs (not universal2), keeping the x86_64 release and the extract_sigs/apply_sigs flow untouched.
  • apply_sigs.sh: detect arch per file (lipo -archs) instead of hardcoding x86_64.

Verified (x86_64 host, Xcode CLT 13.2, Big Sur, python.org 3.12.10)

  • Reproducible: two clean rebuilds → byte-identical .app (95be818c…, empty diff).
  • Codesign + detached-sig flow work for arm64: self-signed sign_osx.shcompare_dmg IS_NOTARIZED=falseDMGs match. success.

Note: Homebrew no longer supports macOS 11 (Tier 3) and fails to build several required tools from source on Big Sur, so I installed autoconf/automake/libtool/gettext/pkgconfig/coreutils via MacPorts.

Adds an arm64 macOS build, cross-compiled on the existing x86_64 build
host, selected via a new ELECTRUM_MACOS_ARCH env var. Default remains
x86_64, and that path is unchanged (byte-identical output) when the var
is unset.

- make_osx.sh: ELECTRUM_MACOS_ARCH={x86_64,arm64}. In arm64 mode the
  native deps (libsecp256k1, zbar, libusb) and C extensions are compiled
  universal2 (-arch x86_64 -arch arm64), so PyInstaller can still import
  them on the x86_64 host during its Analysis step; the app bundle is
  then thinned to arm64. A post-build check verifies every Mach-O in the
  bundle is thin arm64.
- make_universal_qt.py (new): PyQt6-Qt6 ships no universal2 wheel, only
  thin per-arch ones. This downloads both hash-pinned thin wheels (from
  the hashes already pinned in requirements-binaries-mac.txt) and
  lipo-merges each Mach-O into universal2 in the venv, so PyInstaller has
  an arm64 slice to thin. The merge cannot affect shipped bytes: every
  shipped Qt file is either a verbatim slice of a hash-pinned wheel or a
  fat container that is thinned back to the wheel's exact bytes.
- pyinstaller.spec: target_arch follows ELECTRUM_MACOS_ARCH.
- apply_sigs.sh: detect the arch per file via 'lipo -archs' instead of
  hardcoding x86_64, so the detached-signature flow works for arm64
  Mach-O files.
- sign_osx.sh / make_osx.sh: the arm64 artifact is named
  electrum-<version>-arm64.dmg.

We ship separate thin per-arch binaries rather than a universal2 bundle:
this keeps the existing x86_64 release untouched and keeps the
extract_sigs/apply_sigs detached-signature flow on the simple
single-signature (thin) path.

Ref spesmilo#7557.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant