tMerge pull request #5042 from SomberNight/appimage - electrum - Electrum Bitcoin wallet HTML git clone https://git.parazyd.org/electrum DIR Log DIR Files DIR Refs DIR Submodules --- DIR commit 76ff2f53c504b8dc9e7a5fce3e25634267e1ec02 DIR parent ca931f476fc20e0c2003d4eb4fcafceb85e3972e HTML Author: ThomasV <thomasv@electrum.org> Date: Mon, 4 Feb 2019 19:16:15 +0100 Merge pull request #5042 from SomberNight/appimage binaries for Linux: AppImage Diffstat: M .travis.yml | 11 +++++++++++ A contrib/build-linux/appimage/Docke… | 25 +++++++++++++++++++++++++ A contrib/build-linux/appimage/READM… | 41 +++++++++++++++++++++++++++++++ A contrib/build-linux/appimage/appru… | 11 +++++++++++ A contrib/build-linux/appimage/build… | 197 +++++++++++++++++++++++++++++++ M contrib/build-wine/prepare-wine.sh | 56 ++----------------------------- A contrib/build_tools_util.sh | 69 ++++++++++++++++++++++++++++++ M contrib/osx/base.sh | 16 ++-------------- 8 files changed, 359 insertions(+), 67 deletions(-) --- DIR diff --git a/.travis.yml b/.travis.yml t@@ -70,6 +70,17 @@ jobs: script: ./contrib/osx/make_osx after_script: ls -lah dist && md5 dist/* after_success: true + - name: "AppImage build" + sudo: true + language: c + python: false + services: + - docker + install: + - sudo docker build --no-cache -t electrum-appimage-builder-img ./contrib/build-linux/appimage/ + script: + - sudo docker run --name electrum-appimage-builder-cont -v $PWD:/opt/electrum --rm --workdir /opt/electrum/contrib/build-linux/appimage electrum-appimage-builder-img ./build.sh + after_success: true - stage: release check install: - git fetch --all --tags DIR diff --git a/contrib/build-linux/appimage/Dockerfile b/contrib/build-linux/appimage/Dockerfile t@@ -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 DIR diff --git a/contrib/build-linux/appimage/README.md b/contrib/build-linux/appimage/README.md t@@ -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` DIR diff --git a/contrib/build-linux/appimage/apprun.sh b/contrib/build-linux/appimage/apprun.sh t@@ -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" "$@" DIR diff --git a/contrib/build-linux/appimage/build.sh b/contrib/build-linux/appimage/build.sh t@@ -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"/* DIR diff --git a/contrib/build-wine/prepare-wine.sh b/contrib/build-wine/prepare-wine.sh t@@ -24,62 +24,12 @@ PYHOME="c:/$PYTHON_FOLDER" PYTHON="wine $PYHOME/python.exe -OO -B" -# based on https://superuser.com/questions/497940/script-to-verify-a-signature-with-gpg -verify_signature() { - local file=$1 keyring=$2 out= - if out=$(gpg --no-default-keyring --keyring "$keyring" --status-fd 1 --verify "$file" 2>/dev/null) && - echo "$out" | grep -qs "^\[GNUPG:\] VALIDSIG "; then - return 0 - else - echo "$out" >&2 - exit 1 - fi -} - -verify_hash() { - local file=$1 expected_hash=$2 - actual_hash=$(sha256sum $file | awk '{print $1}') - if [ "$actual_hash" == "$expected_hash" ]; then - return 0 - else - echo "$file $actual_hash (unexpected hash)" >&2 - rm "$file" - exit 1 - fi -} - -download_if_not_exist() { - local file_name=$1 url=$2 - if [ ! -e $file_name ] ; then - wget -O $PWD/$file_name "$url" - fi -} - -# https://github.com/travis-ci/travis-build/blob/master/lib/travis/build/templates/header.sh -retry() { - local result=0 - local count=1 - while [ $count -le 3 ]; do - [ $result -ne 0 ] && { - echo -e "\nThe command \"$@\" failed. Retrying, $count of 3.\n" >&2 - } - ! { "$@"; result=$?; } - [ $result -eq 0 ] && break - count=$(($count + 1)) - sleep 1 - done - - [ $count -gt 3 ] && { - echo -e "\nThe command \"$@\" failed 3 times.\n" >&2 - } - - return $result -} - # Let's begin! -here=$(dirname $(readlink -e $0)) +here="$(dirname "$(readlink -e "$0")")" set -e +. $here/../build_tools_util.sh + wine 'wineboot' # HACK to work around https://bugs.winehq.org/show_bug.cgi?id=42474#c22 DIR diff --git a/contrib/build_tools_util.sh b/contrib/build_tools_util.sh t@@ -0,0 +1,69 @@ +#!/usr/bin/env bash + +RED='\033[0;31m' +BLUE='\033[0;34m' +YELLOW='\033[0;33m' +NC='\033[0m' # No Color +function info { + printf "\r💬 ${BLUE}INFO:${NC} ${1}\n" +} +function fail { + printf "\r🗯 ${RED}ERROR:${NC} ${1}\n" + exit 1 +} +function warn { + printf "\r⚠️ ${YELLOW}WARNING:${NC} ${1}\n" +} + + +# based on https://superuser.com/questions/497940/script-to-verify-a-signature-with-gpg +function verify_signature() { + local file=$1 keyring=$2 out= + if out=$(gpg --no-default-keyring --keyring "$keyring" --status-fd 1 --verify "$file" 2>/dev/null) && + echo "$out" | grep -qs "^\[GNUPG:\] VALIDSIG "; then + return 0 + else + echo "$out" >&2 + exit 1 + fi +} + +function verify_hash() { + local file=$1 expected_hash=$2 + actual_hash=$(sha256sum $file | awk '{print $1}') + if [ "$actual_hash" == "$expected_hash" ]; then + return 0 + else + echo "$file $actual_hash (unexpected hash)" >&2 + rm "$file" + exit 1 + fi +} + +function download_if_not_exist() { + local file_name=$1 url=$2 + if [ ! -e $file_name ] ; then + wget -O $file_name "$url" + fi +} + +# https://github.com/travis-ci/travis-build/blob/master/lib/travis/build/templates/header.sh +function retry() { + local result=0 + local count=1 + while [ $count -le 3 ]; do + [ $result -ne 0 ] && { + echo -e "\nThe command \"$@\" failed. Retrying, $count of 3.\n" >&2 + } + ! { "$@"; result=$?; } + [ $result -eq 0 ] && break + count=$(($count + 1)) + sleep 1 + done + + [ $count -gt 3 ] && { + echo -e "\nThe command \"$@\" failed 3 times.\n" >&2 + } + + return $result +} DIR diff --git a/contrib/osx/base.sh b/contrib/osx/base.sh t@@ -1,19 +1,7 @@ #!/usr/bin/env bash -RED='\033[0;31m' -BLUE='\033[0,34m' -YELLOW='\033[0;33m' -NC='\033[0m' # No Color -function info { - printf "\r💬 ${BLUE}INFO:${NC} ${1}\n" -} -function fail { - printf "\r🗯 ${RED}ERROR:${NC} ${1}\n" - exit 1 -} -function warn { - printf "\r⚠️ ${YELLOW}WARNING:${NC} ${1}\n" -} +. $(dirname "$0")/../build_tools_util.sh + function DoCodeSignMaybe { # ARGS: infoName fileOrDirName codesignIdentity infoName="$1"