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()