From a754f9fe1019e17e16282933063bffc2623849f2 Mon Sep 17 00:00:00 2001 From: SomberNight Date: Tue, 29 Jan 2019 21:21:01 +0100 Subject: [PATCH] initial commit for building AppImages for Linux x86_64 --- contrib/build-linux/appimage/Dockerfile | 25 +++ contrib/build-linux/appimage/README.md | 41 +++++ contrib/build-linux/appimage/apprun.sh | 11 ++ contrib/build-linux/appimage/build.sh | 197 ++++++++++++++++++++++++ 4 files changed, 274 insertions(+) create mode 100644 contrib/build-linux/appimage/Dockerfile create mode 100644 contrib/build-linux/appimage/README.md create mode 100755 contrib/build-linux/appimage/apprun.sh create mode 100755 contrib/build-linux/appimage/build.sh diff --git a/contrib/build-linux/appimage/Dockerfile b/contrib/build-linux/appimage/Dockerfile new file mode 100644 index 000000000..7e8a3ba27 --- /dev/null +++ b/contrib/build-linux/appimage/Dockerfile @@ -0,0 +1,25 @@ +FROM ubuntu:14.04@sha256:cac55e5d97fad634d954d00a5c2a56d80576a08dcc01036011f26b88263f1578 + +ENV LC_ALL=C.UTF-8 LANG=C.UTF-8 + +RUN apt-get update -q && \ + apt-get install -qy \ + git \ + wget \ + make \ + autotools-dev \ + autoconf \ + libtool \ + xz-utils \ + libssl-dev \ + zlib1g-dev \ + libffi6 \ + libffi-dev \ + libusb-1.0-0-dev \ + libudev-dev \ + gettext \ + libzbar0 \ + && \ + rm -rf /var/lib/apt/lists/* && \ + apt-get autoremove -y && \ + apt-get clean diff --git a/contrib/build-linux/appimage/README.md b/contrib/build-linux/appimage/README.md new file mode 100644 index 000000000..747c84c55 --- /dev/null +++ b/contrib/build-linux/appimage/README.md @@ -0,0 +1,41 @@ +AppImage binary for Electrum +============================ + +This assumes an Ubuntu host, but it should not be too hard to adapt to another +similar system. The docker commands should be executed in the project's root +folder. + +1. Install Docker + + ``` + $ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - + $ sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" + $ sudo apt-get update + $ sudo apt-get install -y docker-ce + ``` + +2. Build image + + ``` + $ sudo docker build --no-cache -t electrum-appimage-builder-img contrib/build-linux/appimage + ``` + +3. Build binary + + ``` + $ sudo docker run -it \ + --name electrum-appimage-builder-cont \ + -v $PWD:/opt/electrum \ + --rm \ + --workdir /opt/electrum/contrib/build-linux/appimage \ + electrum-appimage-builder-img \ + ./build.sh + ``` + +4. The generated binary is in `./dist`. + + +## FAQ + +### How can I see what is included in the AppImage? +Execute the binary as follows: `./electrum*.AppImage --appimage-extract` diff --git a/contrib/build-linux/appimage/apprun.sh b/contrib/build-linux/appimage/apprun.sh new file mode 100755 index 000000000..6000c7c91 --- /dev/null +++ b/contrib/build-linux/appimage/apprun.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +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}" +export PATH="${APPDIR}/usr/bin:${PATH}" +export LDFLAGS="-L${APPDIR}/usr/lib/x86_64-linux-gnu -L${APPDIR}/usr/lib" + +exec "${APPDIR}/usr/bin/python3.6" -s "${APPDIR}/usr/bin/electrum" "$@" diff --git a/contrib/build-linux/appimage/build.sh b/contrib/build-linux/appimage/build.sh new file mode 100755 index 000000000..35480a6cd --- /dev/null +++ b/contrib/build-linux/appimage/build.sh @@ -0,0 +1,197 @@ +#!/bin/bash + +set -e + +PROJECT_ROOT="$(dirname "$(readlink -e "$0")")/../../.." +CONTRIB="$PROJECT_ROOT/contrib" +DISTDIR="$PROJECT_ROOT/dist" +BUILDDIR="$CONTRIB/build-linux/appimage/build/appimage" +APPDIR="$BUILDDIR/electrum.AppDir" +CACHEDIR="$CONTRIB/build-linux/appimage/.cache/appimage" + +# pinned versions +PYTHON_VERSION=3.6.8 +PKG2APPIMAGE_COMMIT="83483c2971fcaa1cb0c1253acd6c731ef8404381" +LIBSECP_VERSION="452d8e4d2a2f9f1b5be6b02e18f1ba102e5ca0b4" + + +VERSION=`git describe --tags --dirty --always` +APPIMAGE="$DISTDIR/electrum-$VERSION-x86_64.AppImage" + +rm -rf "$BUILDDIR" +mkdir -p "$APPDIR" "$CACHEDIR" "$DISTDIR" + + +. "$CONTRIB"/build_tools_util.sh + + +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" "a73a21a6c1d1e15c0a9f47f017ae833873d1dc6aa74a4c840c0b901bf1dcf09c" + +download_if_not_exist "$CACHEDIR/appimagetool" "https://github.com/probonopd/AppImageKit/releases/download/11/appimagetool-x86_64.AppImage" +verify_hash "$CACHEDIR/appimagetool" "c13026b9ebaa20a17e7e0a4c818a901f0faba759801d8ceab3bb6007dde00372" + +download_if_not_exist "$CACHEDIR/Python-$PYTHON_VERSION.tar.xz" "https://www.python.org/ftp/python/$PYTHON_VERSION/Python-$PYTHON_VERSION.tar.xz" +verify_hash "$CACHEDIR/Python-$PYTHON_VERSION.tar.xz" "35446241e995773b1bed7d196f4b624dadcadc8429f26282e756b2fb8a351193" + + + +info "building python." +tar xf "$CACHEDIR/Python-$PYTHON_VERSION.tar.xz" -C "$BUILDDIR" +( + cd "$BUILDDIR/Python-$PYTHON_VERSION" + export SOURCE_DATE_EPOCH=1530212462 + ./configure \ + --cache-file="$CACHEDIR/python.config.cache" \ + --prefix="$APPDIR/usr" \ + --enable-ipv6 \ + --enable-shared \ + --with-threads \ + -q + make -s + make -s install > /dev/null +) + + +info "building libsecp256k1." +( + git clone https://github.com/bitcoin-core/secp256k1 "$CACHEDIR"/secp256k1 || (cd "$CACHEDIR"/secp256k1 && git pull) + cd "$CACHEDIR"/secp256k1 + git reset --hard "$LIBSECP_VERSION" + git clean -f -x -q + export SOURCE_DATE_EPOCH=1530212462 + ./autogen.sh + echo "LDFLAGS = -no-undefined" >> Makefile.am + ./configure \ + --prefix="$APPDIR/usr" \ + --enable-module-recovery \ + --enable-experimental \ + --enable-module-ecdh \ + --disable-jni \ + -q + make -s + make -s install > /dev/null +) + + +appdir_python() { + env \ + PYTHONNOUSERSITE=1 \ + LD_LIBRARY_PATH="$APPDIR/usr/lib:$APPDIR/usr/lib/x86_64-linux-gnu${LD_LIBRARY_PATH+:$LD_LIBRARY_PATH}" \ + "$APPDIR/usr/bin/python3.6" "$@" +} + +python='appdir_python' + + +info "installing pip." +"$python" -m ensurepip + + +info "preparing electrum-locale." +( + cd "$PROJECT_ROOT" + git submodule update --init + + pushd "$CONTRIB"/deterministic-build/electrum-locale + if ! which msgfmt > /dev/null 2>&1; then + echo "Please install gettext" + exit 1 + fi + for i in ./locale/*; do + dir="$PROJECT_ROOT/electrum/$i/LC_MESSAGES" + mkdir -p $dir + msgfmt --output-file="$dir/electrum.mo" "$i/electrum.po" || true + done + popd +) + + +info "installing electrum and its dependencies." +mkdir -p "$CACHEDIR/pip_cache" +"$python" -m pip install --cache-dir "$CACHEDIR/pip_cache" -r "$CONTRIB/deterministic-build/requirements.txt" +"$python" -m pip install --cache-dir "$CACHEDIR/pip_cache" -r "$CONTRIB/deterministic-build/requirements-binaries.txt" +"$python" -m pip install --cache-dir "$CACHEDIR/pip_cache" -r "$CONTRIB/deterministic-build/requirements-hw.txt" +"$python" -m pip install --cache-dir "$CACHEDIR/pip_cache" "$PROJECT_ROOT" + + +info "copying zbar" +cp "/usr/lib/libzbar.so.0" "$APPDIR/usr/lib/libzbar.so.0" + + +info "desktop integration." +cp "$PROJECT_ROOT/electrum.desktop" "$APPDIR/electrum.desktop" +cp "$PROJECT_ROOT/icons/electrum.png" "$APPDIR/electrum.png" + + +# add launcher +cp "$CONTRIB/build-linux/appimage/apprun.sh" "$APPDIR/AppRun" + +info "finalizing AppDir." +( + export PKG2AICOMMIT="$PKG2APPIMAGE_COMMIT" + . "$CACHEDIR/functions.sh" + + cd "$APPDIR" + # copy system dependencies + # note: temporarily move PyQt5 out of the way so + # we don't try to bundle its system dependencies. + mv "$APPDIR/usr/lib/python3.6/site-packages/PyQt5" "$BUILDDIR" + copy_deps; copy_deps; copy_deps + move_lib + mv "$BUILDDIR/PyQt5" "$APPDIR/usr/lib/python3.6/site-packages" + + # apply global appimage blacklist to exclude stuff + # move usr/include out of the way to preserve usr/include/python3.6m. + mv usr/include usr/include.tmp + delete_blacklisted + mv usr/include.tmp usr/include +) + + +info "stripping binaries from debug symbols." +strip_binaries() +{ + chmod u+w -R "$APPDIR" + { + printf '%s\0' "$APPDIR/usr/bin/python3.6" + find "$APPDIR" -type f -regex '.*\.so\(\.[0-9.]+\)?$' -print0 + } | xargs -0 --no-run-if-empty --verbose -n1 strip +} +strip_binaries + +remove_emptydirs() +{ + find "$APPDIR" -type d -empty -print0 | xargs -0 --no-run-if-empty rmdir -vp --ignore-fail-on-non-empty +} +remove_emptydirs + + +info "removing some unneeded stuff to decrease binary size." +rm -rf "$APPDIR"/usr/lib/python3.6/test +rm -rf "$APPDIR"/usr/lib/python3.6/config-3.6m-x86_64-linux-gnu +rm -rf "$APPDIR"/usr/lib/python3.6/site-packages/PyQt5/Qt/translations/qtwebengine_locales +rm -rf "$APPDIR"/usr/lib/python3.6/site-packages/PyQt5/Qt/resources/qtwebengine_* +rm -rf "$APPDIR"/usr/lib/python3.6/site-packages/PyQt5/Qt/qml +rm -rf "$APPDIR"/usr/lib/python3.6/site-packages/PyQt5/Qt/lib/libQt5Web* +rm -rf "$APPDIR"/usr/lib/python3.6/site-packages/PyQt5/Qt/lib/libQt5Designer* +rm -rf "$APPDIR"/usr/lib/python3.6/site-packages/PyQt5/Qt/lib/libQt5Qml* +rm -rf "$APPDIR"/usr/lib/python3.6/site-packages/PyQt5/Qt/lib/libQt5Quick* +rm -rf "$APPDIR"/usr/lib/python3.6/site-packages/PyQt5/Qt/lib/libQt5Location* +rm -rf "$APPDIR"/usr/lib/python3.6/site-packages/PyQt5/Qt/lib/libQt5Test* +rm -rf "$APPDIR"/usr/lib/python3.6/site-packages/PyQt5/Qt/lib/libQt5Xml* + + +info "creating the AppImage." +( + cd "$BUILDDIR" + chmod +x "$CACHEDIR/appimagetool" + "$CACHEDIR/appimagetool" --appimage-extract + env VERSION="$VERSION" ./squashfs-root/AppRun --no-appstream --verbose "$APPDIR" "$APPIMAGE" +) + + +info "done." +ls -la "$DISTDIR" +sha256sum "$DISTDIR"/*