From 262c009c673db2b252cec610ee15214709f96557 Mon Sep 17 00:00:00 2001 From: SomberNight Date: Fri, 17 Nov 2023 11:19:47 +0000 Subject: [PATCH 1/8] pyinstaller build: towards fixing missing "gui/common_qt" folder pyinstaller tries to import electrum and its different submodules at build-time during the "Analysis" phase. sys._GUI_QT_VERSION was not getting set there, and the resulting exception was blocking pyinstaller from discovering that gui/common_qt is being used. at runtime: ``` $ ./dist/Electrum.app/Contents/MacOS/run_electrum 1.53 | E | daemon.Daemon | GUI raised exception: Exception('Error loading trustedcoin plugin: ModuleNotFoundError("No module named \'electrum.gui.common_qt\'")'). shutting down. 1.53 | E | __main__ | daemon.run_gui errored Traceback (most recent call last): File "electrum/plugin.py", line 135, in load_plugin spec.loader.exec_module(module) File "", line 883, in exec_module File "", line 241, in _call_with_frames_removed File "/Users/vagrant/electrum/dist/Electrum.app/Contents/MacOS/electrum/plugins/trustedcoin/qt.py", line 51, in from .common_qt import TrustedcoinPluginQObject File "/Users/vagrant/electrum/dist/Electrum.app/Contents/MacOS/electrum/plugins/trustedcoin/common_qt.py", line 16, in from electrum.gui.common_qt.plugins import PluginQObject ModuleNotFoundError: No module named 'electrum.gui.common_qt' The above exception was the direct cause of the following exception: Traceback (most recent call last): File "run_electrum", line 456, in handle_cmd d.run_gui() File "electrum/daemon.py", line 617, in run_gui self.gui_object = gui.ElectrumGui(config=self.config, daemon=self, plugins=self._plugins) File "electrum/util.py", line 473, in do_profile o = func(*args, **kw_args) File "electrum/gui/qt/__init__.py", line 153, in __init__ self.plugins.load_plugin('trustedcoin') File "electrum/plugin.py", line 138, in load_plugin raise Exception(f"Error loading {name} plugin: {repr(e)}") from e Exception: Error loading trustedcoin plugin: ModuleNotFoundError("No module named 'electrum.gui.common_qt'") ``` --- contrib/build-wine/deterministic.spec | 1 + contrib/osx/osx.spec | 1 + electrum/gui/common_qt/__init__.py | 16 ++++++++++++++++ electrum/gui/common_qt/plugins.py | 8 ++++++-- electrum/plugins/trustedcoin/common_qt.py | 8 ++++++-- 5 files changed, 30 insertions(+), 4 deletions(-) diff --git a/contrib/build-wine/deterministic.spec b/contrib/build-wine/deterministic.spec index c133de4e6..62b471c23 100644 --- a/contrib/build-wine/deterministic.spec +++ b/contrib/build-wine/deterministic.spec @@ -62,6 +62,7 @@ a = Analysis([home+'run_electrum', home+'electrum/bitcoin.py', home+'electrum/dnssec.py', home+'electrum/commands.py', + home+'electrum/plugins/trustedcoin/qt.py', home+'electrum/plugins/cosigner_pool/qt.py', home+'electrum/plugins/trezor/qt.py', home+'electrum/plugins/safe_t/client.py', diff --git a/contrib/osx/osx.spec b/contrib/osx/osx.spec index b4fb53df9..4f3a53acb 100644 --- a/contrib/osx/osx.spec +++ b/contrib/osx/osx.spec @@ -67,6 +67,7 @@ a = Analysis([electrum+ MAIN_SCRIPT, electrum+'electrum/bitcoin.py', electrum+'electrum/dnssec.py', electrum+'electrum/commands.py', + electrum+'electrum/plugins/trustedcoin/qt.py', electrum+'electrum/plugins/cosigner_pool/qt.py', electrum+'electrum/plugins/trezor/qt.py', electrum+'electrum/plugins/safe_t/client.py', diff --git a/electrum/gui/common_qt/__init__.py b/electrum/gui/common_qt/__init__.py index e69de29bb..b2be0c9a4 100644 --- a/electrum/gui/common_qt/__init__.py +++ b/electrum/gui/common_qt/__init__.py @@ -0,0 +1,16 @@ +# Copyright (C) 2023 The Electrum developers +# Distributed under the MIT software license, see the accompanying +# file LICENCE or http://www.opensource.org/licenses/mit-license.php + +import sys + + +# FIXME: remove when both desktop and mobile are Qt6 +def get_qt_major_version() -> int: + _GUI_QT_VERSION = getattr(sys, '_GUI_QT_VERSION', None) + if _GUI_QT_VERSION is None: + # used by pyinstaller when building (analysis phase) + _GUI_QT_VERSION = 5 + if _GUI_QT_VERSION in (5, 6): + return _GUI_QT_VERSION + raise Exception(f"unexpected {_GUI_QT_VERSION=}") diff --git a/electrum/gui/common_qt/plugins.py b/electrum/gui/common_qt/plugins.py index 0934477b4..9edaa9b19 100644 --- a/electrum/gui/common_qt/plugins.py +++ b/electrum/gui/common_qt/plugins.py @@ -1,9 +1,13 @@ import sys -if getattr(sys, '_GUI_QT_VERSION') == 5: # FIXME: remove when both desktop and mobile are Qt6 +from . import get_qt_major_version + +if (qt_ver := get_qt_major_version()) == 5: from PyQt5.QtCore import pyqtSignal, pyqtProperty, QObject -else: +elif qt_ver == 6: from PyQt6.QtCore import pyqtSignal, pyqtProperty, QObject +else: + raise Exception(f"unexpected {qt_ver=}") from electrum.logging import get_logger diff --git a/electrum/plugins/trustedcoin/common_qt.py b/electrum/plugins/trustedcoin/common_qt.py index 31b9954e7..01836d972 100644 --- a/electrum/plugins/trustedcoin/common_qt.py +++ b/electrum/plugins/trustedcoin/common_qt.py @@ -4,10 +4,14 @@ import base64 import sys from typing import TYPE_CHECKING -if getattr(sys, '_GUI_QT_VERSION') == 5: # FIXME: remove when both desktop and mobile are Qt6 +from electrum.gui.common_qt import get_qt_major_version + +if (qt_ver := get_qt_major_version()) == 5: from PyQt5.QtCore import pyqtSignal, pyqtProperty, pyqtSlot -else: +elif qt_ver == 6: from PyQt6.QtCore import pyqtSignal, pyqtProperty, pyqtSlot +else: + raise Exception(f"unexpected {qt_ver=}") from electrum.i18n import _ from electrum.bip32 import BIP32Node From 1451ec936ae34e0c91edeee0bd4219507d10b53c Mon Sep 17 00:00:00 2001 From: SomberNight Date: Fri, 17 Nov 2023 15:57:05 +0000 Subject: [PATCH 2/8] win/mac build: fix pyinstaller missing libsecp during part of Analysis ``` 602 WARNING: Failed to collect submodules for 'pkg_resources._vendor.pyparsing.diagram' because importing 'pkg_resources._vendor.pyparsing.diagram' raised: ModuleNotFoundError: No module named 'railroad' libsecp256k1 library failed to load. exceptions: [FileNotFoundError("Could not find module 'C:\\python3\\lib\\site-packages\\electrum\\libsecp256k1-2.dll' (or one of its dependencies). Try using the full path with constructor syntax."), FileNotFoundError("Could not find module 'C:\\python3\\lib\\site-packages\\electrum\\libsecp256k1-1.dll'(or one of its dependencies). Try using the full path with constructor syntax."), FileNotFoundError("Could not find module 'C:\\python3\\lib\\site-packages\\electrum\\libsecp256k1-0.dll' (or one of its dependencies). Try using the full path with constructor syntax."), FileNotFoundError("Could not find module 'libsecp256k1-2.dll' (or one ofits dependencies). Try using the full path with constructor syntax."), FileNotFoundError("Could not find module 'libsecp256k1-1.dll' (or one of its dependencies). Try using the full path with constructor syntax."), FileNotFoundError("Could not find module 'libsecp256k1-0.dll' (or one of its dependencies). Try using the full path with constructor syntax.")] libsecp256k1 library failed to load. exceptions: [FileNotFoundError("Could not find module 'C:\\python3\\lib\\site-packages\\electrum\\libsecp256k1-2.dll' (or one of its dependencies). Try using the full path with constructor syntax."), FileNotFoundError("Could not find module 'C:\\python3\\lib\\site-packages\\electrum\\libsecp256k1-1.dll'(or one of its dependencies). Try using the full path with constructor syntax."), FileNotFoundError("Could not find module 'C:\\python3\\lib\\site-packages\\electrum\\libsecp256k1-0.dll' (or one of its dependencies). Try using the full path with constructor syntax."), FileNotFoundError("Could not find module 'libsecp256k1-2.dll' (or one ofits dependencies). Try using the full path with constructor syntax."), FileNotFoundError("Could not find module 'libsecp256k1-1.dll' (or one of its dependencies). Try using the full path with constructor syntax."), FileNotFoundError("Could not find module 'libsecp256k1-0.dll' (or one of its dependencies). Try using the full path with constructor syntax.")] libsecp256k1 library failed to load. exceptions: [FileNotFoundError("Could not find module 'C:\\python3\\lib\\site-packages\\electrum\\libsecp256k1-2.dll' (or one of its dependencies). Try using the full path with constructor syntax."), FileNotFoundError("Could not find module 'C:\\python3\\lib\\site-packages\\electrum\\libsecp256k1-1.dll'(or one of its dependencies). Try using the full path with constructor syntax."), FileNotFoundError("Could not find module 'C:\\python3\\lib\\site-packages\\electrum\\libsecp256k1-0.dll' (or one of its dependencies). Try using the full path with constructor syntax."), FileNotFoundError("Could not find module 'libsecp256k1-2.dll' (or one ofits dependencies). Try using the full path with constructor syntax."), FileNotFoundError("Could not find module 'libsecp256k1-1.dll' (or one of its dependencies). Try using the full path with constructor syntax."), FileNotFoundError("Could not find module 'libsecp256k1-0.dll' (or one of its dependencies). Try using the full path with constructor syntax.")] 5921 WARNING: collect_data_files - skipping data collection for module 'electrum.plugins' as it is not a package. ``` --- contrib/build-wine/build-electrum-git.sh | 3 +++ contrib/build-wine/deterministic.spec | 4 +--- contrib/build-wine/prepare-wine.sh | 6 +++--- contrib/osx/make_osx.sh | 3 +++ contrib/osx/osx.spec | 5 +---- 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/contrib/build-wine/build-electrum-git.sh b/contrib/build-wine/build-electrum-git.sh index d772ffc12..15731e02a 100755 --- a/contrib/build-wine/build-electrum-git.sh +++ b/contrib/build-wine/build-electrum-git.sh @@ -50,6 +50,9 @@ pushd $WINEPREFIX/drive_c/electrum # see https://github.com/pypa/pip/issues/2195 -- pip makes a copy of the entire directory info "Pip installing Electrum. This might take a long time if the project folder is large." $WINE_PYTHON -m pip install --no-build-isolation --no-dependencies --no-warn-script-location . +# pyinstaller needs to be able to "import electrum", for which we need libsecp256k1: +# (or could try "pip install -e" instead) +cp electrum/libsecp256k1-*.dll "$WINEPREFIX/drive_c/python3/Lib/site-packages/electrum/" popd diff --git a/contrib/build-wine/deterministic.spec b/contrib/build-wine/deterministic.spec index 62b471c23..5413f4590 100644 --- a/contrib/build-wine/deterministic.spec +++ b/contrib/build-wine/deterministic.spec @@ -31,9 +31,7 @@ binaries = [] # Workaround for "Retro Look": binaries += [b for b in collect_dynamic_libs('PyQt5') if 'qwindowsvista' in b[0]] -binaries += [('C:/tmp/libsecp256k1-2.dll', '.')] -binaries += [('C:/tmp/libusb-1.0.dll', '.')] -binaries += [('C:/tmp/libzbar-0.dll', '.')] +binaries += [(home+'electrum/*.dll', '.')] datas = [ (home+'electrum/*.json', 'electrum'), diff --git a/contrib/build-wine/prepare-wine.sh b/contrib/build-wine/prepare-wine.sh index a65945983..28bc48e02 100755 --- a/contrib/build-wine/prepare-wine.sh +++ b/contrib/build-wine/prepare-wine.sh @@ -53,9 +53,9 @@ $WINE_PYTHON -m pip install --no-build-isolation --no-dependencies --no-binary : # copy already built DLLs -cp "$DLL_TARGET_DIR"/libsecp256k1-*.dll $WINEPREFIX/drive_c/tmp/ || fail "Could not copy libsecp to its destination" -cp "$DLL_TARGET_DIR/libzbar-0.dll" $WINEPREFIX/drive_c/tmp/ || fail "Could not copy libzbar to its destination" -cp "$DLL_TARGET_DIR/libusb-1.0.dll" $WINEPREFIX/drive_c/tmp/ || fail "Could not copy libusb to its destination" +cp "$DLL_TARGET_DIR"/libsecp256k1-*.dll $WINEPREFIX/drive_c/electrum/electrum/ || fail "Could not copy libsecp to its destination" +cp "$DLL_TARGET_DIR/libzbar-0.dll" $WINEPREFIX/drive_c/electrum/electrum/ || fail "Could not copy libzbar to its destination" +cp "$DLL_TARGET_DIR/libusb-1.0.dll" $WINEPREFIX/drive_c/electrum/electrum/ || fail "Could not copy libusb to its destination" info "Building PyInstaller." diff --git a/contrib/osx/make_osx.sh b/contrib/osx/make_osx.sh index 254eb3ccb..308ac16cb 100755 --- a/contrib/osx/make_osx.sh +++ b/contrib/osx/make_osx.sh @@ -223,6 +223,9 @@ python3 -m pip install --no-build-isolation --no-dependencies --no-binary :all: 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", for which we need libsecp256k1: +# (or could try "pip install -e" instead) +cp "$PROJECT_ROOT/electrum"/libsecp256k1.*.dylib "$VENV_DIR/lib/python$PY_VER_MAJOR/site-packages/electrum/" # strip debug symbols of some compiled libs # - hidapi (hid.cpython-39-darwin.so) in particular is not reproducible without this diff --git a/contrib/osx/osx.spec b/contrib/osx/osx.spec index 4f3a53acb..e428963e1 100644 --- a/contrib/osx/osx.spec +++ b/contrib/osx/osx.spec @@ -48,10 +48,7 @@ datas += collect_data_files('keepkeylib') datas += collect_data_files('ckcc') datas += collect_data_files('bitbox02') -# Add libusb so Trezor and Safe-T mini will work -binaries = [(electrum + "electrum/libusb-1.0.dylib", ".")] -binaries += [(electrum + "electrum/libsecp256k1.2.dylib", ".")] -binaries += [(electrum + "electrum/libzbar.0.dylib", ".")] +binaries = [(electrum + "electrum/*.dylib", ".")] # Workaround for "Retro Look": binaries += [b for b in collect_dynamic_libs('PyQt5') if 'macstyle' in b[0]] From 2c3c50a09bf0cadd16256a952e6fed91f12a81f9 Mon Sep 17 00:00:00 2001 From: SomberNight Date: Wed, 22 Nov 2023 12:33:38 +0000 Subject: [PATCH 3/8] mac build: fix pyinstaller erroring due to not being able to import pyqt6 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Not sure why this a problem in the mac build but not in the windows build... Also not sure why this only came up now -- was working before. (I changed a few things in prev commits re the build scripts but I can't see the cause) ``` 34827 INFO: Processing module hooks... 34828 INFO: Loading module hook 'hook-cryptography.py' from '/Users/vagrant/electrum/contrib/osx/build-venv/lib/python3.10/site-packages/_pyinstaller_hooks_contrib/hooks/stdhooks'... 35932 INFO: Loading module hook 'hook-mnemonic.py' from '/Users/vagrant/electrum/contrib/osx/build-venv/lib/python3.10/site-packages/_pyinstaller_hooks_contrib/hooks/stdhooks'... 35937 INFO: Loading module hook 'hook-certifi.py' from '/Users/vagrant/electrum/contrib/osx/build-venv/lib/python3.10/site-packages/_pyinstaller_hooks_contrib/hooks/stdhooks'... 35939 INFO: Loading module hook 'hook-dns.rdata.py' from '/Users/vagrant/electrum/contrib/osx/build-venv/lib/python3.10/site-packages/_pyinstaller_hooks_contrib/hooks/stdhooks'... 36502 INFO: Loading module hook 'hook-usb1.py' from '/Users/vagrant/electrum/contrib/osx/build-venv/lib/python3.10/site-packages/usb1/__pyinstaller'... 36503 INFO: --- libusb1 pyinstaller hook --- 36504 INFO: Added libusb binaries: [] 36505 INFO: Loading module hook 'hook-sqlite3.py' from '/Users/vagrant/electrum/contrib/osx/build-venv/lib/python3.10/site-packages/PyInstaller/hooks'... 36932 INFO: Loading module hook 'hook-pkg_resources.py' from '/Users/vagrant/electrum/contrib/osx/build-venv/lib/python3.10/site-packages/PyInstaller/hooks'... 38303 INFO: Loading module hook 'hook-PyQt5.py' from '/Users/vagrant/electrum/contrib/osx/build-venv/lib/python3.10/site-packages/PyInstaller/hooks'... 38584 WARNING: Hidden import "sip" not found! 38586 INFO: Loading module hook 'hook-xml.etree.cElementTree.py' from '/Users/vagrant/electrum/contrib/osx/build-venv/lib/python3.10/site-packages/PyInstaller/hooks'... 38587 INFO: Loading module hook 'hook-lib2to3.py' from '/Users/vagrant/electrum/contrib/osx/build-venv/lib/python3.10/site-packages/PyInstaller/hooks'... 38678 INFO: Loading module hook 'hook-_tkinter.py' from '/Users/vagrant/electrum/contrib/osx/build-venv/lib/python3.10/site-packages/PyInstaller/hooks'... 39005 INFO: checking Tree 39006 INFO: Building Tree because Tree-00.toc is non existent 39006 INFO: Building Tree Tree-00.toc 39021 INFO: checking Tree 39022 INFO: Building Tree because Tree-01.toc is non existent 39022 INFO: Building Tree Tree-01.toc 39157 INFO: checking Tree 39158 INFO: Building Tree because Tree-02.toc is non existent 39158 INFO: Building Tree Tree-02.toc 39165 INFO: Loading module hook 'hook-encodings.py' from '/Users/vagrant/electrum/contrib/osx/build-venv/lib/python3.10/site-packages/PyInstaller/hooks'... 39568 INFO: Loading module hook 'hook-distutils.util.py' from '/Users/vagrant/electrum/contrib/osx/build-venv/lib/python3.10/site-packages/PyInstaller/hooks'... 39586 INFO: Loading module hook 'hook-platform.py' from '/Users/vagrant/electrum/contrib/osx/build-venv/lib/python3.10/site-packages/PyInstaller/hooks'... 39587 INFO: Loading module hook 'hook-PyQt5.QtGui.py' from '/Users/vagrant/electrum/contrib/osx/build-venv/lib/python3.10/site-packages/PyInstaller/hooks'... 39734 INFO: Loading module hook 'hook-PyQt5.QtPrintSupport.py' from '/Users/vagrant/electrum/contrib/osx/build-venv/lib/python3.10/site-packages/PyInstaller/hooks'... 39984 INFO: Loading module hook 'hook-PyQt5.QtMultimedia.py' from '/Users/vagrant/electrum/contrib/osx/build-venv/lib/python3.10/site-packages/PyInstaller/hooks'... 40164 INFO: Loading module hook 'hook-packaging.py' from '/Users/vagrant/electrum/contrib/osx/build-venv/lib/python3.10/site-packages/PyInstaller/hooks'... 40165 INFO: Loading module hook 'hook-pickle.py' from '/Users/vagrant/electrum/contrib/osx/build-venv/lib/python3.10/site-packages/PyInstaller/hooks'... 40187 INFO: Loading module hook 'hook-heapq.py' from '/Users/vagrant/electrum/contrib/osx/build-venv/lib/python3.10/site-packages/PyInstaller/hooks'... 40192 INFO: Loading module hook 'hook-difflib.py' from '/Users/vagrant/electrum/contrib/osx/build-venv/lib/python3.10/site-packages/PyInstaller/hooks'... 40196 INFO: Loading module hook 'hook-PyQt5.QtWidgets.py' from '/Users/vagrant/electrum/contrib/osx/build-venv/lib/python3.10/site-packages/PyInstaller/hooks'... 40361 INFO: Loading module hook 'hook-multiprocessing.util.py' from '/Users/vagrant/electrum/contrib/osx/build-venv/lib/python3.10/site-packages/PyInstaller/hooks'... 40382 INFO: Loading module hook 'hook-sysconfig.py' from '/Users/vagrant/electrum/contrib/osx/build-venv/lib/python3.10/site-packages/PyInstaller/hooks'... 40421 INFO: Loading module hook 'hook-PyQt5.QtNetwork.py' from '/Users/vagrant/electrum/contrib/osx/build-venv/lib/python3.10/site-packages/PyInstaller/hooks'... 40545 INFO: Loading module hook 'hook-xml.py' from '/Users/vagrant/electrum/contrib/osx/build-venv/lib/python3.10/site-packages/PyInstaller/hooks'... 40691 INFO: Loading module hook 'hook-distutils.py' from '/Users/vagrant/electrum/contrib/osx/build-venv/lib/python3.10/site-packages/PyInstaller/hooks'... 40692 INFO: Loading module hook 'hook-PyQt5.QtCore.py' from '/Users/vagrant/electrum/contrib/osx/build-venv/lib/python3.10/site-packages/PyInstaller/hooks'... 40903 INFO: Looking for ctypes DLLs 40987 WARNING: Library Cfgmgr32 required via ctypes not found 40992 WARNING: Library setupapi required via ctypes not found 40994 WARNING: Library Advapi32 required via ctypes not found 40996 WARNING: Ignoring /System/Library/Frameworks/IOKit.framework/IOKit imported from /Users/vagrant/electrum/contrib/osx/build-venv/lib/python3.10/site-packages/serial/tools/list_ports_osx.py - only basenames are supported with ctypes imports! 40996 WARNING: Ignoring /System/Library/Frameworks/CoreFoundation.framework/CoreFoundation imported from /Users/vagrant/electrum/contrib/osx/build-venv/lib/python3.10/site-packages/serial/tools/list_ports_osx.py - only basenames are supported with ctypes imports! 41028 INFO: Analyzing run-time hooks ... 41041 INFO: Including run-time hook '/Users/vagrant/electrum/contrib/osx/build-venv/lib/python3.10/site-packages/PyInstaller/hooks/rthooks/pyi_rth_pyqt5.py' 41043 INFO: Including run-time hook '/Users/vagrant/electrum/contrib/osx/build-venv/lib/python3.10/site-packages/PyInstaller/hooks/rthooks/pyi_rth_pkgutil.py' 41047 INFO: Including run-time hook '/Users/vagrant/electrum/contrib/osx/build-venv/lib/python3.10/site-packages/PyInstaller/hooks/rthooks/pyi_rth_inspect.py' 41049 INFO: Including run-time hook '/Users/vagrant/electrum/contrib/osx/build-venv/lib/python3.10/site-packages/PyInstaller/hooks/rthooks/pyi_rth_subprocess.py' 41051 INFO: Including run-time hook '/Users/vagrant/electrum/contrib/osx/build-venv/lib/python3.10/site-packages/PyInstaller/hooks/rthooks/pyi_rth_multiprocessing.py' 41055 INFO: Including run-time hook '/Users/vagrant/electrum/contrib/osx/build-venv/lib/python3.10/site-packages/PyInstaller/hooks/rthooks/pyi_rth_pkgres.py' 41140 INFO: Looking for dynamic libraries 719 INFO: gettext setting initial language to None Traceback (most recent call last): File "/Users/vagrant/electrum/contrib/osx/build-venv/bin/pyinstaller", line 8, in sys.exit(run()) File "/Users/vagrant/electrum/contrib/osx/build-venv/lib/python3.10/site-packages/PyInstaller/__main__.py", line 178, in run run_build(pyi_config, spec_file, **vars(args)) File "/Users/vagrant/electrum/contrib/osx/build-venv/lib/python3.10/site-packages/PyInstaller/__main__.py", line 59, in run_build PyInstaller.building.build_main.main(pyi_config, spec_file, **kwargs) File "/Users/vagrant/electrum/contrib/osx/build-venv/lib/python3.10/site-packages/PyInstaller/building/build_main.py", line 934, in main build(specfile, distpath, workpath, clean_build) File "/Users/vagrant/electrum/contrib/osx/build-venv/lib/python3.10/site-packages/PyInstaller/building/build_main.py", line 856, in build exec(code, spec_namespace) File "contrib/osx/osx.spec", line 48, in a = Analysis([electrum+ MAIN_SCRIPT, File "/Users/vagrant/electrum/contrib/osx/build-venv/lib/python3.10/site-packages/PyInstaller/building/build_main.py", line 381, in __init__ self.__postinit__() File "/Users/vagrant/electrum/contrib/osx/build-venv/lib/python3.10/site-packages/PyInstaller/building/datastruct.py", line 173, in __postinit__ self.assemble() File "/Users/vagrant/electrum/contrib/osx/build-venv/lib/python3.10/site-packages/PyInstaller/building/build_main.py", line 660, in assemble isolated.call(find_binary_dependencies, list(self.binaries), self.binding_redirects, collected_packages) File "/Users/vagrant/electrum/contrib/osx/build-venv/lib/python3.10/site-packages/PyInstaller/isolated/_parent.py", line 238, in call return isolated.call(function, *args, **kwargs) File "/Users/vagrant/electrum/contrib/osx/build-venv/lib/python3.10/site-packages/PyInstaller/isolated/_parent.py", line 176, in call raise RuntimeError(f"Child process call to {function.__name__}() failed with:\n" + output) RuntimeError: Child process call to find_binary_dependencies() failed with: File "/Users/vagrant/electrum/electrum/gui/qml/__init__.py", line 8, in import PyQt6 ModuleNotFoundError: No module named 'PyQt6' During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/Users/vagrant/electrum/contrib/osx/build-venv/lib/python3.10/site-packages/PyInstaller/isolated/_child.py", line 63, in run_next_command output = function(*args, **kwargs) File "/Users/vagrant/electrum/contrib/osx/build-venv/lib/python3.10/site-packages/PyInstaller/building/build_main.py", line 157, in find_binary_dependencies __import__(package) File "/Users/vagrant/electrum/electrum/gui/qml/__init__.py", line 10, in sys.exit("Error: Could not import PyQt6. On Linux systems, you may try 'sudo apt-get install python3-pyqt6'") SystemExit: Error: Could not import PyQt6. On Linux systems, you may try 'sudo apt-get install python3-pyqt6' 🗯 ERROR: Could not build binary ``` --- electrum/gui/qml/__init__.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/electrum/gui/qml/__init__.py b/electrum/gui/qml/__init__.py index 35926cb70..7e28f61f5 100644 --- a/electrum/gui/qml/__init__.py +++ b/electrum/gui/qml/__init__.py @@ -6,13 +6,19 @@ from typing import TYPE_CHECKING try: import PyQt6 -except Exception: - sys.exit("Error: Could not import PyQt6. On Linux systems, you may try 'sudo apt-get install python3-pyqt6'") +except Exception as e: + from electrum import GuiImportError + raise GuiImportError( + "Error: Could not import PyQt6. On Linux systems, " + "you may try 'sudo apt-get install python3-pyqt6'") from e try: import PyQt6.QtQml -except Exception: - sys.exit("Error: Could not import PyQt6.QtQml. On Linux systems, you may try 'sudo apt-get install python3-pyqt6.qtquick'") +except Exception as e: + from electrum import GuiImportError + raise GuiImportError( + "Error: Could not import PyQt6.QtQml. On Linux systems, " + "you may try 'sudo apt-get install python3-pyqt6.qtquick'") from e from PyQt6.QtCore import (Qt, QCoreApplication, QLocale, QTranslator, QTimer, QT_VERSION_STR, PYQT_VERSION_STR) from PyQt6.QtGui import QGuiApplication From b5f1783a62e55bd52acb2d48b4eb6464c4fb16c6 Mon Sep 17 00:00:00 2001 From: SomberNight Date: Fri, 17 Nov 2023 11:29:38 +0000 Subject: [PATCH 4/8] plugins: better error and logging on duplicate plugins (=plugin name collision) This assert is currently failing in pyinstaller builds (probably have been since it was added in 552bfb589ac218b909c81efe9fdc5388d5926321). Looks like the pyinstaller binaries include two copies of the hw_wallet and the jade plugins. This likely has been the case for years, we just never noticed. -- but it started triggering the new assert. ``` 2.78 | I | plugin | iter_modules=[ ModuleInfo(module_finder=FileFinder('C:\\Program Files (x86)\\Electrum\\electrum\\plugins'), name='audio_modem', ispkg=True), ModuleInfo(module_finder=FileFinder('C:\\Program Files (x86)\\Electrum\\electrum\\plugins'), name='bitbox02', ispkg=True), ModuleInfo(module_finder=FileFinder('C:\\Program Files (x86)\\Electrum\\electrum\\plugins'), name='coldcard', ispkg=True), ModuleInfo(module_finder=FileFinder('C:\\Program Files (x86)\\Electrum\\electrum\\plugins'), name='cosigner_pool', ispkg=True), ModuleInfo(module_finder=FileFinder('C:\\Program Files (x86)\\Electrum\\electrum\\plugins'), name='digitalbitbox', ispkg=True), ModuleInfo(module_finder=FileFinder('C:\\Program Files (x86)\\Electrum\\electrum\\plugins'), name='hw_wallet', ispkg=True), ModuleInfo(module_finder=FileFinder('C:\\Program Files (x86)\\Electrum\\electrum\\plugins'), name='jade', ispkg=True), ModuleInfo(module_finder=FileFinder('C:\\Program Files (x86)\\Electrum\\electrum\\plugins'), name='keepkey', ispkg=True), ModuleInfo(module_finder=FileFinder('C:\\Program Files (x86)\\Electrum\\electrum\\plugins'), name='labels', ispkg=True), ModuleInfo(module_finder=FileFinder('C:\\Program Files (x86)\\Electrum\\electrum\\plugins'), name='ledger', ispkg=True), ModuleInfo(module_finder=FileFinder('C:\\Program Files (x86)\\Electrum\\electrum\\plugins'), name='payserver', ispkg=True), ModuleInfo(module_finder=FileFinder('C:\\Program Files (x86)\\Electrum\\electrum\\plugins'), name='revealer', ispkg=True), ModuleInfo(module_finder=FileFinder('C:\\Program Files (x86)\\Electrum\\electrum\\plugins'), name='safe_t', ispkg=True), ModuleInfo(module_finder=FileFinder('C:\\Program Files (x86)\\Electrum\\electrum\\plugins'), name='swapserver', ispkg=True), ModuleInfo(module_finder=FileFinder('C:\\Program Files (x86)\\Electrum\\electrum\\plugins'), name='trezor', ispkg=True), ModuleInfo(module_finder=FileFinder('C:\\Program Files (x86)\\Electrum\\electrum\\plugins'), name='trustedcoin', ispkg=True), ModuleInfo(module_finder=FileFinder('C:\\Program Files (x86)\\Electrum\\electrum\\plugins'), name='virtualkeyboard', ispkg=True), ModuleInfo(module_finder=, name='hw_wallet', ispkg=True), ModuleInfo(module_finder=, name='jade', ispkg=True)] ``` --- electrum/plugin.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/electrum/plugin.py b/electrum/plugin.py index 631f6a388..4be5d323d 100644 --- a/electrum/plugin.py +++ b/electrum/plugin.py @@ -80,7 +80,8 @@ class Plugins(DaemonThread): """ if cls._all_found_plugins is None: cls._all_found_plugins = dict() - for loader, name, ispkg in pkgutil.iter_modules([cls.pkgpath]): + iter_modules = list(pkgutil.iter_modules([cls.pkgpath])) + for loader, name, ispkg in iter_modules: full_name = f'electrum.plugins.{name}' spec = importlib.util.find_spec(full_name) if spec is None: # pkgutil found it but importlib can't ?! @@ -94,7 +95,9 @@ class Plugins(DaemonThread): except Exception as e: raise Exception(f"Error pre-loading {full_name}: {repr(e)}") from e d = module.__dict__ - assert name not in cls._all_found_plugins + if name in cls._all_found_plugins: + _logger.info(f"Found the following plugin modules: {iter_modules=}") + raise Exception(f"duplicate plugins? for {name=}") cls._all_found_plugins[name] = d return cls._all_found_plugins From 4cc9ef207836d7423e76a7a893f82aaf08256c7e Mon Sep 17 00:00:00 2001 From: SomberNight Date: Fri, 17 Nov 2023 13:28:33 +0000 Subject: [PATCH 5/8] pyinstaller build: clean-up .spec, fix issue described in prev commit The hw_wallet and jade plugins were being included twice in the pyinstaller binaries. They were apparently the only two plugins being picked as "modules" (a.pure), which by default are included as compiled-bytecode. In addition, we included "electrum/plugins" as data (a.data), which meant all plugins in source form. Instead of hacking around to fix the specific issue, this attempts a larger clean-up. --- contrib/build-wine/deterministic.spec | 26 +++----------------------- contrib/osx/osx.spec | 26 ++++---------------------- 2 files changed, 7 insertions(+), 45 deletions(-) diff --git a/contrib/build-wine/deterministic.spec b/contrib/build-wine/deterministic.spec index 5413f4590..92204104d 100644 --- a/contrib/build-wine/deterministic.spec +++ b/contrib/build-wine/deterministic.spec @@ -13,17 +13,7 @@ home = 'C:\\electrum\\' # 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('trezorlib') -hiddenimports += collect_submodules('safetlib') -hiddenimports += collect_submodules('btchip') # device plugin: ledger -hiddenimports += collect_submodules('ledger_bitcoin') # device plugin: ledger -hiddenimports += collect_submodules('keepkeylib') -hiddenimports += collect_submodules('websocket') -hiddenimports += collect_submodules('ckcc') -hiddenimports += collect_submodules('bitbox02') -hiddenimports += ['electrum.plugins.jade.jade'] -hiddenimports += ['electrum.plugins.jade.jadepy.jade'] -hiddenimports += ['PyQt5.QtPrintSupport'] # needed by Revealer +hiddenimports += collect_submodules('electrum.plugins') binaries = [] @@ -39,10 +29,10 @@ datas = [ (home+'electrum/wordlist/english.txt', 'electrum/wordlist'), (home+'electrum/wordlist/slip39.txt', 'electrum/wordlist'), (home+'electrum/locale', 'electrum/locale'), - (home+'electrum/plugins', 'electrum/plugins'), (home+'electrum/gui/icons', 'electrum/gui/icons'), ] -datas += collect_data_files('trezorlib') +datas += collect_data_files('electrum.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('btchip') datas += collect_data_files('keepkeylib') @@ -60,16 +50,6 @@ a = Analysis([home+'run_electrum', home+'electrum/bitcoin.py', home+'electrum/dnssec.py', home+'electrum/commands.py', - home+'electrum/plugins/trustedcoin/qt.py', - home+'electrum/plugins/cosigner_pool/qt.py', - home+'electrum/plugins/trezor/qt.py', - home+'electrum/plugins/safe_t/client.py', - home+'electrum/plugins/safe_t/qt.py', - home+'electrum/plugins/keepkey/qt.py', - home+'electrum/plugins/ledger/qt.py', - home+'electrum/plugins/coldcard/qt.py', - home+'electrum/plugins/jade/qt.py', - #home+'packages/requests/utils.py' ], binaries=binaries, datas=datas, diff --git a/contrib/osx/osx.spec b/contrib/osx/osx.spec index e428963e1..d8433c36f 100644 --- a/contrib/osx/osx.spec +++ b/contrib/osx/osx.spec @@ -20,17 +20,8 @@ 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('trezorlib') -hiddenimports += collect_submodules('safetlib') -hiddenimports += collect_submodules('btchip') # device plugin: ledger -hiddenimports += collect_submodules('ledger_bitcoin') # device plugin: ledger -hiddenimports += collect_submodules('keepkeylib') -hiddenimports += collect_submodules('websocket') -hiddenimports += collect_submodules('ckcc') -hiddenimports += collect_submodules('bitbox02') -hiddenimports += ['electrum.plugins.jade.jade'] -hiddenimports += ['electrum.plugins.jade.jadepy.jade'] -hiddenimports += ['PyQt5.QtPrintSupport'] # needed by Revealer +hiddenimports += collect_submodules('electrum.plugins') + datas = [ (electrum + PYPKG + '/*.json', PYPKG), @@ -38,10 +29,10 @@ datas = [ (electrum + PYPKG + '/wordlist/english.txt', PYPKG + '/wordlist'), (electrum + PYPKG + '/wordlist/slip39.txt', PYPKG + '/wordlist'), (electrum + PYPKG + '/locale', PYPKG + '/locale'), - (electrum + PYPKG + '/plugins', PYPKG + '/plugins'), (electrum + PYPKG + '/gui/icons', PYPKG + '/gui/icons'), ] -datas += collect_data_files('trezorlib') +datas += collect_data_files('electrum.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('btchip') datas += collect_data_files('keepkeylib') @@ -64,15 +55,6 @@ a = Analysis([electrum+ MAIN_SCRIPT, electrum+'electrum/bitcoin.py', electrum+'electrum/dnssec.py', electrum+'electrum/commands.py', - electrum+'electrum/plugins/trustedcoin/qt.py', - electrum+'electrum/plugins/cosigner_pool/qt.py', - electrum+'electrum/plugins/trezor/qt.py', - electrum+'electrum/plugins/safe_t/client.py', - electrum+'electrum/plugins/safe_t/qt.py', - electrum+'electrum/plugins/keepkey/qt.py', - electrum+'electrum/plugins/ledger/qt.py', - electrum+'electrum/plugins/coldcard/qt.py', - electrum+'electrum/plugins/jade/qt.py', ], binaries=binaries, datas=datas, From 0912e5615d20801ba81fc6213a175979613970e5 Mon Sep 17 00:00:00 2001 From: SomberNight Date: Thu, 30 Nov 2023 11:34:27 +0000 Subject: [PATCH 6/8] pyinstaller build: follow-up: dirty hack for duplicate plugins Ah ok, I give up for now... the prev does not really work. The prior commit works on Windows but not on macOS. On Windows, it would package all plugins as code and only as code. On MacOS however, it would not package any plugins at all. And with this commit, where I mark the plugins folder to be packaged as *data*, it packages all plugins as *both* code and data. Not sure why. Let's just package all plugins as both code and data, and ignore the code instances explicitly... --- contrib/build-wine/deterministic.spec | 1 + contrib/osx/osx.spec | 1 + electrum/plugin.py | 5 +++++ 3 files changed, 7 insertions(+) diff --git a/contrib/build-wine/deterministic.spec b/contrib/build-wine/deterministic.spec index 92204104d..23fe939ff 100644 --- a/contrib/build-wine/deterministic.spec +++ b/contrib/build-wine/deterministic.spec @@ -29,6 +29,7 @@ datas = [ (home+'electrum/wordlist/english.txt', 'electrum/wordlist'), (home+'electrum/wordlist/slip39.txt', 'electrum/wordlist'), (home+'electrum/locale', 'electrum/locale'), + (home+'electrum/plugins', 'electrum/plugins'), (home+'electrum/gui/icons', 'electrum/gui/icons'), ] datas += collect_data_files('electrum.plugins') diff --git a/contrib/osx/osx.spec b/contrib/osx/osx.spec index d8433c36f..143d7602f 100644 --- a/contrib/osx/osx.spec +++ b/contrib/osx/osx.spec @@ -29,6 +29,7 @@ datas = [ (electrum + PYPKG + '/wordlist/english.txt', PYPKG + '/wordlist'), (electrum + PYPKG + '/wordlist/slip39.txt', PYPKG + '/wordlist'), (electrum + PYPKG + '/locale', PYPKG + '/locale'), + (electrum + PYPKG + '/plugins', PYPKG + '/plugins'), (electrum + PYPKG + '/gui/icons', PYPKG + '/gui/icons'), ] datas += collect_data_files('electrum.plugins') diff --git a/electrum/plugin.py b/electrum/plugin.py index 4be5d323d..e24a8effb 100644 --- a/electrum/plugin.py +++ b/electrum/plugin.py @@ -82,6 +82,11 @@ class Plugins(DaemonThread): cls._all_found_plugins = dict() iter_modules = list(pkgutil.iter_modules([cls.pkgpath])) for loader, name, ispkg in iter_modules: + # FIXME pyinstaller binaries are packaging each built-in plugin twice: + # once as data and once as code. To honor the "no duplicates" rule below, + # we exclude the ones packaged as *code*, here: + if loader.__class__.__qualname__ == "FrozenImporter": + continue full_name = f'electrum.plugins.{name}' spec = importlib.util.find_spec(full_name) if spec is None: # pkgutil found it but importlib can't ?! From b551cb5f7598234e133b18bf940eb0e38327c5be Mon Sep 17 00:00:00 2001 From: SomberNight Date: Thu, 23 Nov 2023 18:01:48 +0000 Subject: [PATCH 7/8] pyinstaller build: better parameterise .spec --- contrib/build-wine/deterministic.spec | 67 ++++++++++++++------------- contrib/osx/osx.spec | 54 ++++++++++----------- 2 files changed, 62 insertions(+), 59 deletions(-) diff --git a/contrib/build-wine/deterministic.spec b/contrib/build-wine/deterministic.spec index 23fe939ff..1d840cd23 100644 --- a/contrib/build-wine/deterministic.spec +++ b/contrib/build-wine/deterministic.spec @@ -4,16 +4,20 @@ from PyInstaller.utils.hooks import collect_data_files, collect_submodules, coll import sys, os +PYPKG="electrum" +MAIN_SCRIPT="run_electrum" +PROJECT_ROOT = "C:/electrum" +ICONS_FILE=f"{PROJECT_ROOT}/{PYPKG}/gui/icons/electrum.ico" + cmdline_name = os.environ.get("ELECTRUM_CMDLINE_NAME") if not cmdline_name: raise Exception('no name') -home = 'C:\\electrum\\' # 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('electrum.plugins') +hiddenimports += collect_submodules(f"{PYPKG}.plugins") binaries = [] @@ -21,18 +25,18 @@ binaries = [] # Workaround for "Retro Look": binaries += [b for b in collect_dynamic_libs('PyQt5') if 'qwindowsvista' in b[0]] -binaries += [(home+'electrum/*.dll', '.')] +binaries += [(f"{PROJECT_ROOT}/{PYPKG}/*.dll", '.')] datas = [ - (home+'electrum/*.json', 'electrum'), - (home+'electrum/lnwire/*.csv', 'electrum/lnwire'), - (home+'electrum/wordlist/english.txt', 'electrum/wordlist'), - (home+'electrum/wordlist/slip39.txt', 'electrum/wordlist'), - (home+'electrum/locale', 'electrum/locale'), - (home+'electrum/plugins', 'electrum/plugins'), - (home+'electrum/gui/icons', 'electrum/gui/icons'), + (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}/locale", f"{PYPKG}/locale"), + (f"{PROJECT_ROOT}/{PYPKG}/plugins", f"{PYPKG}/plugins"), + (f"{PROJECT_ROOT}/{PYPKG}/gui/icons", f"{PYPKG}/gui/icons"), ] -datas += collect_data_files('electrum.plugins') +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('btchip') @@ -41,20 +45,19 @@ datas += collect_data_files('ckcc') datas += collect_data_files('bitbox02') # 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([home+'run_electrum', - home+'electrum/gui/qt/main_window.py', - home+'electrum/gui/qt/qrreader/qtmultimedia/camera_dialog.py', - home+'electrum/gui/text.py', - home+'electrum/util.py', - home+'electrum/wallet.py', - home+'electrum/simple_config.py', - home+'electrum/bitcoin.py', - home+'electrum/dnssec.py', - home+'electrum/commands.py', +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, - #pathex=[home+'lib', home+'gui', home+'plugins'], hiddenimports=hiddenimports, hookspath=[]) @@ -105,11 +108,11 @@ exe_standalone = EXE( a.scripts, a.binaries, a.datas, - name=os.path.join('build\\pyi.win32\\electrum', cmdline_name + ".exe"), + name=os.path.join("build", "pyi.win32", PYPKG, f"{cmdline_name}.exe"), debug=False, strip=None, upx=False, - icon=home+'electrum/gui/icons/electrum.ico', + icon=ICONS_FILE, console=False) # console=True makes an annoying black box pop up, but it does make Electrum output command line commands, with this turned off no output will be given but commands can still be used @@ -118,11 +121,11 @@ exe_portable = EXE( a.scripts, a.binaries, a.datas + [('is_portable', 'README.md', 'DATA')], - name=os.path.join('build\\pyi.win32\\electrum', cmdline_name + "-portable.exe"), + name=os.path.join("build", "pyi.win32", PYPKG, f"{cmdline_name}-portable.exe"), debug=False, strip=None, upx=False, - icon=home+'electrum/gui/icons/electrum.ico', + icon=ICONS_FILE, console=False) ##### @@ -132,22 +135,22 @@ exe_inside_setup_noconsole = EXE( pyz, a.scripts, exclude_binaries=True, - name=os.path.join('build\\pyi.win32\\electrum', cmdline_name), + name=os.path.join("build", "pyi.win32", PYPKG, f"{cmdline_name}.exe"), debug=False, strip=None, upx=False, - icon=home+'electrum/gui/icons/electrum.ico', + icon=ICONS_FILE, console=False) exe_inside_setup_console = EXE( pyz, a.scripts, exclude_binaries=True, - name=os.path.join('build\\pyi.win32\\electrum', cmdline_name+"-debug"), + name=os.path.join("build", "pyi.win32", PYPKG, f"{cmdline_name}-debug.exe"), debug=False, strip=None, upx=False, - icon=home+'electrum/gui/icons/electrum.ico', + icon=ICONS_FILE, console=True) coll = COLLECT( @@ -159,6 +162,6 @@ coll = COLLECT( strip=None, upx=True, debug=False, - icon=home+'electrum/gui/icons/electrum.ico', + icon=ICONS_FILE, console=False, - name=os.path.join('dist', 'electrum')) + name=os.path.join('dist', PYPKG)) diff --git a/contrib/osx/osx.spec b/contrib/osx/osx.spec index 143d7602f..7fae2e783 100644 --- a/contrib/osx/osx.spec +++ b/contrib/osx/osx.spec @@ -4,35 +4,35 @@ from PyInstaller.utils.hooks import collect_data_files, collect_submodules, coll import sys, os -PACKAGE='Electrum' +PACKAGE_NAME='Electrum.app' PYPKG='electrum' MAIN_SCRIPT='run_electrum' -ICONS_FILE=PYPKG + '/gui/icons/electrum.icns' +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') -electrum = os.path.abspath(".") + "/" 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('electrum.plugins') +hiddenimports += collect_submodules(f"{PYPKG}.plugins") datas = [ - (electrum + PYPKG + '/*.json', PYPKG), - (electrum + PYPKG + '/lnwire/*.csv', PYPKG + '/lnwire'), - (electrum + PYPKG + '/wordlist/english.txt', PYPKG + '/wordlist'), - (electrum + PYPKG + '/wordlist/slip39.txt', PYPKG + '/wordlist'), - (electrum + PYPKG + '/locale', PYPKG + '/locale'), - (electrum + PYPKG + '/plugins', PYPKG + '/plugins'), - (electrum + PYPKG + '/gui/icons', PYPKG + '/gui/icons'), + (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}/locale", f"{PYPKG}/locale"), + (f"{PROJECT_ROOT}/{PYPKG}/plugins", f"{PYPKG}/plugins"), + (f"{PROJECT_ROOT}/{PYPKG}/gui/icons", f"{PYPKG}/gui/icons"), ] -datas += collect_data_files('electrum.plugins') +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('btchip') @@ -40,22 +40,22 @@ datas += collect_data_files('keepkeylib') datas += collect_data_files('ckcc') datas += collect_data_files('bitbox02') -binaries = [(electrum + "electrum/*.dylib", ".")] +binaries = [(f"{PROJECT_ROOT}/{PYPKG}/*.dylib", ".")] # Workaround for "Retro Look": binaries += [b for b in collect_dynamic_libs('PyQt5') if 'macstyle' in b[0]] # 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([electrum+ MAIN_SCRIPT, - electrum+'electrum/gui/qt/main_window.py', - electrum+'electrum/gui/qt/qrreader/qtmultimedia/camera_dialog.py', - electrum+'electrum/gui/text.py', - electrum+'electrum/util.py', - electrum+'electrum/wallet.py', - electrum+'electrum/simple_config.py', - electrum+'electrum/bitcoin.py', - electrum+'electrum/dnssec.py', - electrum+'electrum/commands.py', +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, @@ -87,7 +87,7 @@ exe = EXE( debug=False, strip=False, upx=True, - icon=electrum+ICONS_FILE, + icon=ICONS_FILE, console=False, target_arch='x86_64', # TODO investigate building 'universal2' ) @@ -97,9 +97,9 @@ app = BUNDLE( a.binaries, a.zipfiles, a.datas, - version = VERSION, - name=PACKAGE + '.app', - icon=electrum+ICONS_FILE, + version=VERSION, + name=PACKAGE_NAME, + icon=ICONS_FILE, bundle_identifier=None, info_plist={ 'NSHighResolutionCapable': 'True', From 9743bd5219d89a8eb86aa9dcb58bc692f4e3ab6c Mon Sep 17 00:00:00 2001 From: SomberNight Date: Fri, 24 Nov 2023 22:32:18 +0000 Subject: [PATCH 8/8] pyinstaller build: (trivial) format so that win/osx look more similar --- contrib/build-wine/deterministic.spec | 4 ++-- contrib/osx/osx.spec | 13 ++++++++----- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/contrib/build-wine/deterministic.spec b/contrib/build-wine/deterministic.spec index 1d840cd23..013c55a97 100644 --- a/contrib/build-wine/deterministic.spec +++ b/contrib/build-wine/deterministic.spec @@ -21,12 +21,12 @@ hiddenimports += collect_submodules(f"{PYPKG}.plugins") binaries = [] - # Workaround for "Retro Look": binaries += [b for b in collect_dynamic_libs('PyQt5') if 'qwindowsvista' in b[0]] - +# add libsecp256k1, libusb, etc: binaries += [(f"{PROJECT_ROOT}/{PYPKG}/*.dll", '.')] + datas = [ (f"{PROJECT_ROOT}/{PYPKG}/*.json", PYPKG), (f"{PROJECT_ROOT}/{PYPKG}/lnwire/*.csv", f"{PYPKG}/lnwire"), diff --git a/contrib/osx/osx.spec b/contrib/osx/osx.spec index 7fae2e783..bf15dce3c 100644 --- a/contrib/osx/osx.spec +++ b/contrib/osx/osx.spec @@ -23,6 +23,13 @@ hiddenimports += collect_submodules('pkg_resources') # workaround for https://g hiddenimports += collect_submodules(f"{PYPKG}.plugins") +binaries = [] +# Workaround for "Retro Look": +binaries += [b for b in collect_dynamic_libs('PyQt5') 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"), @@ -40,11 +47,6 @@ datas += collect_data_files('keepkeylib') datas += collect_data_files('ckcc') datas += collect_data_files('bitbox02') -binaries = [(f"{PROJECT_ROOT}/{PYPKG}/*.dylib", ".")] - -# Workaround for "Retro Look": -binaries += [b for b in collect_dynamic_libs('PyQt5') if 'macstyle' in b[0]] - # 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", @@ -62,6 +64,7 @@ a = Analysis([f"{PROJECT_ROOT}/{MAIN_SCRIPT}", hiddenimports=hiddenimports, hookspath=[]) + # 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]: