aboutsummaryrefslogtreecommitdiff
path: root/tools/build_gui.py
diff options
context:
space:
mode:
authorSam Scholten2025-10-14 14:20:16 +1000
committerSam Scholten2025-10-14 14:29:53 +1000
commit614ec191a2fb7ed15cee2262035011ec2b910568 (patch)
tree010b9c5aced17b3c6183650a8f47e54ab37dd69b /tools/build_gui.py
parent28db0686a5393704d3b5c13d68dbd654fc3ecbba (diff)
downloadpicostream-614ec191a2fb7ed15cee2262035011ec2b910568.tar.gz
picostream-614ec191a2fb7ed15cee2262035011ec2b910568.zip
Implement Python build script for robust PyInstaller GUI build
Diffstat (limited to 'tools/build_gui.py')
-rw-r--r--tools/build_gui.py100
1 files changed, 100 insertions, 0 deletions
diff --git a/tools/build_gui.py b/tools/build_gui.py
new file mode 100644
index 0000000..e912d8d
--- /dev/null
+++ b/tools/build_gui.py
@@ -0,0 +1,100 @@
+import glob
+import os
+import platform
+import sys
+
+try:
+ import PyInstaller.__main__
+except ImportError:
+ print("Error: PyInstaller is not installed. Please run 'uv pip install pyinstaller'.")
+ sys.exit(1)
+
+
+def find_libffi_dll() -> str | None:
+ """
+ Find the libffi-*.dll file required for _ctypes on Windows.
+
+ Searches in common locations:
+ 1. The active Python environment's DLLs directory.
+ 2. The base Python installation's DLLs directory (if in a venv).
+ 3. A Conda environment's Library/bin directory.
+ """
+ if sys.platform != "win32":
+ return None
+
+ search_paths = []
+ # 1. Active environment's DLLs directory (sys.prefix is the venv path)
+ search_paths.append(os.path.join(sys.prefix, "DLLs"))
+
+ # 2. Base Python installation's DLLs directory (if in a venv)
+ if hasattr(sys, "base_prefix") and sys.base_prefix != sys.prefix:
+ search_paths.append(os.path.join(sys.base_prefix, "DLLs"))
+
+ # 3. Conda environment's Library/bin directory
+ conda_prefix = os.environ.get("CONDA_PREFIX")
+ if conda_prefix:
+ search_paths.append(os.path.join(conda_prefix, "Library", "bin"))
+
+ print("--- Searching for libffi DLL on Windows ---")
+ unique_paths = sorted(list(set(p for p in search_paths if os.path.isdir(p))))
+
+ for path in unique_paths:
+ print(f" - Checking: {path}")
+ dll_pattern = os.path.join(path, "libffi-*.dll")
+ found_dlls = glob.glob(dll_pattern)
+ if found_dlls:
+ dll_path = found_dlls[0]
+ print(f" - Found: {dll_path}")
+ return dll_path
+
+ return None
+
+
+def main() -> None:
+ """
+ Build the PicoStream GUI executable using PyInstaller.
+ """
+ print(f"--- Starting PicoStream Build ---")
+ print(f"Python: {sys.version} ({platform.architecture()[0]}) on {sys.platform}")
+
+ entry_script = "picostream/main.py"
+ app_name = "PicoStream"
+
+ pyinstaller_args = [
+ "--onefile",
+ "--windowed",
+ f"--name={app_name}",
+ ]
+
+ if sys.platform == "win32":
+ libffi_path = find_libffi_dll()
+ if libffi_path:
+ # The format for --add-binary is "source;destination" on Windows.
+ # We add the DLL to the root of the bundle.
+ pyinstaller_args.append(f"--add-binary={libffi_path}{os.pathsep}.")
+ else:
+ print("\nERROR: Could not find libffi-*.dll on Windows.")
+ print("This DLL is required for the _ctypes module to work correctly.")
+ print("Searched in the following directories:")
+ for path in sorted(list(set(p for p in search_paths if os.path.isdir(p)))):
+ print(f" - {path}")
+ print("Please ensure you are using a standard CPython or Conda installation.")
+ sys.exit(1)
+
+ # Add the entry script at the end
+ pyinstaller_args.append(entry_script)
+
+ print("\n--- Running PyInstaller ---")
+ print(f"Arguments: {' '.join(pyinstaller_args)}")
+
+ PyInstaller.__main__.run(pyinstaller_args)
+
+ print(f"\n--- Build Complete ---")
+ if sys.platform == "win32":
+ print(f"Executable created at: dist\\{app_name}.exe")
+ else:
+ print(f"Executable created at: dist/{app_name}")
+
+
+if __name__ == "__main__":
+ main()