diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0f11b54d..4ae5d0aa 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -35,7 +35,7 @@ jobs: windows-test: runs-on: windows-latest - timeout-minutes: 30 + timeout-minutes: 60 strategy: matrix: python-version: ['3.13'] @@ -49,12 +49,11 @@ jobs: - name: Install ngspice shell: powershell run: | - curl.exe -L -o ngspice.7z "https://downloads.sourceforge.net/project/ngspice/ng-spice-rework/45.2/ngspice-45.2_64.7z" - - 7z x ngspice.7z -o"C:\Program Files" - - Remove-Item ngspice.7z - "C:\Program Files\Spice64\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + # Use MSYS2 (pre-installed on windows-latest) to install ngspice. + # This avoids SourceForge mirrors which intermittently return HTML instead of binaries. + C:\msys64\usr\bin\bash.exe -lc "pacman -Sy --noconfirm mingw-w64-x86_64-ngspice" + # Expose ngspice.exe and its companion DLLs to subsequent steps + "C:\msys64\mingw64\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append - name: Install dependencies run: | diff --git a/engibench/problems/power_electronics/utils/ngspice.py b/engibench/problems/power_electronics/utils/ngspice.py index ddba6769..b08007e2 100644 --- a/engibench/problems/power_electronics/utils/ngspice.py +++ b/engibench/problems/power_electronics/utils/ngspice.py @@ -3,10 +3,11 @@ import os import platform import re +import shutil import subprocess MIN_SUPPORTED_VERSION: int = 42 # Major version number of ngspice -MAX_SUPPORTED_VERSION: int = 45 # Major version number of ngspice +MAX_SUPPORTED_VERSION: int = 46 # Major version number of ngspice class NgSpice: @@ -28,27 +29,25 @@ def _get_ngspice_path(self) -> str: """Get the path to the ngspice executable based on the operating system. Returns: - The path to ngspice executable or None if not found + The path to the ngspice executable """ if self.system == "windows": # For Windows, use the bundled ngspice.exe - # Look for ngspice in Spice64 folder and common install locations + # Look for ngspice in PATH (e.g. Chocolatey), Spice64 folder and common install locations possible_paths = [ self.ngspice_windows_path, - "ngspice.exe", + shutil.which("ngspice"), os.path.normpath(os.path.join("C:/Program Files/Spice64/bin/ngspice.exe")), os.path.normpath(os.path.join("C:/Program Files (x86)/ngspice/bin/ngspice.exe")), ] - for path in possible_paths: - if path and os.path.exists(path): - ngspice_path: str | None = path - break - else: - ngspice_path = possible_paths[0] # Default to first path if none found - if ngspice_path is None or not os.path.exists(ngspice_path): + ngspice_path: str | None = next((p for p in possible_paths if p and os.path.exists(p)), None) + if ngspice_path is None: raise FileNotFoundError( - f"ngspice.exe not found at {ngspice_path}. You can download it from https://sourceforge.net/projects/ngspice/files/ng-spice-rework/45.2/. You can also see our GitHub Actions workflow (test.yml) for how to automatically install it." + "ngspice.exe not found. You can install it via Chocolatey " + "(`choco install ngspice`) or download it from " + "https://sourceforge.net/projects/ngspice/files/ng-spice-rework/. " + "You can also see our GitHub Actions workflow (test.yml) for how to automatically install it." ) return ngspice_path if self.system in ["darwin", "linux"]: @@ -108,18 +107,21 @@ def version(self) -> int: subprocess.CalledProcessError: If ngspice fails to run """ if self.system == "windows": + # Try finding the version from the docs folder (for SourceForge binary package) pattern_int = re.compile(r"ngspice-(\d+)-manual\.pdf") pattern_dec = re.compile(r"ngspice-(\d+\.\d+)-manual\.pdf") docs_path = os.path.normpath(os.path.join(os.path.dirname(self._ngspice_path), "../docs/")) - for filename in os.listdir(docs_path): - match_int = pattern_int.match(filename) - match_dec = pattern_dec.match(filename) - if match_int: - return int(match_int.group(1)) # Already returns just the major version - if match_dec: - return int(match_dec.group(1).split(".")[0]) # Return only the major version - raise NgSpiceManualNotFoundError + try: + for filename in os.listdir(docs_path): + match_int = pattern_int.match(filename) + match_dec = pattern_dec.match(filename) + if match_int: + return int(match_int.group(1)) # Already returns just the major version + if match_dec: + return int(match_dec.group(1).split(".")[0]) # Return only the major version + except OSError: + print(f"Could not read ngspice docs folder at {docs_path!r}, falling back to --version flag.") cmd = [self._ngspice_path, "--version"] result = subprocess.run(cmd, capture_output=True, text=True, check=True)