You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
256 lines
10 KiB
256 lines
10 KiB
#!/bin/bash |
|
|
|
set -e |
|
|
|
PROJECT_ROOT="$(dirname "$(readlink -e "$0")")/../../.." |
|
CONTRIB="$PROJECT_ROOT/contrib" |
|
CONTRIB_APPIMAGE="$CONTRIB/build-linux/appimage" |
|
DISTDIR="$PROJECT_ROOT/dist" |
|
BUILDDIR="$CONTRIB_APPIMAGE/build/appimage" |
|
APPDIR="$BUILDDIR/electrum.AppDir" |
|
CACHEDIR="$CONTRIB_APPIMAGE/.cache/appimage" |
|
PIP_CACHE_DIR="$CACHEDIR/pip_cache" |
|
|
|
export GCC_STRIP_BINARIES="1" |
|
|
|
# pinned versions |
|
PYTHON_VERSION=3.9.11 |
|
PKG2APPIMAGE_COMMIT="a9c85b7e61a3a883f4a35c41c5decb5af88b6b5d" |
|
|
|
|
|
VERSION=`git describe --tags --dirty --always` |
|
APPIMAGE="$DISTDIR/electrum-$VERSION-x86_64.AppImage" |
|
|
|
. "$CONTRIB"/build_tools_util.sh |
|
|
|
rm -rf "$BUILDDIR" |
|
mkdir -p "$APPDIR" "$CACHEDIR" "$PIP_CACHE_DIR" "$DISTDIR" |
|
|
|
# potential leftover from setuptools that might make pip put garbage in binary |
|
rm -rf "$PROJECT_ROOT/build" |
|
|
|
|
|
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/AppImageKit/releases/download/13/appimagetool-x86_64.AppImage" |
|
verify_hash "$CACHEDIR/appimagetool" "df3baf5ca5facbecfc2f3fa6713c29ab9cefa8fd8c1eac5d283b79cab33e4acb" |
|
|
|
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" "66767a35309d724f370df9e503c172b4ee444f49d62b98bc4eca725123e26c49" |
|
|
|
|
|
|
|
info "building python." |
|
tar xf "$CACHEDIR/Python-$PYTHON_VERSION.tar.xz" -C "$BUILDDIR" |
|
( |
|
cd "$BUILDDIR/Python-$PYTHON_VERSION" |
|
LC_ALL=C export BUILD_DATE=$(date -u -d "@$SOURCE_DATE_EPOCH" "+%b %d %Y") |
|
LC_ALL=C export BUILD_TIME=$(date -u -d "@$SOURCE_DATE_EPOCH" "+%H:%M:%S") |
|
# Patch taken from Ubuntu http://archive.ubuntu.com/ubuntu/pool/main/p/python3.9/python3.9_3.9.5-3~21.04.debian.tar.xz |
|
patch -p1 < "$CONTRIB_APPIMAGE/patches/python-3.9-reproducible-buildinfo.diff" |
|
./configure \ |
|
--cache-file="$CACHEDIR/python.config.cache" \ |
|
--prefix="$APPDIR/usr" \ |
|
--enable-ipv6 \ |
|
--enable-shared \ |
|
-q |
|
make -j4 -s || fail "Could not build Python" |
|
make -s install > /dev/null || fail "Could not install Python" |
|
# When building in docker on macOS, python builds with .exe extension because the |
|
# case insensitive file system of macOS leaks into docker. This causes the build |
|
# to result in a different output on macOS compared to Linux. We simply patch |
|
# sysconfigdata to remove the extension. |
|
# Some more info: https://bugs.python.org/issue27631 |
|
sed -i -e 's/\.exe//g' "$APPDIR"/usr/lib/python3.9/_sysconfigdata* |
|
) |
|
|
|
|
|
"$CONTRIB"/make_libsecp256k1.sh || fail "Could not build libsecp" |
|
cp -f "$PROJECT_ROOT/electrum/libsecp256k1.so.0" "$APPDIR/usr/lib/libsecp256k1.so.0" || fail "Could not copy libsecp to its destination" |
|
|
|
|
|
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.9" "$@" |
|
} |
|
|
|
python='appdir_python' |
|
|
|
|
|
info "installing pip." |
|
"$python" -m ensurepip |
|
|
|
break_legacy_easy_install |
|
|
|
|
|
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 |
|
fail "Please install gettext" |
|
fi |
|
# we want the binary to have only compiled (.mo) locale files; not source (.po) files |
|
rm -rf "$PROJECT_ROOT/electrum/locale/" |
|
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 build dependencies." |
|
# note: re pip installing from PyPI, |
|
# we prefer compiling C extensions ourselves, instead of using binary wheels, |
|
# hence "--no-binary :all:" flags. However, we specifically allow |
|
# - PyQt5, as it's harder to build from source |
|
# - cryptography, as it's harder to build from source |
|
# - the whole of "requirements-build-base.txt", which includes pip and friends, as it also includes "wheel", |
|
# and I am not quite sure how to break the circular dependence there (I guess we could introduce |
|
# "requirements-build-base-base.txt" with just wheel in it...) |
|
"$python" -m pip install --no-build-isolation --no-dependencies --no-warn-script-location \ |
|
--cache-dir "$PIP_CACHE_DIR" -r "$CONTRIB/deterministic-build/requirements-build-base.txt" |
|
"$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-build-appimage.txt" |
|
|
|
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 PyQt5,PyQt5-Qt5,cryptography --no-warn-script-location \ |
|
--cache-dir "$PIP_CACHE_DIR" -r "$CONTRIB/deterministic-build/requirements-binaries.txt" |
|
"$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" |
|
|
|
"$python" -m pip install --no-build-isolation --no-dependencies --no-warn-script-location \ |
|
--cache-dir "$PIP_CACHE_DIR" "$PROJECT_ROOT" |
|
|
|
# was only needed during build time, not runtime |
|
"$python" -m pip uninstall -y Cython |
|
|
|
|
|
info "copying zbar" |
|
cp "/usr/lib/x86_64-linux-gnu/libzbar.so.0" "$APPDIR/usr/lib/libzbar.so.0" |
|
|
|
|
|
info "desktop integration." |
|
cp "$PROJECT_ROOT/electrum.desktop" "$APPDIR/electrum.desktop" |
|
cp "$PROJECT_ROOT/electrum/gui/icons/electrum.png" "$APPDIR/electrum.png" |
|
|
|
|
|
# add launcher |
|
cp "$CONTRIB_APPIMAGE/apprun.sh" "$APPDIR/AppRun" |
|
|
|
info "finalizing AppDir." |
|
( |
|
export PKG2AICOMMIT="$PKG2APPIMAGE_COMMIT" |
|
. "$CACHEDIR/functions.sh" |
|
|
|
cd "$APPDIR" |
|
# copy system dependencies |
|
copy_deps; copy_deps; copy_deps |
|
move_lib |
|
|
|
# apply global appimage blacklist to exclude stuff |
|
# move usr/include out of the way to preserve usr/include/python3.9m. |
|
mv usr/include usr/include.tmp |
|
delete_blacklisted |
|
mv usr/include.tmp usr/include |
|
) || fail "Could not finalize 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" |
|
# 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" |
|
# 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" |
|
) |
|
|
|
info "stripping binaries from debug symbols." |
|
# "-R .note.gnu.build-id" also strips the build id |
|
# "-R .comment" also strips the GCC version information |
|
strip_binaries() |
|
{ |
|
chmod u+w -R "$APPDIR" |
|
{ |
|
printf '%s\0' "$APPDIR/usr/bin/python3.9" |
|
find "$APPDIR" -type f -regex '.*\.so\(\.[0-9.]+\)?$' -print0 |
|
} | xargs -0 --no-run-if-empty --verbose strip -R .note.gnu.build-id -R .comment |
|
} |
|
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/{share,include} |
|
PYDIR="$APPDIR"/usr/lib/python3.9 |
|
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"/site-packages/{opt,pip,setuptools,wheel} |
|
rm -rf "$PYDIR"/site-packages/Cryptodome/SelfTest |
|
rm -rf "$PYDIR"/site-packages/{psutil,qrcode,websocket}/tests |
|
# rm lots of unused parts of Qt/PyQt. (assuming PyQt 5.15.3+ layout) |
|
for component in connectivity declarative help location multimedia quickcontrols2 serialport webengine websockets xmlpatterns ; do |
|
rm -rf "$PYDIR"/site-packages/PyQt5/Qt5/translations/qt${component}_* |
|
rm -rf "$PYDIR"/site-packages/PyQt5/Qt5/resources/qt${component}_* |
|
done |
|
rm -rf "$PYDIR"/site-packages/PyQt5/Qt5/{qml,libexec} |
|
rm -rf "$PYDIR"/site-packages/PyQt5/{pyrcc*.so,pylupdate*.so,uic} |
|
rm -rf "$PYDIR"/site-packages/PyQt5/Qt5/plugins/{bearer,gamepads,geometryloaders,geoservices,playlistformats,position,renderplugins,sceneparsers,sensors,sqldrivers,texttospeech,webview} |
|
for component in Bluetooth Concurrent Designer Help Location NetworkAuth Nfc Positioning PositioningQuick Qml Quick Sensors SerialPort Sql Test Web Xml ; do |
|
rm -rf "$PYDIR"/site-packages/PyQt5/Qt5/lib/libQt5${component}* |
|
rm -rf "$PYDIR"/site-packages/PyQt5/Qt${component}* |
|
done |
|
rm -rf "$PYDIR"/site-packages/PyQt5/Qt.so |
|
|
|
# these are deleted as they were not deterministic; and are not needed anyway |
|
find "$APPDIR" -path '*/__pycache__*' -delete |
|
# although note that *.dist-info might be needed by certain packages... |
|
# e.g. importlib-metadata, see https://gitlab.com/python-devs/importlib_metadata/issues/71 |
|
rm -rf "$PYDIR"/site-packages/*.dist-info/ |
|
rm -rf "$PYDIR"/site-packages/*.egg-info/ |
|
|
|
|
|
find -exec touch -h -d '2000-11-11T11:11:11+00:00' {} + |
|
|
|
|
|
info "creating the AppImage." |
|
( |
|
cd "$BUILDDIR" |
|
cp "$CACHEDIR/appimagetool" "$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" |
|
"$CACHEDIR/appimagetool_copy" --appimage-extract |
|
# We build a small wrapper for mksquashfs that removes the -mkfs-time option |
|
# as it conflicts with SOURCE_DATE_EPOCH. |
|
mv "$BUILDDIR/squashfs-root/usr/lib/appimagekit/mksquashfs" "$BUILDDIR/squashfs-root/usr/lib/appimagekit/mksquashfs_orig" |
|
cat > "$BUILDDIR/squashfs-root/usr/lib/appimagekit/mksquashfs" << EOF |
|
#!/bin/sh |
|
args=\$(echo "\$@" | sed -e 's/-mkfs-time 0//') |
|
"$BUILDDIR/squashfs-root/usr/lib/appimagekit/mksquashfs_orig" \$args |
|
EOF |
|
chmod +x "$BUILDDIR/squashfs-root/usr/lib/appimagekit/mksquashfs" |
|
env VERSION="$VERSION" ARCH=x86_64 ./squashfs-root/AppRun --no-appstream --verbose "$APPDIR" "$APPIMAGE" |
|
) |
|
|
|
|
|
info "done." |
|
ls -la "$DISTDIR" |
|
sha256sum "$DISTDIR"/*
|
|
|