Some checks failed
Orchestrator / Commit Message Validation (push) Has been skipped
Orchestrator / Lint (push) Failing after 2m0s
Orchestrator / Windows (x86-64) (push) Has been skipped
Orchestrator / Linux (x86-64) (push) Has been skipped
Orchestrator / Create Release (push) Has been skipped
Working-tree snapshot taken 2026-07-28 so this work is not lost. To be clear about provenance: NONE of this was written in the session that committed it — it is pre-existing uncommitted work on phase-a-tracing, last committed 2026-07-09, from the xenia-rs decoder/deadlock investigation. It is recorded verbatim, not reviewed and not tested. Contents: 24 tracked modifications (ppc_emit_memory, xex_module, object_table, shim_utils, xboxkrnl threading/rtl, xevent/xobject/xthread, memory) plus the untracked probe sources audit_68_host_mem_watch, audit_69_event_signal_watch, audit_70_semaphore_release_watch, phase_b_snapshot, and cmake/toolchains. Deliberately excluded: build-cross/ (51 GB of build output) and vkd3d-proton.cache. Note third_party/snappy is a submodule pointer change whose target may not be pushed anywhere, so this branch is a preservation record rather than a guaranteed-buildable tree. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
127 lines
4.1 KiB
Python
127 lines
4.1 KiB
Python
#!/usr/bin/env python3
|
|
"""Scan xenia source for #include directives and ensure case-aliases exist
|
|
in xwin's flat SDK/CRT include dirs. xwin's default splat adds lowercase
|
|
aliases for headers that ship with uppercase names (e.g. Windows.h),
|
|
but does NOT add aliases in the reverse direction. xenia (and its
|
|
third_party deps) include some headers using historical Microsoft casing
|
|
(<ObjBase.h>, <Psapi.h>) that don't match the lowercase filenames the
|
|
SDK ships with. This helper creates the missing symlinks.
|
|
|
|
Idempotent: re-running is safe.
|
|
"""
|
|
from __future__ import annotations
|
|
import os
|
|
import re
|
|
import 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]:
|
|
"""Return set of all <foo.h>-style include targets found in source."""
|
|
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 index_dir(d: Path) -> dict[str, str]:
|
|
"""Return mapping lowercased-basename -> actual basename for files in d."""
|
|
if not d.is_dir():
|
|
return {}
|
|
idx: dict[str, str] = {}
|
|
for entry in d.iterdir():
|
|
idx.setdefault(entry.name.lower(), entry.name)
|
|
return idx
|
|
|
|
|
|
def fix_dir(target_dir: Path, want: set[str]) -> tuple[int, int, list[str]]:
|
|
"""For each header name in `want` (basename only), create a symlink to
|
|
the case-folded sibling if the exact case doesn't exist. Returns
|
|
(created, already_ok, missing-from-sdk)."""
|
|
if not target_dir.is_dir():
|
|
return 0, 0, []
|
|
idx = index_dir(target_dir)
|
|
created = 0
|
|
already_ok = 0
|
|
missing: list[str] = []
|
|
for name in want:
|
|
# Only look at single-segment names; subdir/foo.h handled separately
|
|
if "/" in name:
|
|
continue
|
|
actual = idx.get(name.lower())
|
|
if actual is None:
|
|
missing.append(name)
|
|
continue
|
|
if actual == name:
|
|
already_ok += 1
|
|
continue
|
|
link = target_dir / name
|
|
if link.exists() or link.is_symlink():
|
|
already_ok += 1
|
|
continue
|
|
try:
|
|
link.symlink_to(actual)
|
|
created += 1
|
|
except OSError as e:
|
|
print(f" ! failed to symlink {link} -> {actual}: {e}",
|
|
file=sys.stderr)
|
|
return created, already_ok, missing
|
|
|
|
|
|
def main() -> int:
|
|
if len(sys.argv) != 3:
|
|
print("usage: xwin-case-symlinks.py <xenia-canary-root> <xwin-splat-dir>",
|
|
file=sys.stderr)
|
|
return 2
|
|
repo_root = Path(sys.argv[1]).resolve()
|
|
xwin_root = Path(sys.argv[2]).resolve()
|
|
if not (xwin_root / "crt" / "include").exists():
|
|
print(f"error: {xwin_root}/crt/include missing", file=sys.stderr)
|
|
return 1
|
|
want = collect_includes(repo_root)
|
|
print(f"Collected {len(want)} unique #include <...> targets")
|
|
sdk_dirs = [
|
|
xwin_root / "crt" / "include",
|
|
xwin_root / "sdk" / "include" / "ucrt",
|
|
xwin_root / "sdk" / "include" / "um",
|
|
xwin_root / "sdk" / "include" / "shared",
|
|
xwin_root / "sdk" / "include" / "winrt",
|
|
]
|
|
total_created = 0
|
|
for d in sdk_dirs:
|
|
created, ok, _missing = fix_dir(d, want)
|
|
if created:
|
|
print(f" {d}: created {created} case-symlinks")
|
|
total_created += created
|
|
print(f"Done: created {total_created} case-symlinks total")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|