From ec2790d138bf258214a86cc87541e44fe005ddd4 Mon Sep 17 00:00:00 2001 From: davide3011 Date: Sun, 11 Jan 2026 00:40:17 +0100 Subject: [PATCH] Add ARM64 (aarch64) support for AppImage builds --- contrib/build-linux/appimage/Dockerfile | 2 + contrib/build-linux/appimage/apprun.sh | 14 ++++- contrib/build-linux/appimage/build.sh | 10 +++ contrib/build-linux/appimage/make_appimage.sh | 62 +++++++++++++++---- .../appimage/make_type2_runtime.sh | 22 ++++--- 5 files changed, 87 insertions(+), 23 deletions(-) diff --git a/contrib/build-linux/appimage/Dockerfile b/contrib/build-linux/appimage/Dockerfile index 5f43ac930..987616d51 100644 --- a/contrib/build-linux/appimage/Dockerfile +++ b/contrib/build-linux/appimage/Dockerfile @@ -2,10 +2,12 @@ # from https://docs.appimage.org/introduction/concepts.html : # "[AppImages] should be built on the oldest possible system, allowing them to run on newer system[s]" +ARG TARGETARCH=x86_64 FROM debian:bullseye@sha256:cf48c31af360e1c0a0aedd33aae4d928b68c2cdf093f1612650eb1ff434d1c34 ENV LC_ALL=C.UTF-8 LANG=C.UTF-8 ENV DEBIAN_FRONTEND=noninteractive +ENV TARGETARCH=${TARGETARCH} # need ca-certificates before using snapshot packages RUN apt update -qq > /dev/null && apt install -qq --yes --no-install-recommends \ diff --git a/contrib/build-linux/appimage/apprun.sh b/contrib/build-linux/appimage/apprun.sh index 134323530..c99f1c9bb 100755 --- a/contrib/build-linux/appimage/apprun.sh +++ b/contrib/build-linux/appimage/apprun.sh @@ -4,8 +4,18 @@ set -e APPDIR="$(dirname "$(readlink -e "$0")")" -export LD_LIBRARY_PATH="${APPDIR}/usr/lib/:${APPDIR}/usr/lib/x86_64-linux-gnu${LD_LIBRARY_PATH+:$LD_LIBRARY_PATH}" +# Detect architecture-specific lib directory +if [ -d "${APPDIR}/usr/lib/x86_64-linux-gnu" ]; then + ARCH_LIB_DIR="x86_64-linux-gnu" +elif [ -d "${APPDIR}/usr/lib/aarch64-linux-gnu" ]; then + ARCH_LIB_DIR="aarch64-linux-gnu" +else + # Fallback to x86_64 for backward compatibility + ARCH_LIB_DIR="x86_64-linux-gnu" +fi + +export LD_LIBRARY_PATH="${APPDIR}/usr/lib/:${APPDIR}/usr/lib/${ARCH_LIB_DIR}${LD_LIBRARY_PATH+:$LD_LIBRARY_PATH}" export PATH="${APPDIR}/usr/bin:${PATH}" -export LDFLAGS="-L${APPDIR}/usr/lib/x86_64-linux-gnu -L${APPDIR}/usr/lib" +export LDFLAGS="-L${APPDIR}/usr/lib/${ARCH_LIB_DIR} -L${APPDIR}/usr/lib" exec "${APPDIR}/usr/bin/python3" -s "${APPDIR}/usr/bin/electrum" "$@" diff --git a/contrib/build-linux/appimage/build.sh b/contrib/build-linux/appimage/build.sh index cf5bd0775..f75bf3d67 100755 --- a/contrib/build-linux/appimage/build.sh +++ b/contrib/build-linux/appimage/build.sh @@ -3,6 +3,7 @@ # env vars: # - ELECBUILD_NOCACHE: if set, forces rebuild of docker image # - ELECBUILD_COMMIT: if set, do a fresh clone and git checkout +# - ARCH: target architecture (x86_64 or aarch64, default: x86_64) set -e @@ -15,6 +16,13 @@ BUILD_UID=$(/usr/bin/stat -c %u "$PROJECT_ROOT") . "$CONTRIB"/build_tools_util.sh +# Determine architecture (default to x86_64 if not set) +export ARCH="${ARCH:-x86_64}" +if [ "$ARCH" != "x86_64" ] && [ "$ARCH" != "aarch64" ]; then + fail "Unsupported ARCH: $ARCH. Must be x86_64 or aarch64" +fi +info "Building AppImage for architecture: $ARCH" + DOCKER_BUILD_FLAGS="" if [ ! -z "$ELECBUILD_NOCACHE" ] ; then @@ -29,6 +37,7 @@ fi info "building docker image." docker build \ $DOCKER_BUILD_FLAGS \ + --build-arg TARGETARCH="$ARCH" \ -t pallectrum-appimage-builder-img \ "$CONTRIB_APPIMAGE" @@ -78,6 +87,7 @@ docker run $DOCKER_RUN_FLAGS \ -v "$PROJECT_ROOT_OR_FRESHCLONE_ROOT":/opt/pallectrum \ --rm \ --workdir /opt/pallectrum/contrib/build-linux/appimage \ + -e ARCH="$ARCH" \ pallectrum-appimage-builder-img \ ./make_appimage.sh diff --git a/contrib/build-linux/appimage/make_appimage.sh b/contrib/build-linux/appimage/make_appimage.sh index 586c7d129..daf77f2cb 100755 --- a/contrib/build-linux/appimage/make_appimage.sh +++ b/contrib/build-linux/appimage/make_appimage.sh @@ -20,6 +20,20 @@ git -C "$PROJECT_ROOT" rev-parse 2>/dev/null || fail "Building outside a git clo export GCC_STRIP_BINARIES="1" +# Determine architecture (default to x86_64 if not set) +ARCH="${ARCH:-x86_64}" +if [ "$ARCH" != "x86_64" ] && [ "$ARCH" != "aarch64" ]; then + fail "Unsupported ARCH: $ARCH. Must be x86_64 or aarch64" +fi +info "Building for architecture: $ARCH" + +# Set architecture-specific variables +if [ "$ARCH" = "aarch64" ]; then + LINUX_GNU_ARCH="aarch64-linux-gnu" +else + LINUX_GNU_ARCH="x86_64-linux-gnu" +fi + # pinned versions PYTHON_VERSION=3.12.11 PY_VER_MAJOR="3.12" # as it appears in fs paths @@ -36,8 +50,12 @@ info "downloading some dependencies." download_if_not_exist "$CACHEDIR/functions.sh" "https://raw.githubusercontent.com/AppImage/pkg2appimage/$PKG2APPIMAGE_COMMIT/functions.sh" verify_hash "$CACHEDIR/functions.sh" "8f67711a28635b07ce539a9b083b8c12d5488c00003d6d726c7b134e553220ed" -download_if_not_exist "$CACHEDIR/appimagetool" "https://github.com/AppImage/appimagetool/releases/download/1.9.0/appimagetool-x86_64.AppImage" -verify_hash "$CACHEDIR/appimagetool" "46fdd785094c7f6e545b61afcfb0f3d98d8eab243f644b4b17698c01d06083d1" +download_if_not_exist "$CACHEDIR/appimagetool-$ARCH" "https://github.com/AppImage/appimagetool/releases/download/1.9.0/appimagetool-$ARCH.AppImage" +if [ "$ARCH" = "x86_64" ]; then + verify_hash "$CACHEDIR/appimagetool-$ARCH" "46fdd785094c7f6e545b61afcfb0f3d98d8eab243f644b4b17698c01d06083d1" +else + verify_hash "$CACHEDIR/appimagetool-$ARCH" "04f45ea45b5aa07bb2b071aed9dbf7a5185d3953b11b47358c1311f11ea94a96" +fi # note: desktop-file-utils in the docker image is needed to run desktop-file-validate for appimagetool <= 1.9.0, so it can be removed once # appimagetool tags a new release (see https://github.com/AppImage/appimagetool/pull/47) @@ -99,7 +117,7 @@ cp -f "$DLL_TARGET_DIR/libzbar.so.0" "$APPDIR/usr/lib/" || fail "Could not copy appdir_python() { env \ PYTHONNOUSERSITE=1 \ - LD_LIBRARY_PATH="$APPDIR/usr/lib:$APPDIR/usr/lib/x86_64-linux-gnu${LD_LIBRARY_PATH+:$LD_LIBRARY_PATH}" \ + LD_LIBRARY_PATH="$APPDIR/usr/lib:$APPDIR/usr/lib/$LINUX_GNU_ARCH${LD_LIBRARY_PATH+:$LD_LIBRARY_PATH}" \ "$APPDIR/usr/bin/python${PY_VER_MAJOR}" "$@" } @@ -118,12 +136,12 @@ info "determining version for AppImage filename." VERSION=$("$python" "$CONTRIB"/print_electrum_version.py) if [ -n "$ELECBUILD_COMMIT" ] || git diff-index --quiet HEAD -- ; then # Reproducible build or clean repo: clean version only - APPIMAGE="$DISTDIR/pallectrum-v$VERSION-x86_64.AppImage" + APPIMAGE="$DISTDIR/pallectrum-v$VERSION-$ARCH.AppImage" else # Development build with uncommitted changes: add git info GIT_HASH=$(git rev-parse --short=9 HEAD) VERSION_WITH_HASH="v$VERSION-$(git rev-list --count HEAD)-g$GIT_HASH-dirty" - APPIMAGE="$DISTDIR/pallectrum-$VERSION_WITH_HASH-x86_64.AppImage" + APPIMAGE="$DISTDIR/pallectrum-$VERSION_WITH_HASH-$ARCH.AppImage" fi info "AppImage will be: $APPIMAGE" @@ -160,8 +178,26 @@ export ELECTRUM_ECC_DONT_COMPILE=1 info "installing electrum and its dependencies." "$python" -m pip install --no-build-isolation --no-dependencies --no-binary :all: --no-warn-script-location \ --cache-dir "$PIP_CACHE_DIR" -r "$CONTRIB/deterministic-build/requirements.txt" -"$python" -m pip install --no-build-isolation --no-dependencies --no-binary :all: --only-binary PyQt6,PyQt6-Qt6,cryptography --no-warn-script-location \ - --cache-dir "$PIP_CACHE_DIR" -r "$CONTRIB/deterministic-build/requirements-binaries.txt" + +# For aarch64, PyQt6 6.9.0 is not available, so we install compatible versions without hash verification +if [ "$ARCH" = "aarch64" ]; then + info "Installing binaries for aarch64 (using latest compatible versions)..." + "$python" -m pip install --no-build-isolation --no-dependencies --only-binary PyQt6,PyQt6-Qt6,cryptography --no-warn-script-location \ + --cache-dir "$PIP_CACHE_DIR" \ + 'cffi==1.17.1' \ + 'cryptography==45.0.3' \ + 'pip==25.1.1' \ + 'pycparser==2.22' \ + 'PyQt6>=6.7.0,<6.8.0' \ + 'PyQt6-Qt6>=6.7.0,<6.8.0' \ + 'PyQt6-sip==13.10.2' \ + 'setuptools==80.9.0' \ + 'wheel==0.45.1' +else + "$python" -m pip install --no-build-isolation --no-dependencies --no-binary :all: --only-binary PyQt6,PyQt6-Qt6,cryptography --no-warn-script-location \ + --cache-dir "$PIP_CACHE_DIR" -r "$CONTRIB/deterministic-build/requirements-binaries.txt" +fi + "$python" -m pip install --no-build-isolation --no-dependencies --no-binary :all: --no-warn-script-location \ --cache-dir "$PIP_CACHE_DIR" -r "$CONTRIB/deterministic-build/requirements-hw.txt" @@ -200,11 +236,11 @@ info "finalizing AppDir." info "Copying additional libraries" ( # On some systems it can cause problems to use the system libusb (on AppImage excludelist) - cp -f /usr/lib/x86_64-linux-gnu/libusb-1.0.so "$APPDIR/usr/lib/libusb-1.0.so" || fail "Could not copy libusb" + cp -f /usr/lib/$LINUX_GNU_ARCH/libusb-1.0.so "$APPDIR/usr/lib/libusb-1.0.so" || fail "Could not copy libusb" # some distros lack libxkbcommon-x11 - cp -f /usr/lib/x86_64-linux-gnu/libxkbcommon-x11.so.0 "$APPDIR"/usr/lib/x86_64-linux-gnu || fail "Could not copy libxkbcommon-x11" + cp -f /usr/lib/$LINUX_GNU_ARCH/libxkbcommon-x11.so.0 "$APPDIR"/usr/lib/$LINUX_GNU_ARCH || fail "Could not copy libxkbcommon-x11" # some distros lack some libxcb libraries (see https://github.com/Electron-Cash/Electron-Cash/issues/2196) - cp -f /usr/lib/x86_64-linux-gnu/libxcb-* "$APPDIR"/usr/lib/x86_64-linux-gnu || fail "Could not copy libxcb" + cp -f /usr/lib/$LINUX_GNU_ARCH/libxcb-* "$APPDIR"/usr/lib/$LINUX_GNU_ARCH || fail "Could not copy libxcb" ) info "stripping binaries from debug symbols." @@ -233,7 +269,7 @@ PYDIR="$APPDIR/usr/lib/python${PY_VER_MAJOR}" rm -rf "$PYDIR"/{test,ensurepip,lib2to3,idlelib,turtledemo} rm -rf "$PYDIR"/{ctypes,sqlite3,tkinter,unittest}/test rm -rf "$PYDIR"/distutils/{command,tests} -rm -rf "$PYDIR"/config-3.*-x86_64-linux-gnu +rm -rf "$PYDIR"/config-3.*-$LINUX_GNU_ARCH rm -rf "$PYDIR"/site-packages/{opt,pip,setuptools,wheel} rm -rf "$PYDIR"/site-packages/Cryptodome/SelfTest rm -rf "$PYDIR"/site-packages/{psutil,qrcode,websocket}/tests @@ -271,7 +307,7 @@ find -exec touch -h -d '2000-11-11T11:11:11+00:00' {} + info "creating the AppImage." ( cd "$BUILDDIR" - cp "$CACHEDIR/appimagetool" "$CACHEDIR/appimagetool_copy" + cp "$CACHEDIR/appimagetool-$ARCH" "$CACHEDIR/appimagetool_copy" # zero out "appimage" magic bytes, as on some systems they confuse the linker sed -i 's|AI\x02|\x00\x00\x00|' "$CACHEDIR/appimagetool_copy" chmod +x "$CACHEDIR/appimagetool_copy" @@ -285,7 +321,7 @@ args=\$(echo "\$@" | sed -e 's/-mkfs-time 0//') "$BUILDDIR/squashfs-root/usr/bin/mksquashfs_orig" \$args EOF chmod +x "$BUILDDIR/squashfs-root/usr/bin/mksquashfs" - env VERSION="$VERSION" ARCH=x86_64 ./squashfs-root/AppRun --runtime-file "$TYPE2_RUNTIME_REPO_DIR/runtime-x86_64" --no-appstream --verbose "$APPDIR" "$APPIMAGE" + env VERSION="$VERSION" ARCH="$ARCH" ./squashfs-root/AppRun --runtime-file "$TYPE2_RUNTIME_REPO_DIR/runtime-$ARCH" --no-appstream --verbose "$APPDIR" "$APPIMAGE" ) diff --git a/contrib/build-linux/appimage/make_type2_runtime.sh b/contrib/build-linux/appimage/make_type2_runtime.sh index ad54728c1..eacef5ac1 100755 --- a/contrib/build-linux/appimage/make_type2_runtime.sh +++ b/contrib/build-linux/appimage/make_type2_runtime.sh @@ -13,12 +13,18 @@ TYPE2_RUNTIME_REPO="https://github.com/AppImage/type2-runtime.git" . "$CONTRIB"/build_tools_util.sh +# Determine architecture (default to x86_64 if not set) +ARCH="${ARCH:-x86_64}" +if [ "$ARCH" != "x86_64" ] && [ "$ARCH" != "aarch64" ]; then + fail "Unsupported ARCH: $ARCH. Must be x86_64 or aarch64" +fi +info "Building type2-runtime for architecture: $ARCH" # Use a shared cache location that persists across fresh clones # This is critical for reproducible builds with ELECBUILD_COMMIT TYPE2_RUNTIME_REPO_DIR="$PROJECT_ROOT/contrib/build-linux/appimage/.cache/appimage/type2-runtime" -if [ -f "$TYPE2_RUNTIME_REPO_DIR/runtime-x86_64" ] && [ -s "$TYPE2_RUNTIME_REPO_DIR/runtime-x86_64" ]; then - info "type2-runtime already built ($(du -h "$TYPE2_RUNTIME_REPO_DIR/runtime-x86_64" | cut -f1)), skipping" +if [ -f "$TYPE2_RUNTIME_REPO_DIR/runtime-$ARCH" ] && [ -s "$TYPE2_RUNTIME_REPO_DIR/runtime-$ARCH" ]; then + info "type2-runtime already built ($(du -h "$TYPE2_RUNTIME_REPO_DIR/runtime-$ARCH" | cut -f1)), skipping" exit 0 fi @@ -42,21 +48,21 @@ fi info "building type2-runtime in build container..." cd "$TYPE2_RUNTIME_REPO_DIR/scripts/docker" -env ARCH=x86_64 ./build-with-docker.sh || fail "Failed to build type2-runtime with docker" +env ARCH="$ARCH" ./build-with-docker.sh || fail "Failed to build type2-runtime with docker" # Verify the runtime was created in the expected location -if [ ! -f "./runtime-x86_64" ]; then - fail "Runtime binary was not created by docker build (expected at: $TYPE2_RUNTIME_REPO_DIR/scripts/docker/runtime-x86_64)" +if [ ! -f "./runtime-$ARCH" ]; then + fail "Runtime binary was not created by docker build (expected at: $TYPE2_RUNTIME_REPO_DIR/scripts/docker/runtime-$ARCH)" fi -mv "./runtime-x86_64" "$TYPE2_RUNTIME_REPO_DIR/" +mv "./runtime-$ARCH" "$TYPE2_RUNTIME_REPO_DIR/" # Verify runtime is valid (not empty) -if [ ! -s "$TYPE2_RUNTIME_REPO_DIR/runtime-x86_64" ]; then +if [ ! -s "$TYPE2_RUNTIME_REPO_DIR/runtime-$ARCH" ]; then fail "Runtime binary is empty or invalid" fi # clean up the empty created 'out' dir to prevent permission issues rm -rf "$TYPE2_RUNTIME_REPO_DIR/out" -info "runtime build successful: $(sha256sum "$TYPE2_RUNTIME_REPO_DIR/runtime-x86_64")" +info "runtime build successful: $(sha256sum "$TYPE2_RUNTIME_REPO_DIR/runtime-$ARCH")"