#!/usr/bin/env python3 """Scan xenia source for #include <...> directives and ensure case-aliases exist in xwin's flat SDK/CRT include dirs. Idempotent — safe to re-run.""" from __future__ import annotations import os, re, sys from pathlib import Path INCLUDE_RE = re.compile(r'^\s*#\s*include\s*<([A-Za-z][A-Za-z0-9_/.\-]*\.h)>', re.MULTILINE) SOURCE_ROOTS = ["src", "third_party/SDL2/src", "third_party/SDL2/include", "third_party/discord-rpc/src", "third_party/fmt", "third_party/imgui"] SOURCE_EXTS = {".h", ".hpp", ".inc", ".c", ".cc", ".cpp", ".cxx"} def collect_includes(repo_root: Path) -> set[str]: seen: set[str] = set() for root in SOURCE_ROOTS: base = repo_root / root if not base.exists(): continue for path in base.rglob("*"): if path.suffix.lower() not in SOURCE_EXTS: continue try: txt = path.read_text(encoding="utf-8", errors="ignore") except OSError: continue for m in INCLUDE_RE.finditer(txt): seen.add(m.group(1)) return seen def fix_dir(target_dir: Path, want: set[str]) -> int: if not target_dir.is_dir(): return 0 idx = {e.name.lower(): e.name for e in target_dir.iterdir()} created = 0 for name in want: if "/" in name: continue actual = idx.get(name.lower()) if actual is None or actual == name: continue link = target_dir / name if link.exists() or link.is_symlink(): continue try: link.symlink_to(actual) created += 1 except OSError: pass return created if __name__ == "__main__": if len(sys.argv) != 3: sys.exit("usage: xwin-case-symlinks.py ") repo = Path(sys.argv[1]).resolve() xwin = Path(sys.argv[2]).resolve() want = collect_includes(repo) total = 0 for d in (xwin/"crt/include", xwin/"sdk/include/ucrt", xwin/"sdk/include/um", xwin/"sdk/include/shared", xwin/"sdk/include/winrt"): total += fix_dir(d, want) print(f"Created {total} case-symlinks across {len(want)} unique includes.")