115 lines
6.3 KiB
Python
115 lines
6.3 KiB
Python
# -*- coding: utf-8 -*-
|
|
""" builder.py - Handles the execution of the PyInstaller build process in a separate thread. """
|
|
|
|
import subprocess
|
|
import threading
|
|
import queue
|
|
import os
|
|
import traceback # For logging exceptions
|
|
|
|
# NO CHANGES NEEDED in this file for the restructuring - EXCEPT FOR THE DEBUG LOG LINE ADDED BELOW
|
|
|
|
def run_build_in_thread(command, working_dir, output_queue, logger_func, output_dir_name, environment=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.
|
|
|
|
Args:
|
|
command (list): The PyInstaller command and its arguments as a list of strings.
|
|
working_dir (str): The directory from which to run the command (project root).
|
|
output_queue (queue.Queue): Queue to send log messages and status updates.
|
|
logger_func (function): Function from the GUI to log messages (also used by this thread).
|
|
output_dir_name (str): The name of the dist/output directory (e.g., '_dist').
|
|
environment (dict, optional): A custom environment for the subprocess. Defaults to None (inherited).
|
|
"""
|
|
build_process = None
|
|
try:
|
|
logger_func("Build thread starting execution...", level="INFO")
|
|
|
|
# --- MODIFICA INIZIO ---
|
|
# Loggare il comando esatto che sta per essere eseguito per debugging
|
|
# Questo ci aiuterà a vedere se --clean è presente o meno.
|
|
# Usiamo una formattazione che gestisca gli spazi negli argomenti per una migliore leggibilità del log.
|
|
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")
|
|
# --- MODIFICA FINE ---
|
|
|
|
env_log_msg = f"Using {'custom' if environment else 'inherited'} environment."
|
|
if environment: pass # Placeholder, si potrebbe loggare di più sull'ambiente se necessario
|
|
logger_func(f"PyInstaller process starting. {env_log_msg}", level="INFO")
|
|
|
|
build_process = subprocess.Popen(
|
|
command,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.STDOUT, # Redirect stderr to stdout
|
|
cwd=working_dir,
|
|
text=True, # Decode output as text
|
|
encoding='utf-8',
|
|
errors='replace', # Handle potential decoding errors gracefully
|
|
bufsize=1, # Line-buffered
|
|
env=environment # Pass the custom or inherited environment
|
|
)
|
|
logger_func(f"PyInstaller process launched (PID: {build_process.pid}).", level="INFO")
|
|
|
|
# Stream output
|
|
while True:
|
|
try:
|
|
line = build_process.stdout.readline()
|
|
if not line:
|
|
# Check if process has ended
|
|
if build_process.poll() is not None:
|
|
logger_func("End of PyInstaller output stream.", level="DEBUG")
|
|
break
|
|
else:
|
|
# Process might still be running but not producing output momentarily
|
|
# Could add a small sleep here if CPU usage is a concern,
|
|
# but for typical PyInstaller builds, continuous checking is fine.
|
|
pass
|
|
else:
|
|
output_queue.put(("LOG", line)) # Send line to GUI for display
|
|
except Exception as read_err:
|
|
# This might happen if the stream is closed unexpectedly
|
|
logger_func(f"Error reading PyInstaller output stream: {read_err}\n{traceback.format_exc()}", level="ERROR")
|
|
break
|
|
|
|
return_code = build_process.wait() # Wait for the process to complete
|
|
logger_func(f"PyInstaller process finished with exit code: {return_code}", level="INFO")
|
|
|
|
if return_code == 0:
|
|
# Check if the expected output directory/file exists
|
|
dist_path_abs = os.path.join(working_dir, output_dir_name) # output_dir_name is like '_dist'
|
|
# Note: For one-file, the output is a file, not a directory directly under dist_path_abs
|
|
# For one-dir, the output is a directory.
|
|
# We check for the existence of the dist_path_abs itself.
|
|
# A more robust check might involve looking for the executable name inside dist_path_abs.
|
|
if os.path.exists(dist_path_abs): # This checks if '_dist' (or equivalent) exists
|
|
success_msg = f"Build completed successfully!\nOutput Directory: {dist_path_abs}"
|
|
output_queue.put(("BUILD_SUCCESS", success_msg))
|
|
else:
|
|
# This case might be rare if PyInstaller exits with 0, but good for robustness
|
|
warn_msg = (f"Build finished with exit code 0, but output directory not found:\n"
|
|
f"{dist_path_abs}\nCheck the full log for potential issues.")
|
|
logger_func(warn_msg, level="WARNING")
|
|
output_queue.put(("BUILD_ERROR", warn_msg)) # Treat as error if output dir is missing
|
|
else:
|
|
error_msg = f"Build failed! (Exit Code: {return_code})\nCheck the log for details."
|
|
output_queue.put(("BUILD_ERROR", error_msg))
|
|
|
|
except FileNotFoundError:
|
|
# This occurs if 'pyinstaller' command itself is not found
|
|
error_msg = ("Error: 'pyinstaller' command not found.\n"
|
|
"Ensure PyInstaller is installed correctly and in the system PATH.")
|
|
output_queue.put(("BUILD_ERROR", error_msg))
|
|
logger_func(error_msg, level="CRITICAL") # Logged via logger_func as well
|
|
except Exception as e:
|
|
# Catch any other unexpected errors during process execution
|
|
error_msg = f"Unexpected error during build process execution: {e}"
|
|
output_queue.put(("BUILD_ERROR", error_msg))
|
|
logger_func(error_msg, level="CRITICAL") # Logged via logger_func
|
|
logger_func(traceback.format_exc(), level="DEBUG") # Log full traceback for debugging
|
|
finally:
|
|
# Always signal that the build attempt (successful or not) has finished
|
|
output_queue.put(("BUILD_FINISHED", None))
|
|
logger_func("Build thread finished.", level="INFO") |