- this merges `contrib/deterministic-build/locale` and `electrum/locale`
- it is now once again possible have translations when running from a local git clone
- which was already possible in the past before crowdin removed their unauthenticated APIs
- see https://github.com/spesmilo/electrum/issues/9531
- however, the translations available are the often-old frozen strings from electrum-locale
- while previously one could just download the latest strings from crowdin
227 lines
9.1 KiB
Bash
Executable File
227 lines
9.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
set -e
|
|
|
|
# Parameterize
|
|
PYTHON_VERSION=3.11.9
|
|
PY_VER_MAJOR="3.11" # as it appears in fs paths
|
|
PACKAGE=Electrum
|
|
GIT_REPO=https://github.com/spesmilo/electrum
|
|
|
|
export GCC_STRIP_BINARIES="1"
|
|
export PYTHONDONTWRITEBYTECODE=1 # don't create __pycache__/ folders with .pyc files
|
|
|
|
|
|
. "$(dirname "$0")/../build_tools_util.sh"
|
|
|
|
|
|
CONTRIB_OSX="$(dirname "$(realpath "$0")")"
|
|
CONTRIB="$CONTRIB_OSX/.."
|
|
PROJECT_ROOT="$CONTRIB/.."
|
|
CACHEDIR="$CONTRIB_OSX/.cache"
|
|
export DLL_TARGET_DIR="$CACHEDIR/dlls"
|
|
|
|
mkdir -p "$CACHEDIR" "$DLL_TARGET_DIR"
|
|
|
|
cd "$PROJECT_ROOT"
|
|
|
|
git -C "$PROJECT_ROOT" rev-parse 2>/dev/null || fail "Building outside a git clone is not supported."
|
|
|
|
|
|
which brew > /dev/null 2>&1 || fail "Please install brew from https://brew.sh/ to continue"
|
|
which xcodebuild > /dev/null 2>&1 || fail "Please install xcode command line tools to continue"
|
|
|
|
|
|
info "Installing Python $PYTHON_VERSION"
|
|
PKG_FILE="python-${PYTHON_VERSION}-macos11.pkg"
|
|
if [ ! -f "$CACHEDIR/$PKG_FILE" ]; then
|
|
curl -o "$CACHEDIR/$PKG_FILE" "https://www.python.org/ftp/python/${PYTHON_VERSION}/$PKG_FILE"
|
|
fi
|
|
echo "b6cfdee2571ca56ee895043ca1e7110fb78a878cee3eb0c21accb2de34d24b55 $CACHEDIR/$PKG_FILE" | shasum -a 256 -c \
|
|
|| fail "python pkg checksum mismatched"
|
|
sudo installer -pkg "$CACHEDIR/$PKG_FILE" -target / \
|
|
|| fail "failed to install python"
|
|
|
|
# sanity check "python3" has the version we just installed.
|
|
FOUND_PY_VERSION=$(python3 -c 'import sys; print(".".join(map(str, sys.version_info[:3])))')
|
|
if [[ "$FOUND_PY_VERSION" != "$PYTHON_VERSION" ]]; then
|
|
fail "python version mismatch: $FOUND_PY_VERSION != $PYTHON_VERSION"
|
|
fi
|
|
|
|
break_legacy_easy_install
|
|
|
|
# create a fresh virtualenv
|
|
# This helps to avoid older versions of pip-installed dependencies interfering with the build.
|
|
VENV_DIR="$CONTRIB_OSX/build-venv"
|
|
rm -rf "$VENV_DIR"
|
|
python3 -m venv $VENV_DIR
|
|
source $VENV_DIR/bin/activate
|
|
|
|
# don't add debug info to compiled C files (e.g. when pip calls setuptools/wheel calls gcc)
|
|
# see https://github.com/pypa/pip/issues/6505#issuecomment-526613584
|
|
# note: this does not seem sufficient when cython is involved (although it is on linux, just not on mac... weird.)
|
|
# see additional "strip" pass on built files later in the file.
|
|
export CFLAGS="-g0"
|
|
|
|
# Do not build universal binaries. The default on macos 11+ and xcode 12+ is "-arch arm64 -arch x86_64"
|
|
# but with that e.g. "hid.cpython-310-darwin.so" is not reproducible as built by clang.
|
|
export ARCHFLAGS="-arch x86_64"
|
|
|
|
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
|
|
# - PyQt6, 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...)
|
|
python3 -m pip install --no-build-isolation --no-dependencies --no-warn-script-location \
|
|
-Ir ./contrib/deterministic-build/requirements-build-base.txt \
|
|
|| fail "Could not install build dependencies (base)"
|
|
python3 -m pip install --no-build-isolation --no-dependencies --no-binary :all: --no-warn-script-location \
|
|
-Ir ./contrib/deterministic-build/requirements-build-mac.txt \
|
|
|| fail "Could not install build dependencies (mac)"
|
|
|
|
info "Installing some build-time deps for compilation..."
|
|
brew install autoconf automake libtool gettext coreutils pkgconfig
|
|
|
|
info "Building PyInstaller."
|
|
PYINSTALLER_REPO="https://github.com/pyinstaller/pyinstaller.git"
|
|
PYINSTALLER_COMMIT="5d7a0449ecea400eccbbb30d5fcef27d72f8f75d"
|
|
# ^ tag "v6.6.0"
|
|
(
|
|
if [ -f "$CACHEDIR/pyinstaller/PyInstaller/bootloader/Darwin-64bit/runw" ]; then
|
|
info "pyinstaller already built, skipping"
|
|
exit 0
|
|
fi
|
|
cd "$PROJECT_ROOT"
|
|
ELECTRUM_COMMIT_HASH=$(git rev-parse HEAD)
|
|
cd "$CACHEDIR"
|
|
rm -rf pyinstaller
|
|
mkdir pyinstaller
|
|
cd pyinstaller
|
|
# Shallow clone
|
|
git init
|
|
git remote add origin $PYINSTALLER_REPO
|
|
git fetch --depth 1 origin $PYINSTALLER_COMMIT
|
|
git checkout -b pinned "${PYINSTALLER_COMMIT}^{commit}"
|
|
rm -fv PyInstaller/bootloader/Darwin-*/run* || true
|
|
# add reproducible randomness. this ensures we build a different bootloader for each commit.
|
|
# if we built the same one for all releases, that might also get anti-virus false positives
|
|
echo "const char *electrum_tag = \"tagged by Electrum@$ELECTRUM_COMMIT_HASH\";" >> ./bootloader/src/pyi_main.c
|
|
pushd bootloader
|
|
# compile bootloader
|
|
python3 ./waf all CFLAGS="-static"
|
|
popd
|
|
# sanity check bootloader is there:
|
|
[[ -e "PyInstaller/bootloader/Darwin-64bit/runw" ]] || fail "Could not find runw in target dir!"
|
|
) || fail "PyInstaller build failed"
|
|
info "Installing PyInstaller."
|
|
python3 -m pip install --no-build-isolation --no-dependencies --no-warn-script-location "$CACHEDIR/pyinstaller"
|
|
|
|
info "Using these versions for building $PACKAGE:"
|
|
sw_vers
|
|
python3 --version
|
|
echo -n "Pyinstaller "
|
|
pyinstaller --version
|
|
|
|
rm -rf ./dist
|
|
|
|
if ! which msgfmt > /dev/null 2>&1; then
|
|
brew install gettext
|
|
brew link --force gettext
|
|
fi
|
|
|
|
info "preparing electrum-locale."
|
|
(
|
|
cd "$PROJECT_ROOT"
|
|
git submodule update --init
|
|
|
|
LOCALE="$PROJECT_ROOT/electrum/locale/"
|
|
cd "$LOCALE"
|
|
git clean -ffxd
|
|
git reset --hard
|
|
"$CONTRIB/build_locale.sh" "$LOCALE/locale" "$LOCALE/locale"
|
|
# we want the binary to have only compiled (.mo) locale files; not source (.po) files
|
|
rm -r locale/*/electrum.po
|
|
) || fail "failed generating locale"
|
|
|
|
|
|
if [ ! -f "$DLL_TARGET_DIR/libsecp256k1.2.dylib" ]; then
|
|
info "Building libsecp256k1 dylib..."
|
|
"$CONTRIB"/make_libsecp256k1.sh || fail "Could not build libsecp"
|
|
else
|
|
info "Skipping libsecp256k1 build: reusing already built dylib."
|
|
fi
|
|
#cp -f "$DLL_TARGET_DIR"/libsecp256k1.*.dylib "$PROJECT_ROOT/electrum" || fail "Could not copy libsecp256k1 dylib"
|
|
|
|
if [ ! -f "$DLL_TARGET_DIR/libzbar.0.dylib" ]; then
|
|
info "Building ZBar dylib..."
|
|
"$CONTRIB"/make_zbar.sh || fail "Could not build ZBar dylib"
|
|
else
|
|
info "Skipping ZBar build: reusing already built dylib."
|
|
fi
|
|
cp -f "$DLL_TARGET_DIR/libzbar.0.dylib" "$PROJECT_ROOT/electrum/" || fail "Could not copy ZBar dylib"
|
|
|
|
if [ ! -f "$DLL_TARGET_DIR/libusb-1.0.dylib" ]; then
|
|
info "Building libusb dylib..."
|
|
"$CONTRIB"/make_libusb.sh || fail "Could not build libusb dylib"
|
|
else
|
|
info "Skipping libusb build: reusing already built dylib."
|
|
fi
|
|
cp -f "$DLL_TARGET_DIR/libusb-1.0.dylib" "$PROJECT_ROOT/electrum/" || fail "Could not copy libusb dylib"
|
|
|
|
|
|
# opt out of compiling C extensions
|
|
export YARL_NO_EXTENSIONS=1
|
|
|
|
export ELECTRUM_ECC_DONT_COMPILE=1
|
|
|
|
info "Installing requirements..."
|
|
python3 -m pip install --no-build-isolation --no-dependencies --no-binary :all: \
|
|
--no-warn-script-location \
|
|
-Ir ./contrib/deterministic-build/requirements.txt \
|
|
|| fail "Could not install requirements"
|
|
|
|
info "Installing hardware wallet requirements..."
|
|
python3 -m pip install --no-build-isolation --no-dependencies --no-binary :all: --only-binary cryptography \
|
|
--no-warn-script-location \
|
|
-Ir ./contrib/deterministic-build/requirements-hw.txt \
|
|
|| fail "Could not install hardware wallet requirements"
|
|
|
|
info "Installing dependencies specific to binaries..."
|
|
python3 -m pip install --no-build-isolation --no-dependencies --no-binary :all: --only-binary PyQt6,PyQt6-Qt6,cryptography \
|
|
--no-warn-script-location \
|
|
-Ir ./contrib/deterministic-build/requirements-binaries-mac.txt \
|
|
|| fail "Could not install dependencies specific to binaries"
|
|
|
|
info "Building $PACKAGE..."
|
|
python3 -m pip install --no-build-isolation --no-dependencies \
|
|
--no-warn-script-location . > /dev/null || fail "Could not build $PACKAGE"
|
|
# pyinstaller needs to be able to "import electrum_ecc", for which we need libsecp256k1:
|
|
# (or could try "pip install -e" instead)
|
|
cp "$DLL_TARGET_DIR"/libsecp256k1.*.dylib "$VENV_DIR/lib/python$PY_VER_MAJOR/site-packages/electrum_ecc/"
|
|
|
|
# strip debug symbols of some compiled libs
|
|
# - hidapi (hid.cpython-39-darwin.so) in particular is not reproducible without this
|
|
find "$VENV_DIR/lib/python$PY_VER_MAJOR/site-packages/" -type f -name '*.so' -print0 \
|
|
| xargs -0 -t strip -x
|
|
|
|
info "Faking timestamps..."
|
|
find . -exec touch -t '200101220000' {} + || true
|
|
|
|
VERSION=$(git describe --tags --dirty --always)
|
|
|
|
info "Building binary"
|
|
ELECTRUM_VERSION=$VERSION pyinstaller --noconfirm --clean contrib/osx/osx.spec || fail "Could not build binary"
|
|
|
|
info "Finished building unsigned dist/${PACKAGE}.app. This hash should be reproducible:"
|
|
find "dist/${PACKAGE}.app" -type f -print0 | sort -z | xargs -0 shasum -a 256 | shasum -a 256
|
|
|
|
info "Creating unsigned .DMG"
|
|
hdiutil create -fs HFS+ -volname $PACKAGE -srcfolder dist/$PACKAGE.app dist/electrum-$VERSION-unsigned.dmg || fail "Could not create .DMG"
|
|
|
|
info "App was built successfully but was not code signed. Users may get security warnings from macOS."
|
|
info "Now you also need to run sign_osx.sh to codesign/notarize the binary."
|