42 lines
1.7 KiB
Python
42 lines
1.7 KiB
Python
import re
|
|
import shutil
|
|
import subprocess
|
|
import time
|
|
from pathlib import Path
|
|
|
|
from .models import Toolchain
|
|
|
|
|
|
class ToolchainError(RuntimeError):
|
|
pass
|
|
|
|
|
|
def _decode(value):
|
|
return value if isinstance(value, str) else (value or b"").decode("utf-8", errors="replace")
|
|
|
|
|
|
def discover_toolchain(root: Path, *, which=shutil.which, run=subprocess.run, deadline=None, clock=time.monotonic) -> Toolchain:
|
|
root = root.resolve()
|
|
executable = which("dotnet")
|
|
if executable is None or not Path(executable).is_file():
|
|
raise ToolchainError("dotnet not found; install .NET SDK 8 and check PATH and global.json")
|
|
executable = Path(executable).resolve()
|
|
argv = [str(executable), "--version"]
|
|
context = f"argv={argv!r}; cwd={str(root)!r}; check .NET SDK 8 and global.json"
|
|
timeout = 1800 if deadline is None else min(1800, deadline - clock())
|
|
if timeout <= 0:
|
|
raise ToolchainError(f"SDK probe timed out before it could start; {context}")
|
|
try:
|
|
result = run(argv, cwd=root, capture_output=True, check=False, shell=False, timeout=timeout)
|
|
except subprocess.TimeoutExpired as exc:
|
|
raise ToolchainError(f"SDK probe timed out; {context}") from exc
|
|
except OSError as exc:
|
|
raise ToolchainError(f"SDK probe could not start: {exc}; {context}") from exc
|
|
if result.returncode != 0:
|
|
diagnostic = (_decode(result.stdout) + "\n" + _decode(result.stderr)).strip()[-8000:]
|
|
raise ToolchainError(f"SDK probe failed with exit code {result.returncode}; {context}; output tail: {diagnostic}")
|
|
version = _decode(result.stdout).strip()
|
|
if not re.fullmatch(r"8\.0\.\d+", version):
|
|
raise ToolchainError(f"expected stable .NET SDK 8, got {version!r}; {context}")
|
|
return Toolchain(executable, version)
|