diff --git a/src/toolManager/main.py b/src/toolManager/main.py index e005821de..0141db3e9 100644 --- a/src/toolManager/main.py +++ b/src/toolManager/main.py @@ -18,23 +18,21 @@ except ImportError: pass # Will handle gracefully later if missing +from registry import TOOLS, CATEGORIES +from paths import get_toolmanager_root, get_install_state_path +from constants import IS_WINDOWS +from config import ConfigManager + # ==================== CONFIG ==================== -BASE_DIR = Path(__file__).resolve().parent -INFO_JSON = BASE_DIR / "information.json" +BASE_DIR = get_toolmanager_root() +INFO_JSON = get_install_state_path() FULL_GUI = BASE_DIR / "gui_fixed.py" PYTHON = sys.executable -ANALOG_TOOLS = ["esim", "kicad", "ngspice"] -DIGITAL_TOOLS = ["esim", "kicad", "ngspice", "ghdl", "verilator", "llvm"] +ANALOG_TOOLS = CATEGORIES["analog"] +DIGITAL_TOOLS = CATEGORIES["digital"] -TOOL_LABELS = { - "esim": "eSim", - "kicad": "KiCad", - "ngspice": "Ngspice", - "ghdl": "GHDL", - "verilator": "Verilator", - "llvm": "LLVM", -} +TOOL_LABELS = {tid: TOOLS[tid].label for tid in DIGITAL_TOOLS} TOOL_VERSIONS = { "esim": "2.4", @@ -68,14 +66,11 @@ def relaunch_as_admin(): def load_installed_versions(): versions = {k: "Not installed" for k in TOOL_LABELS} try: - if INFO_JSON.exists(): - with open(INFO_JSON) as f: - data = json.load(f) - for pkg in data.get("important_packages", []): - name = pkg.get("package_name", "") - ver = pkg.get("version", "Not installed") - if name in versions and ver not in ("", "Not installed"): - versions[name] = ver + config = ConfigManager() + for tool_id in TOOL_LABELS: + state = config.get_tool_state(tool_id) + if state and state.get("version", "") not in ("", "Not installed"): + versions[tool_id] = state["version"] except Exception as e: print(f"Could not load version info: {e}") return versions @@ -656,7 +651,7 @@ def _run_uninstall(self, tools, label): subprocess.Popen( [PYTHON, backend, "uninstall", tool, "none"], creationflags=(subprocess.CREATE_NO_WINDOW - if sys.platform == "win32" else 0) + if IS_WINDOWS else 0) ) QMessageBox.information( self, "Uninstall Started", @@ -669,7 +664,7 @@ def _run_uninstall(self, tools, label): def main(): - if sys.platform == "win32" and not is_admin(): + if IS_WINDOWS and not is_admin(): # Note: We deliberately skip a manual PyQt consent dialog here because # Windows will natively prompt the user with a UAC shield anyway. relaunch_as_admin() diff --git a/src/toolManager/tool_manager_windows.py b/src/toolManager/tool_manager_windows.py index de268df7a..4c035eb21 100644 --- a/src/toolManager/tool_manager_windows.py +++ b/src/toolManager/tool_manager_windows.py @@ -22,22 +22,21 @@ from utils import ( run_cmd_safe, run_cmd_stream, which, print_status, DEFAULT_MSYS2_PATH, DEFAULT_ESIM_DIR, WIN_KICAD_PATHS, - WIN_NGSPICE_PATHS, WIN_LLVM_PATHS, get_msys2_bash, + WIN_NGSPICE_PATHS, WIN_LLVM_PATHS, get_msys2_bash, get_msys2_mingw_bin, get_msys2_mingw_root ) +from constants import IS_WINDOWS +from paths import get_toolmanager_root, get_install_state_path, get_download_cache_dir +from config import ConfigManager -if sys.platform == "win32": +if IS_WINDOWS: sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='ignore') sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='ignore') -BASE_DIR = Path(__file__).resolve().parent -STATE_FILE = BASE_DIR / "information.json" -BASE_DIR.mkdir(parents=True, exist_ok=True) - +BASE_DIR = get_toolmanager_root() +STATE_FILE = get_install_state_path() MSYS2_PATH = DEFAULT_MSYS2_PATH - -DOWNLOAD_DIR = BASE_DIR / "Download" -DOWNLOAD_DIR.mkdir(parents=True, exist_ok=True) +DOWNLOAD_DIR = get_download_cache_dir() VERILATOR_VERSIONS = { "5.006": "verilator-5.006.7z", @@ -202,7 +201,7 @@ def _find_ngspice_exe(): return which("ngspice") or which("ngspice.exe") def find_llvm_fixed(version=None): - if platform.system() == "Windows": + if IS_WINDOWS: import ctypes HWND_BROADCAST = 0xFFFF WM_SETTINGCHANGE = 0x001A @@ -1137,41 +1136,7 @@ def install_verilator(v, upgrade=False): except Exception as e: print_status("install_failed", str(e)[:50], v) -def read_state(): - if STATE_FILE.exists(): - try: - with open(STATE_FILE) as f: - return json.load(f) - except Exception: - pass - return {"important_packages": []} - -def write_state(data): - with open(STATE_FILE, "w") as f: - json.dump(data, f, indent=4) - -def update_state(package_name, version): - from datetime import datetime - data = read_state() - for pkg in data["important_packages"]: - if pkg["package_name"] == package_name: - pkg["version"] = version - pkg["installed_date"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S") - write_state(data) - return - data["important_packages"].append({ - "package_name": package_name, - "version": version, - "installed_date": datetime.now().strftime("%Y-%m-%d %H:%M:%S"), - }) - write_state(data) - -def get_state_version(package_name): - data = read_state() - for pkg in data["important_packages"]: - if pkg["package_name"] == package_name: - return pkg.get("version") - return None +_config = ConfigManager() ESIM_DIR = DEFAULT_ESIM_DIR @@ -1187,7 +1152,8 @@ def check_esim(target_version): ] for p in indicators: if p.exists(): - ver = get_state_version("esim") or "unknown" + esim_state = _config.get_tool_state("esim") + ver = esim_state.get("version", "unknown") if esim_state else "unknown" tv = target_version if target_version != "none" else "latest" if tv in ("latest", "none") or tv == ver: print_status("installed", ver, tv) @@ -1226,7 +1192,7 @@ def install_esim(target_version, upgrade=False): time.sleep(20) for p in [BASE_DIR / "eSim.bat", BASE_DIR / "src" / "frontEnd" / "Application.py", ESIM_DIR / "eSim.bat", ESIM_DIR / "uninst-eSim.exe"]: if p.exists(): - update_state("esim", display_ver) + _config.update_tool_state("esim", display_ver) print(f"[OK] eSim {display_ver} installed") print_status("installed", display_ver, target_version) return diff --git a/src/toolManager/utils.py b/src/toolManager/utils.py index 093e5d094..ecb99e9d0 100644 --- a/src/toolManager/utils.py +++ b/src/toolManager/utils.py @@ -18,11 +18,8 @@ import time from pathlib import Path -# ==================== CONSTANTS & DEFAULTS ==================== - -# Default Windows installation paths -DEFAULT_MSYS2_PATH = Path(r"C:\msys64") -DEFAULT_ESIM_DIR = Path(r"C:\FOSSEE\eSim") +from constants import DEFAULT_MSYS2_PATH, DEFAULT_ESIM_DIR +from platform_utils import subprocess_flags WIN_KICAD_PATHS = [ (r"C:\Program Files\KiCad\9.0\bin\kicad.exe", "9"), @@ -62,14 +59,7 @@ def run_cmd_safe(cmd, timeout=30, cwd=None, env=None): Returns the subprocess.CompletedProcess result or None if failed. """ try: - if platform.system() == "Windows": - startupinfo = subprocess.STARTUPINFO() - startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW - startupinfo.wShowWindow = subprocess.SW_HIDE - creationflags = subprocess.CREATE_NO_WINDOW - else: - startupinfo = None - creationflags = 0 + flags = subprocess_flags() result = subprocess.run( cmd, @@ -77,8 +67,8 @@ def run_cmd_safe(cmd, timeout=30, cwd=None, env=None): text=True, shell=False, timeout=timeout, - startupinfo=startupinfo, - creationflags=creationflags, + startupinfo=flags.get("startupinfo"), + creationflags=flags.get("creationflags", 0), encoding='utf-8', errors='ignore', cwd=cwd, @@ -95,14 +85,7 @@ def run_cmd_stream(cmd, timeout=900, cwd=None, env=None): Returns a tuple of (returncode, full_output_string). """ try: - if platform.system() == "Windows": - si = subprocess.STARTUPINFO() - si.dwFlags |= subprocess.STARTF_USESHOWWINDOW - si.wShowWindow = subprocess.SW_HIDE - cf = subprocess.CREATE_NO_WINDOW - else: - si = None - cf = 0 + flags = subprocess_flags() proc = subprocess.Popen( cmd, @@ -110,8 +93,8 @@ def run_cmd_stream(cmd, timeout=900, cwd=None, env=None): stderr=subprocess.STDOUT, text=True, shell=False, - startupinfo=si, - creationflags=cf, + startupinfo=flags.get("startupinfo"), + creationflags=flags.get("creationflags", 0), encoding='utf-8', errors='ignore', cwd=cwd,