170 lines · 5.4 KB · Python
RawDownload
from __future__ import annotations import dataclassesimport osimport pathlibimport platformimport reimport shutilimport subprocess from scripts.pgso.model import PgsoError  REQUIRED_ZIG_VERSION = "0.16.0"REQUIRED_LLVM_VERSION = "21.1.8"SUPPORTED_TARGET = "aarch64-macos"  def _resolve_executable(command: str, display_name: str) -> pathlib.Path:    resolved = shutil.which(command)    if resolved is None:        raise PgsoError(f"missing executable: {display_name}")    return pathlib.Path(resolved).resolve()  def _capture(argv: tuple[str, ...], stage: str) -> str:    try:        result = subprocess.run(            argv,            capture_output=True,            text=True,            encoding="utf-8",            errors="replace",            timeout=10,            check=False,        )    except (OSError, subprocess.TimeoutExpired) as error:        raise PgsoError(f"could not query {stage}: {error}") from error    if result.returncode != 0:        raise PgsoError(            f"could not query {stage}: exit code {result.returncode}: "            f"{result.stderr.strip()}"        )    return result.stdout.strip()  def _llvm_version(tool: pathlib.Path, display_name: str) -> str:    output = _capture((str(tool), "--version"), f"{display_name} version")    match = re.search(r"\b\d+\.\d+\.\d+\b", output)    if match is None:        raise PgsoError(f"could not parse LLVM version from {display_name}: {output}")    version = match.group(0)    if version != REQUIRED_LLVM_VERSION:        raise PgsoError(            f"PGSO requires LLVM {REQUIRED_LLVM_VERSION}; "            f"{display_name} reported {version}"        )    return version  @dataclasses.dataclass(frozen=True)class Toolchain:    zig: pathlib.Path    llvm_bin: pathlib.Path    opt: pathlib.Path    llc: pathlib.Path    llvm_profdata: pathlib.Path    llvm_ar: pathlib.Path    clang: pathlib.Path    strip: pathlib.Path    codesign: pathlib.Path    otool: pathlib.Path    xcrun: pathlib.Path    sdk: pathlib.Path    profile_runtime: pathlib.Path    zig_version: str    llvm_version: str    target: str    host_arch: str     @classmethod    def discover(        cls,        zig: str,        llvm_bin: str,        target: str,    ) -> "Toolchain":        if target != SUPPORTED_TARGET:            raise PgsoError(f"unsupported target: {target}")        if platform.system() != "Darwin":            raise PgsoError("PGSO requires a Darwin host")        host_arch = platform.machine()        if host_arch != "arm64":            raise PgsoError("PGSO requires an arm64 host")         zig_path = _resolve_executable(zig, "zig")        zig_version = _capture((str(zig_path), "version"), "Zig version")        if zig_version != REQUIRED_ZIG_VERSION:            raise PgsoError(                f"PGSO requires Zig {REQUIRED_ZIG_VERSION}; "                f"zig reported {zig_version}"            )         llvm_root = pathlib.Path(llvm_bin).expanduser().resolve()        if not llvm_root.is_dir():            raise PgsoError(f"LLVM bin root does not exist: {llvm_root}")         llvm_tools: dict[str, pathlib.Path] = {}        for name in ("opt", "llc", "llvm-profdata", "llvm-ar", "clang"):            candidate = llvm_root / name            if not candidate.is_file() or not os.access(candidate, os.X_OK):                raise PgsoError(f"missing executable: {name}")            resolved = candidate.resolve()            if resolved.parent != llvm_root:                raise PgsoError(                    f"LLVM tool escapes configured root: {name}: {resolved}"                )            llvm_tools[name] = resolved         versions = {            name: _llvm_version(path, name)            for name, path in llvm_tools.items()        }        llvm_version = versions["opt"]         strip = _resolve_executable("strip", "strip")        codesign = _resolve_executable("codesign", "codesign")        otool = _resolve_executable("otool", "otool")        xcrun = _resolve_executable("xcrun", "xcrun")         sdk_output = _capture(            (str(xcrun), "--sdk", "macosx", "--show-sdk-path"),            "macOS SDK path",        )        sdk = pathlib.Path(sdk_output).expanduser().resolve()        if not sdk.is_dir():            raise PgsoError(f"macOS SDK does not exist: {sdk}")         resource_output = _capture(            (str(llvm_tools["clang"]), "--print-resource-dir"),            "clang resource directory",        )        resource_dir = pathlib.Path(resource_output).expanduser().resolve()        profile_runtime = (            resource_dir / "lib" / "darwin" / "libclang_rt.profile_osx.a"        )        if not profile_runtime.is_file() or profile_runtime.stat().st_size == 0:            raise PgsoError(                f"missing LLVM profile runtime: {profile_runtime}"            )         return cls(            zig=zig_path,            llvm_bin=llvm_root,            opt=llvm_tools["opt"],            llc=llvm_tools["llc"],            llvm_profdata=llvm_tools["llvm-profdata"],            llvm_ar=llvm_tools["llvm-ar"],            clang=llvm_tools["clang"],            strip=strip,            codesign=codesign,            otool=otool,            xcrun=xcrun,            sdk=sdk,            profile_runtime=profile_runtime.resolve(),            zig_version=zig_version,            llvm_version=llvm_version,            target=target,            host_arch=host_arch,        )