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.

252 lines
9.3 KiB

#!/usr/bin/env bash
set -e
# Parameterize
PYTHON_VERSION=3.9.11
BUILDDIR=/tmp/electrum-build
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"
mkdir -p "$CACHEDIR"
cd "$PROJECT_ROOT"
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"
# Code Signing: See https://developer.apple.com/library/archive/documentation/Security/Conceptual/CodeSigningGuide/Procedures/Procedures.html
if [ -n "$CODESIGN_CERT" ]; then
# Test the identity is valid for signing by doing this hack. There is no other way to do this.
cp -f /bin/ls ./CODESIGN_TEST
set +e
codesign -s "$CODESIGN_CERT" --dryrun -f ./CODESIGN_TEST > /dev/null 2>&1
res=$?
set -e
rm -f ./CODESIGN_TEST
if ((res)); then
fail "Code signing identity \"$CODESIGN_CERT\" appears to be invalid."
fi
unset res
info "Code signing enabled using identity \"$CODESIGN_CERT\""
else
warn "Code signing DISABLED. Specify a valid macOS Developer identity installed on the system to enable signing."
fi
function DoCodeSignMaybe { # ARGS: infoName fileOrDirName
infoName="$1"
file="$2"
deep=""
if [ -z "$CODESIGN_CERT" ]; then
# no cert -> we won't codesign
return
fi
if [ -d "$file" ]; then
deep="--deep"
fi
if [ -z "$infoName" ] || [ -z "$file" ] || [ ! -e "$file" ]; then
fail "Argument error to internal function DoCodeSignMaybe()"
fi
hardened_arg="--entitlements=${CONTRIB_OSX}/entitlements.plist -o runtime"
info "Code signing ${infoName}..."
codesign -f -v $deep -s "$CODESIGN_CERT" $hardened_arg "$file" || fail "Could not code sign ${infoName}"
}
info "Installing Python $PYTHON_VERSION"
PKG_FILE="python-${PYTHON_VERSION}-macosx10.9.pkg"
if [ ! -f "$CACHEDIR/$PKG_FILE" ]; then
curl -o "$CACHEDIR/$PKG_FILE" "https://www.python.org/ftp/python/${PYTHON_VERSION}/$PKG_FILE"
fi
echo "c2073d44c404c661dadbf0cbda55c6e7d681baba9178ed1bdb126d34caa898a9 $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
info "Installing build dependencies"
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-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="40c9abce2d8de879e414fd377c933dccaab1e156"
# ^ tag "4.2"
# TODO test newer versions of pyinstaller for build-reproducibility.
# we are using this version for now due to change in code-signing behaviour
# (https://github.com/pyinstaller/pyinstaller/pull/5581)
(
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!"
mac build: add workaround for installing pyinstaller with setuptools 61 - the mac build broke with https://github.com/spesmilo/electrum/commit/184e122c36555b23bd466cb7a2521c09a09ce6cc - upstream fix is at https://github.com/pyinstaller/pyinstaller/pull/6701 however, we are using an old version of pyinstaller atm, so this workaround is easier log excerpt of failed build: ``` 💬 INFO: Installing PyInstaller. Processing ./contrib/osx/.cache/pyinstaller Preparing metadata (pyproject.toml) ... error error: subprocess-exited-with-error × Preparing metadata (pyproject.toml) did not run successfully. │ exit code: 1 ╰─> [42 lines of output] Traceback (most recent call last): File "/Users/user/wspace/electrum/contrib/osx/build-venv/lib/python3.9/site-packages/pip/_vendor/pep517/in_process/_in_process.py", line 363, in <module> main() File "/Users/user/wspace/electrum/contrib/osx/build-venv/lib/python3.9/site-packages/pip/_vendor/pep517/in_process/_in_process.py", line 345, in main json_out['return_val'] = hook(**hook_input['kwargs']) File "/Users/user/wspace/electrum/contrib/osx/build-venv/lib/python3.9/site-packages/pip/_vendor/pep517/in_process/_in_process.py", line 164, in prepare_metadata_for_build_wheel return hook(metadata_directory, config_settings) File "/Users/user/wspace/electrum/contrib/osx/build-venv/lib/python3.9/site-packages/setuptools/build_meta.py", line 188, in prepare_metadata_for_build_wheel self.run_setup() File "/Users/user/wspace/electrum/contrib/osx/build-venv/lib/python3.9/site-packages/setuptools/build_meta.py", line 281, in run_setup super(_BuildMetaLegacyBackend, File "/Users/user/wspace/electrum/contrib/osx/build-venv/lib/python3.9/site-packages/setuptools/build_meta.py", line 174, in run_setup exec(compile(code, __file__, 'exec'), locals()) File "setup.py", line 75, in <module> setup( File "/Users/user/wspace/electrum/contrib/osx/build-venv/lib/python3.9/site-packages/setuptools/__init__.py", line 87, in setup return distutils.core.setup(**attrs) File "/Users/user/wspace/electrum/contrib/osx/build-venv/lib/python3.9/site-packages/setuptools/_distutils/core.py", line 122, in setup dist.parse_config_files() File "/Users/user/wspace/electrum/contrib/osx/build-venv/lib/python3.9/site-packages/setuptools/dist.py", line 850, in parse_config_files setupcfg.parse_configuration( File "/Users/user/wspace/electrum/contrib/osx/build-venv/lib/python3.9/site-packages/setuptools/config/setupcfg.py", line 167, in parse_configuration meta.parse() File "/Users/user/wspace/electrum/contrib/osx/build-venv/lib/python3.9/site-packages/setuptools/config/setupcfg.py", line 446, in parse section_parser_method(section_options) File "/Users/user/wspace/electrum/contrib/osx/build-venv/lib/python3.9/site-packages/setuptools/config/setupcfg.py", line 417, in parse_section self[name] = value File "/Users/user/wspace/electrum/contrib/osx/build-venv/lib/python3.9/site-packages/setuptools/config/setupcfg.py", line 238, in __setitem__ value = parser(value) File "/Users/user/wspace/electrum/contrib/osx/build-venv/lib/python3.9/site-packages/setuptools/config/setupcfg.py", line 552, in _parse_version return expand.version(self._parse_attr(value, self.package_dir, self.root_dir)) File "/Users/user/wspace/electrum/contrib/osx/build-venv/lib/python3.9/site-packages/setuptools/config/setupcfg.py", line 372, in _parse_attr return expand.read_attr(attr_desc, package_dir, root_dir) File "/Users/user/wspace/electrum/contrib/osx/build-venv/lib/python3.9/site-packages/setuptools/config/expand.py", line 194, in read_attr module = _load_spec(spec, module_name) File "/Users/user/wspace/electrum/contrib/osx/build-venv/lib/python3.9/site-packages/setuptools/config/expand.py", line 214, in _load_spec spec.loader.exec_module(module) # type: ignore File "<frozen importlib._bootstrap_external>", line 850, in exec_module File "<frozen importlib._bootstrap>", line 228, in _call_with_frames_removed File "/Users/user/wspace/electrum/contrib/osx/.cache/pyinstaller/PyInstaller.py", line 16, in <module> from PyInstaller.__main__ import run ModuleNotFoundError: No module named 'PyInstaller.__main__'; 'PyInstaller' is not a package [end of output] note: This error originates from a subprocess, and is likely not a problem with pip. error: metadata-generation-failed × Encountered error while generating package metadata. ╰─> See above for output. note: This is an issue with the package mentioned above, not pip. hint: See above for details. ```
3 years ago
rm pyinstaller.py # workaround for https://github.com/pyinstaller/pyinstaller/pull/6701
) || 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
git submodule update --init
rm -rf "$BUILDDIR" > /dev/null 2>&1
mkdir "$BUILDDIR"
info "generating locale"
(
if ! which msgfmt > /dev/null 2>&1; then
brew install gettext
brew link --force gettext
fi
cd "$CONTRIB"/deterministic-build/electrum-locale
# 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
) || fail "failed generating locale"
if [ ! -f "$PROJECT_ROOT"/electrum/libsecp256k1.0.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 "$PROJECT_ROOT"/electrum/libsecp256k1.0.dylib "$CONTRIB"/osx
qt: new qrreader using QtMultimedia; drop CalinsQRReader(mac) This commit ports the work of EchterAgo and cculianu from Electron-Cash, to implement a new toolchain to scan qr codes. Previously, on Linux and Win, we have been using zbar to access the camera and read qrcodes; and on macOS we used CalinsQRReader (an objective-C project by cculianu). The new toolchain added here can use QtMultimedia to access the camera, and then feed that image into zbar. When used this way, zbar needs fewer dependencies and is easier to compile, in particular it can be compiled for macOS. The new toolchain works on all three platforms, with some caveats (see code comments in related commits) -- so we also keep the end-to-end zbar toolchain; but at least we can drop CalinsQRReader. The related changes in Electron-Cash are spread over 50+ commits (several PRs and direct pushes to master), but see in particular: https://github.com/Electron-Cash/Electron-Cash/pull/1376 some other interesting links: https://github.com/Electron-Cash/Electron-Cash/commit/b2b737001c8cc41a38fa580ea252a6d24e08f5d5 https://github.com/Electron-Cash/Electron-Cash/commit/163224cf1fad3af63f2d3cbe68a34fb8ff279af6 https://github.com/Electron-Cash/Electron-Cash/commit/3b31e0fcb13f67646228ff42c0dd39d2a0912291 https://github.com/Electron-Cash/Electron-Cash/commit/eda015908e9d6ea9a0adfbda9db55b929c0926ba https://github.com/Electron-Cash/Electron-Cash/pull/1545 https://github.com/Electron-Cash/Electron-Cash/commit/052aa06c23b939adcea07c701f70ae28ebcf9e0a
4 years ago
if [ ! -f "$PROJECT_ROOT"/electrum/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 "$PROJECT_ROOT"/electrum/libzbar.0.dylib "$CONTRIB"/osx
if [ ! -f "$PROJECT_ROOT"/electrum/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 "$PROJECT_ROOT"/electrum/libusb-1.0.dylib "$CONTRIB"/osx
info "Installing requirements..."
python3 -m pip install --no-build-isolation --no-dependencies --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-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-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"
info "Faking timestamps..."
find . -exec touch -t '200101220000' {} + || true
VERSION=`git describe --tags --dirty --always`
info "Building binary"
pyinstaller --noconfirm --ascii --clean --name $VERSION contrib/osx/osx.spec || fail "Could not build binary"
info "Adding bitcoin URI types to Info.plist"
plutil -insert 'CFBundleURLTypes' \
-xml '<array><dict> <key>CFBundleURLName</key> <string>bitcoin</string> <key>CFBundleURLSchemes</key> <array><string>bitcoin</string><string>lightning</string></array> </dict></array>' \
-- dist/$PACKAGE.app/Contents/Info.plist \
|| fail "Could not add keys to Info.plist. Make sure the program 'plutil' exists and is installed."
DoCodeSignMaybe "app bundle" "dist/${PACKAGE}.app"
if [ ! -z "$CODESIGN_CERT" ]; then
if [ ! -z "$APPLE_ID_USER" ]; then
info "Notarizing .app with Apple's central server..."
"${CONTRIB_OSX}/notarize_app.sh" "dist/${PACKAGE}.app" || fail "Could not notarize binary."
else
warn "AppleID details not set! Skipping Apple notarization."
fi
fi
info "Creating .DMG"
hdiutil create -fs HFS+ -volname $PACKAGE -srcfolder dist/$PACKAGE.app dist/electrum-$VERSION.dmg || fail "Could not create .DMG"
DoCodeSignMaybe ".DMG" "dist/electrum-${VERSION}.dmg"
if [ -z "$CODESIGN_CERT" ]; then
warn "App was built successfully but was not code signed. Users may get security warnings from macOS."
warn "Specify a valid code signing identity to enable code signing."
fi