Skip to content

Commit 63bcc66

Browse files
committed
Refactor engines to use required_tools property with dict format
`Update sys_deps to handle system package installation` `Update engines to use new required_tools property`
1 parent a282331 commit 63bcc66

5 files changed

Lines changed: 128 additions & 64 deletions

File tree

Core/engines_loader/base.py

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,12 +87,59 @@ def should_compile_file(
8787
return True
8888

8989
@property
90-
def required_tools(self) -> list[str]:
90+
def required_tools(self) -> dict[str, list[str]]:
9191
"""
92-
Return list of tool names required by this engine (e.g., ['pyinstaller'], ['nuitka']).
93-
Used by VenvManager to check/install dependencies.
92+
Return dict of required tools with installation modes.
93+
Keys: 'python' for pip-installable tools, 'system' for system packages.
94+
Used by VenvManager for Python tools and system installer for system tools.
95+
Example: {'python': ['pyinstaller'], 'system': ['build-essential']}
9496
"""
95-
return []
97+
return {'python': [], 'system': []}
98+
99+
def ensure_tools_installed(self, gui) -> bool:
100+
"""
101+
Ensure all required tools are installed.
102+
Returns True if all tools are ready, False if installation failed or was cancelled.
103+
Handles both Python (pip) and system packages.
104+
"""
105+
try:
106+
tools = self.required_tools
107+
python_tools = tools.get('python', [])
108+
system_tools = tools.get('system', [])
109+
110+
# Check if VenvManager is available
111+
if hasattr(gui, 'venv_manager') and gui.venv_manager:
112+
venv_root = gui.venv_manager.resolve_project_venv()
113+
if venv_root:
114+
# Install Python tools via VenvManager
115+
if python_tools:
116+
try:
117+
gui.venv_manager.ensure_tools_installed(venv_root, python_tools)
118+
# Wait for completion (simplified - in practice would need async handling)
119+
except Exception as e:
120+
if hasattr(gui, 'log') and gui.log:
121+
gui.log.append(f"⚠️ Failed to install Python tools {python_tools}: {e}")
122+
return False
123+
124+
# Install system tools
125+
if system_tools:
126+
try:
127+
from Core.sys_deps import install_system_packages
128+
success = install_system_packages(system_tools, gui)
129+
if not success:
130+
if hasattr(gui, 'log') and gui.log:
131+
gui.log.append(f"⚠️ Failed to install system tools {system_tools}")
132+
return False
133+
except Exception as e:
134+
if hasattr(gui, 'log') and gui.log:
135+
gui.log.append(f"⚠️ Failed to install system tools {system_tools}: {e}")
136+
return False
137+
138+
return True
139+
except Exception as e:
140+
if hasattr(gui, 'log') and gui.log:
141+
gui.log.append(f"⚠️ Error ensuring tools installed: {e}")
142+
return False
96143

97144
def get_log_prefix(self, file_basename: str) -> str:
98145
"""

Core/sys_deps.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -918,3 +918,46 @@ def install_packages_linux(
918918
)
919919
except Exception:
920920
return None
921+
922+
923+
def install_system_packages(packages: list[str], gui) -> bool:
924+
"""
925+
Install system packages using the appropriate package manager.
926+
Returns True if successful, False otherwise.
927+
"""
928+
try:
929+
manager = SysDependencyManager(gui)
930+
system = platform.system().lower()
931+
932+
if system == "linux":
933+
# For Linux, use the install_packages_linux method
934+
process = manager.install_packages_linux(packages)
935+
if process:
936+
# Wait for completion (simplified - in practice should be async)
937+
process.waitForFinished(300000) # 5 minutes timeout
938+
return process.exitCode() == 0
939+
elif system == "windows":
940+
# For Windows, convert package names to winget format
941+
# This is a simplified mapping - in practice would need proper mapping
942+
winget_packages = []
943+
for pkg in packages:
944+
# Basic mapping - would need expansion for real use
945+
if pkg == "build-essential":
946+
winget_packages.append({"id": "Microsoft.VisualStudio.2022.BuildTools"})
947+
elif pkg == "python3-dev":
948+
winget_packages.append({"id": "Python.Python.3"})
949+
else:
950+
# Generic fallback
951+
winget_packages.append({"id": pkg})
952+
953+
process = manager.install_packages_windows(winget_packages)
954+
if process:
955+
process.waitForFinished(300000) # 5 minutes timeout
956+
return process.exitCode() == 0
957+
else:
958+
# Unsupported platform
959+
return False
960+
961+
return False
962+
except Exception:
963+
return False

ENGINES/cx_freeze/__init__.py

Lines changed: 8 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -51,28 +51,16 @@ class CXFreezeEngine(CompilerEngine):
5151
required_sdk_version: str = "1.0.0"
5252

5353
@property
54-
def required_tools(self) -> list[str]:
55-
return ["cx_freeze"]
54+
def required_tools(self) -> dict[str, list[str]]:
55+
"""Return required tools for CX_Freeze compilation."""
56+
return {
57+
'python': ['cx_freeze'],
58+
'system': []
59+
}
5660

5761
def preflight(self, gui, file: str) -> bool:
58-
"""Check if CX_Freeze is available in the venv."""
59-
try:
60-
venv_manager = getattr(gui, "venv_manager", None)
61-
if not venv_manager:
62-
return True # Let build_command fail instead
63-
64-
venv_path = venv_manager.resolve_project_venv()
65-
if not venv_path:
66-
return True # Let build_command fail instead
67-
68-
if not venv_manager.has_tool_binary(venv_path, "cx_freeze"):
69-
# Try to install cx_freeze
70-
venv_manager.ensure_tools_installed(venv_path, ["cx_freeze"])
71-
return False # Will be retried after installation
72-
73-
return True
74-
except Exception:
75-
return True # Let build_command handle errors
62+
"""Preflight check - dependencies are handled automatically by required_tools."""
63+
return True
7664

7765
def build_command(self, gui, file: str) -> list[str]:
7866
"""Build the CX_Freeze command line."""

ENGINES/nuitka/__init__.py

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -55,28 +55,26 @@ class NuitkaEngine(CompilerEngine):
5555
required_sdk_version: str = "1.0.0"
5656

5757
@property
58-
def required_tools(self) -> list[str]:
59-
return ["nuitka"]
58+
def required_tools(self) -> dict[str, list[str]]:
59+
"""Return required tools for Nuitka compilation."""
60+
system_tools = []
61+
if platform.system() == "Linux":
62+
# patchelf is needed for Linux binary manipulation
63+
# gcc is needed for compilation
64+
system_tools = ["patchelf", "gcc"]
65+
elif platform.system() == "Windows":
66+
# On Windows, Visual Studio Build Tools or similar might be needed
67+
# but we'll keep it minimal for now
68+
system_tools = []
69+
70+
return {
71+
'python': ['nuitka'],
72+
'system': system_tools
73+
}
6074

6175
def preflight(self, gui, file: str) -> bool:
62-
"""Check if Nuitka is available in the venv."""
63-
try:
64-
venv_manager = getattr(gui, "venv_manager", None)
65-
if not venv_manager:
66-
return True # Let build_command fail instead
67-
68-
venv_path = venv_manager.resolve_project_venv()
69-
if not venv_path:
70-
return True # Let build_command fail instead
71-
72-
if not venv_manager.has_tool_binary(venv_path, "nuitka"):
73-
# Try to install nuitka
74-
venv_manager.ensure_tools_installed(venv_path, ["nuitka"])
75-
return False # Will be retried after installation
76-
77-
return True
78-
except Exception:
79-
return True # Let build_command handle errors
76+
"""Preflight check - dependencies are handled automatically by required_tools."""
77+
return True
8078

8179
def build_command(self, gui, file: str) -> list[str]:
8280
"""Build the Nuitka command line."""

ENGINES/pyinstaller/__init__.py

Lines changed: 8 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -57,28 +57,16 @@ class PyInstallerEngine(CompilerEngine):
5757
required_sdk_version: str = "1.0.0"
5858

5959
@property
60-
def required_tools(self) -> list[str]:
61-
return ["pyinstaller"]
60+
def required_tools(self) -> dict[str, list[str]]:
61+
"""Return required tools for PyInstaller compilation."""
62+
return {
63+
'python': ['pyinstaller'],
64+
'system': []
65+
}
6266

6367
def preflight(self, gui, file: str) -> bool:
64-
"""Check if PyInstaller is available in the venv."""
65-
try:
66-
venv_manager = getattr(gui, "venv_manager", None)
67-
if not venv_manager:
68-
return True # Let build_command fail instead
69-
70-
venv_path = venv_manager.resolve_project_venv()
71-
if not venv_path:
72-
return True # Let build_command fail instead
73-
74-
if not venv_manager.has_tool_binary(venv_path, "pyinstaller"):
75-
# Try to install pyinstaller
76-
venv_manager.ensure_tools_installed(venv_path, ["pyinstaller"])
77-
return False # Will be retried after installation
78-
79-
return True
80-
except Exception:
81-
return True # Let build_command handle errors
68+
"""Preflight check - dependencies are handled automatically by required_tools."""
69+
return True
8270

8371
def build_command(self, gui, file: str) -> list[str]:
8472
"""Build the PyInstaller command line."""

0 commit comments

Comments
 (0)