1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
|
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():
"""
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.
Returns:
A tuple containing the path to the DLL (or None) and a list of
directories that were searched.
"""
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, unique_paths
return None, unique_paths
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, searched_paths = 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 searched_paths:
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()
|