From b6e4ec8f95c5f405dc4c0b6750df076e56740ae0 Mon Sep 17 00:00:00 2001 From: SomberNight Date: Wed, 11 Jun 2025 21:32:39 +0000 Subject: [PATCH] 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 --- contrib/build-wine/pyinstaller.spec | 58 +++++++++++++---------------- contrib/osx/pyinstaller.spec | 56 +++++++++++++--------------- 2 files changed, 50 insertions(+), 64 deletions(-) diff --git a/contrib/build-wine/pyinstaller.spec b/contrib/build-wine/pyinstaller.spec index 82702425a..c127e7936 100644 --- a/contrib/build-wine/pyinstaller.spec +++ b/contrib/build-wine/pyinstaller.spec @@ -52,6 +52,28 @@ 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}", @@ -68,7 +90,9 @@ a = Analysis([f"{PROJECT_ROOT}/{MAIN_SCRIPT}", binaries=binaries, datas=datas, hiddenimports=hiddenimports, - hookspath=[]) + hookspath=[], + excludes=excludes, + ) # http://stackoverflow.com/questions/19055089/pyinstaller-onefile-warning-pyconfig-h-when-importing-scipy-or-scipy-signal @@ -77,38 +101,6 @@ for d in a.datas: a.datas.remove(d) break -# Strip out parts of Qt that we never use. Reduces binary size by tens of MBs. see #4815 -qt_bins2remove=( - r'pyqt6\qt6\qml', - r'pyqt6\qt6\bin\qt6quick', - r'pyqt6\qt6\bin\qt6qml', - r'pyqt6\qt6\bin\qt6multimediaquick', - r'pyqt6\qt6\bin\qt6pdfquick', - r'pyqt6\qt6\bin\qt6positioning', - r'pyqt6\qt6\bin\qt6spatialaudio', - r'pyqt6\qt6\bin\qt6shadertools', - r'pyqt6\qt6\bin\qt6sensors', - r'pyqt6\qt6\bin\qt6web', - r'pyqt6\qt6\bin\qt6test', -) -print("Removing Qt binaries:", *qt_bins2remove) -for x in a.binaries.copy(): - for r in qt_bins2remove: - if x[0].lower().startswith(r): - a.binaries.remove(x) - print('----> Removed x =', x) - -qt_data2remove=( - r'pyqt6\qt6\translations\qtwebengine_locales', - r'pyqt6\qt6\qml', -) -print("Removing Qt datas:", *qt_data2remove) -for x in a.datas.copy(): - for r in qt_data2remove: - if x[0].lower().startswith(r): - a.datas.remove(x) - print('----> Removed x =', x) - # hotfix for #3171 (pre-Win10 binaries) a.binaries = [x for x in a.binaries if not x[1].lower().startswith(r'c:\windows')] diff --git a/contrib/osx/pyinstaller.spec b/contrib/osx/pyinstaller.spec index 1b94179ce..b1c0a6151 100644 --- a/contrib/osx/pyinstaller.spec +++ b/contrib/osx/pyinstaller.spec @@ -55,6 +55,28 @@ 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}", @@ -71,7 +93,9 @@ a = Analysis([f"{PROJECT_ROOT}/{MAIN_SCRIPT}", binaries=binaries, datas=datas, hiddenimports=hiddenimports, - hookspath=[]) + hookspath=[], + excludes=excludes, + ) # http://stackoverflow.com/questions/19055089/pyinstaller-onefile-warning-pyconfig-h-when-importing-scipy-or-scipy-signal @@ -80,36 +104,6 @@ for d in a.datas: a.datas.remove(d) break -# Strip out parts of Qt that we never use. Reduces binary size by tens of MBs. see #4815 -qt_bins2remove=( - 'pyqt6/qt6/qml', - 'pyqt6/qt6/lib/qtqml', - 'pyqt6/qt6/lib/qtquick', - 'pyqt6/qt6/lib/qtshadertools', - 'pyqt6/qt6/lib/qtspatialaudio', - 'pyqt6/qt6/lib/qtmultimediaquick', - 'pyqt6/qt6/lib/qtweb', - 'pyqt6/qt6/lib/qtpositioning', - 'pyqt6/qt6/lib/qtsensors', - 'pyqt6/qt6/lib/qtpdfquick', - 'pyqt6/qt6/lib/qttest', -) -print("Removing Qt binaries:", *qt_bins2remove) -for x in a.binaries.copy(): - for r in qt_bins2remove: - if x[0].lower().startswith(r): - a.binaries.remove(x) - print('----> Removed x =', x) - -qt_data2remove=( - 'pyqt6/qt6/qml', -) -print("Removing Qt datas:", *qt_data2remove) -for x in a.datas.copy(): - for r in qt_data2remove: - if x[0].lower().startswith(r): - a.datas.remove(x) - print('----> Removed x =', x) pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)