1
0
Files
electrum/contrib/osx/pyinstaller.spec
SomberNight b6e4ec8f95 mac build: fix broken symlinks inside .app, due to rm-ed qt parts
probably since qt6 migration many symlinks inside the .app in bundled PyQt were broken:
```
$ cp -r $HOME/electrum/dist/Electrum.app Electrum-ghost3.app
cp: /Users/vagrant/electrum/dist/Electrum.app/Contents/Resources/PyQt6/Qt6/lib/QtMultimediaQuick.framework/QtMultimediaQuick: No such file or directory
cp: /Users/vagrant/electrum/dist/Electrum.app/Contents/Resources/PyQt6/Qt6/lib/QtQuickTimeline.framework/QtQuickTimeline: No such file or directory
cp: /Users/vagrant/electrum/dist/Electrum.app/Contents/Resources/PyQt6/Qt6/lib/QtQuickControls2.framework/QtQuickControls2: No such file or directory
```
We were stripping out lots of datas/binaries from Qt from the mac build artifact, leaving behind dangling symlinks.

Instead of adding more hacks on top of the current hacks to also rm the dangling links, I tried to clean up this blacklisting.

There was no issue re the Windows build, no symlinks there, but I like to keep these two spec files in sync.

-----

Some numbers:
- mac:
  - without any exclusions at all, the mac .dmg is 80 MiB.
  - with these exclusions it is 57 MiB.
- win:
  - (haven't built without exclusions.)
  - with the previous stripping strategy, exes were 68M/68M/50M
  - with these exclusions, exes are 66M/66M/50M
2025-06-11 21:35:44 +00:00

144 lines
4.4 KiB
Python

# -*- mode: python -*-
import sys
import os
from typing import TYPE_CHECKING
from PyInstaller.utils.hooks import collect_data_files, collect_submodules, collect_dynamic_libs, copy_metadata
if TYPE_CHECKING:
from PyInstaller.building.build_main import Analysis, PYZ, EXE, BUNDLE
PACKAGE_NAME='Electrum.app'
PYPKG='electrum'
MAIN_SCRIPT='run_electrum'
PROJECT_ROOT = os.path.abspath(".")
ICONS_FILE=f"{PROJECT_ROOT}/{PYPKG}/gui/icons/electrum.icns"
VERSION = os.environ.get("ELECTRUM_VERSION")
if not VERSION:
raise Exception('no version')
block_cipher = None
# see https://github.com/pyinstaller/pyinstaller/issues/2005
hiddenimports = []
hiddenimports += collect_submodules('pkg_resources') # workaround for https://github.com/pypa/setuptools/issues/1963
hiddenimports += collect_submodules(f"{PYPKG}.plugins")
binaries = []
# Workaround for "Retro Look":
binaries += [b for b in collect_dynamic_libs('PyQt6') if 'macstyle' in b[0]]
# add libsecp256k1, libusb, etc:
binaries += [(f"{PROJECT_ROOT}/{PYPKG}/*.dylib", ".")]
datas = [
(f"{PROJECT_ROOT}/{PYPKG}/*.json", PYPKG),
(f"{PROJECT_ROOT}/{PYPKG}/lnwire/*.csv", f"{PYPKG}/lnwire"),
(f"{PROJECT_ROOT}/{PYPKG}/wordlist/english.txt", f"{PYPKG}/wordlist"),
(f"{PROJECT_ROOT}/{PYPKG}/wordlist/slip39.txt", f"{PYPKG}/wordlist"),
(f"{PROJECT_ROOT}/{PYPKG}/chains", f"{PYPKG}/chains"),
(f"{PROJECT_ROOT}/{PYPKG}/locale", f"{PYPKG}/locale"),
(f"{PROJECT_ROOT}/{PYPKG}/plugins", f"{PYPKG}/plugins"),
(f"{PROJECT_ROOT}/{PYPKG}/gui/icons", f"{PYPKG}/gui/icons"),
(f"{PROJECT_ROOT}/{PYPKG}/gui/fonts", f"{PYPKG}/gui/fonts"),
]
datas += collect_data_files(f"{PYPKG}.plugins")
datas += collect_data_files('trezorlib') # TODO is this needed? and same question for other hww libs
datas += collect_data_files('safetlib')
datas += collect_data_files('ckcc')
datas += collect_data_files('bitbox02')
# some deps rely on importlib metadata
datas += copy_metadata('slip10') # from trezor->slip10
# Exclude parts of Qt that we never use. Reduces binary size by tens of MBs. see #4815
excludes = [
"PyQt6.QtBluetooth",
"PyQt6.QtDesigner",
"PyQt6.QtNetwork",
"PyQt6.QtNfc",
"PyQt6.QtPositioning",
"PyQt6.QtQml",
"PyQt6.QtQuick",
"PyQt6.QtQuick3D",
"PyQt6.QtQuickWidgets",
"PyQt6.QtRemoteObjects",
"PyQt6.QtSensors",
"PyQt6.QtSerialPort",
"PyQt6.QtSpatialAudio",
"PyQt6.QtSql",
"PyQt6.QtTest",
"PyQt6.QtTextToSpeech",
"PyQt6.QtWebChannel",
"PyQt6.QtWebSockets",
"PyQt6.QtXml",
]
# We don't put these files in to actually include them in the script but to make the Analysis method scan them for imports
a = Analysis([f"{PROJECT_ROOT}/{MAIN_SCRIPT}",
f"{PROJECT_ROOT}/{PYPKG}/gui/qt/main_window.py",
f"{PROJECT_ROOT}/{PYPKG}/gui/qt/qrreader/qtmultimedia/camera_dialog.py",
f"{PROJECT_ROOT}/{PYPKG}/gui/text.py",
f"{PROJECT_ROOT}/{PYPKG}/util.py",
f"{PROJECT_ROOT}/{PYPKG}/wallet.py",
f"{PROJECT_ROOT}/{PYPKG}/simple_config.py",
f"{PROJECT_ROOT}/{PYPKG}/bitcoin.py",
f"{PROJECT_ROOT}/{PYPKG}/dnssec.py",
f"{PROJECT_ROOT}/{PYPKG}/commands.py",
],
binaries=binaries,
datas=datas,
hiddenimports=hiddenimports,
hookspath=[],
excludes=excludes,
)
# http://stackoverflow.com/questions/19055089/pyinstaller-onefile-warning-pyconfig-h-when-importing-scipy-or-scipy-signal
for d in a.datas:
if 'pyconfig' in d[0]:
a.datas.remove(d)
break
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
exe = EXE(
pyz,
a.scripts,
exclude_binaries=True,
name=MAIN_SCRIPT,
debug=False,
strip=False,
upx=True,
icon=ICONS_FILE,
console=False,
target_arch='x86_64', # TODO investigate building 'universal2'
)
app = BUNDLE(
exe,
a.binaries,
a.zipfiles,
a.datas,
version=VERSION,
name=PACKAGE_NAME,
icon=ICONS_FILE,
bundle_identifier=None,
info_plist={
'NSHighResolutionCapable': 'True',
'NSSupportsAutomaticGraphicsSwitching': 'True',
'CFBundleURLTypes':
[{
'CFBundleURLName': 'bitcoin',
'CFBundleURLSchemes': ['bitcoin', 'lightning', ],
}],
'LSMinimumSystemVersion': '11',
'NSCameraUsageDescription': 'Electrum would like to access the camera to scan for QR codes',
},
)