# -*- coding: utf-8 -*- """builder.py - Handles the execution of the PyInstaller build process in a separate thread.""" import subprocess import os import traceback from typing import Optional, List, Dict, Callable def run_build_in_thread( command: List[str], working_dir: str, output_queue: 'queue.Queue', logger_func: Callable[..., None], output_dir_name: str, environment: Optional[Dict[str, str]] = None ): """ Executes the PyInstaller command using subprocess.Popen in the background. Streams stdout/stderr to the output_queue for display in the GUI. Notifies the main thread of success, failure, or completion via the queue. """ build_process = None try: logger_func("Build thread starting execution...", level="INFO") command_str_for_log = " ".join([f'"{c}"' if " " in c else c for c in command]) logger_func(f"PyInstaller command to be executed: {command_str_for_log}", level="DEBUG") logger_func(f"Working directory: {working_dir}", level="DEBUG") env_log_msg = f"Using {'custom' if environment else 'inherited'} environment." logger_func(f"PyInstaller process starting. {env_log_msg}", level="INFO") build_process = subprocess.Popen( command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=working_dir, text=True, encoding="utf-8", errors="replace", bufsize=1, env=environment, ) logger_func(f"PyInstaller process launched (PID: {build_process.pid}).", level="INFO") # Stream output while True: line = build_process.stdout.readline() if not line: if build_process.poll() is not None: logger_func("End of PyInstaller output stream.", level="DEBUG") break else: continue # Messaggio per il logger della GUI output_queue.put(("LOG_STREAM", "LOG", line)) return_code = build_process.wait() logger_func(f"PyInstaller process finished with exit code: {return_code}", level="INFO") if return_code == 0: dist_path_abs = os.path.join(working_dir, output_dir_name) if os.path.exists(dist_path_abs): success_msg = f"Build completed successfully!\nOutput Directory: {dist_path_abs}" # Messaggio per la GUI principale output_queue.put(("MAIN_GUI_ACTION", "BUILD_SUCCESS", success_msg)) else: warn_msg = f"Build finished with exit code 0, but output directory not found:\n{dist_path_abs}\nCheck the full log for potential issues." logger_func(warn_msg, level="WARNING") output_queue.put(("MAIN_GUI_ACTION", "BUILD_ERROR", warn_msg)) else: error_msg = f"Build failed! (Exit Code: {return_code})\nCheck the log for details." output_queue.put(("MAIN_GUI_ACTION", "BUILD_ERROR", error_msg)) except FileNotFoundError: error_msg = "Error: 'pyinstaller' command not found.\nEnsure PyInstaller is installed correctly and in the system PATH." output_queue.put(("MAIN_GUI_ACTION", "BUILD_ERROR", error_msg)) logger_func(error_msg, level="CRITICAL") except Exception as e: error_msg = f"Unexpected error during build process execution: {e}" output_queue.put(("MAIN_GUI_ACTION", "BUILD_ERROR", error_msg)) logger_func(error_msg, level="CRITICAL") logger_func(traceback.format_exc(), level="DEBUG") finally: # Segnala sempre la fine del tentativo di build output_queue.put(("MAIN_GUI_ACTION", "BUILD_FINISHED", None)) logger_func("Build thread finished.", level="INFO")