mirror of
https://github.com/herdrdev/herdr.git
synced 2026-09-14 18:03:41 +08:00
107 lines
3.8 KiB
Python
107 lines
3.8 KiB
Python
"""One-time Windows SDK setup and Windows target linting from Unix hosts."""
|
|
|
|
import argparse
|
|
import os
|
|
from pathlib import Path
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
|
|
|
|
SDK_ROOT = Path.home() / ".local/share/herdr/windows-cross"
|
|
LIBC_ENV = "LIBGHOSTTY_VT_WINDOWS_LIBC"
|
|
TARGET = "x86_64-pc-windows-msvc"
|
|
|
|
|
|
def libc_contents(root: Path) -> str:
|
|
paths = {
|
|
"include_dir": root / "sdk/include/ucrt",
|
|
"sys_include_dir": root / "crt/include",
|
|
"crt_dir": root / "sdk/lib/ucrt/x86_64",
|
|
"msvc_lib_dir": root / "crt/lib/x86_64",
|
|
"kernel32_lib_dir": root / "sdk/lib/um/x86_64",
|
|
}
|
|
required = {
|
|
"include_dir": "stdlib.h",
|
|
"sys_include_dir": "vcruntime.h",
|
|
"crt_dir": "ucrt.lib",
|
|
"msvc_lib_dir": "vcruntime.lib",
|
|
"kernel32_lib_dir": "kernel32.lib",
|
|
}
|
|
for key, filename in required.items():
|
|
if not (paths[key] / filename).is_file():
|
|
raise ValueError(f"Windows SDK is incomplete: missing {paths[key] / filename}")
|
|
return "".join(f"{key}={value}\n" for key, value in paths.items()) + "gcc_dir=\n"
|
|
|
|
|
|
def libc_path() -> Path:
|
|
override = os.environ.get(LIBC_ENV)
|
|
path = Path(override).expanduser() if override else SDK_ROOT / "libc.txt"
|
|
if not path.is_file():
|
|
raise ValueError(
|
|
f"Windows cross-check needs SDK configuration at {path}.\n"
|
|
"Run `just setup-windows-cross` once, or set "
|
|
f"{LIBC_ENV} to an existing Zig libc configuration."
|
|
)
|
|
return path.resolve()
|
|
|
|
|
|
def setup(accept_license: bool) -> None:
|
|
if not shutil.which("xwin"):
|
|
raise ValueError("Install xwin first: cargo install xwin --locked")
|
|
if not shutil.which(os.environ.get("ZIG", "zig")):
|
|
raise ValueError("Install Zig 0.16.0 first, or set ZIG to its executable.")
|
|
SDK_ROOT.mkdir(parents=True, exist_ok=True)
|
|
# SDK downloads can be large; /tmp is often RAM-backed on Linux.
|
|
temp_parent = "/var/tmp" if sys.platform.startswith("linux") else None
|
|
with tempfile.TemporaryDirectory(prefix="herdr-windows-sdk-", dir=temp_parent) as cache:
|
|
command = ["xwin", "--arch", "x86_64", "--cache-dir", cache]
|
|
if accept_license:
|
|
command.append("--accept-license")
|
|
subprocess.run(command + ["splat", "--copy", "--output", str(SDK_ROOT)], check=True)
|
|
config = SDK_ROOT / "libc.txt"
|
|
config.write_text(libc_contents(SDK_ROOT))
|
|
subprocess.run(
|
|
[os.environ.get("ZIG", "zig"), "libc", "-target", "x86_64-windows-msvc", str(config)],
|
|
check=True,
|
|
)
|
|
print(f"Windows SDK configured at {config}. Run `just windows-lint` or `just check`.")
|
|
|
|
|
|
def lint() -> None:
|
|
env = {**os.environ, LIBC_ENV: str(libc_path()), "LIBGHOSTTY_VT_SIMD": "false"}
|
|
subprocess.run(["rustup", "target", "add", TARGET], check=True)
|
|
subprocess.run(
|
|
["cargo", "clippy", "--bin", "herdr", "--locked", "--target", TARGET, "--", "-D", "warnings"],
|
|
env=env,
|
|
check=True,
|
|
)
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
commands = parser.add_subparsers(dest="command", required=True)
|
|
setup_parser = commands.add_parser("setup", help="Download Microsoft's SDK using xwin")
|
|
setup_parser.add_argument(
|
|
"--accept-license", action="store_true",
|
|
help="Explicitly accept Microsoft's SDK license instead of xwin's interactive prompt",
|
|
)
|
|
commands.add_parser("lint", help="Run Windows clippy with the configured SDK")
|
|
args = parser.parse_args()
|
|
try:
|
|
if args.command == "setup":
|
|
setup(args.accept_license)
|
|
else:
|
|
lint()
|
|
except (ValueError, OSError) as error:
|
|
print(error, file=sys.stderr)
|
|
return 1
|
|
except subprocess.CalledProcessError as error:
|
|
return error.returncode
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|