Private
Public Access
88 lines
3.6 KiB
Python
Executable File
88 lines
3.6 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Check Capy formatter idempotence and Wasm preservation."""
|
|
import argparse
|
|
import json
|
|
import pathlib
|
|
import re
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
|
|
|
|
def command(capyc, args, input_bytes=None):
|
|
result = subprocess.run(
|
|
[str(capyc), *map(str, args)],
|
|
input=input_bytes,
|
|
capture_output=True,
|
|
timeout=60,
|
|
)
|
|
if result.returncode:
|
|
detail = result.stderr.decode(errors="replace").strip()
|
|
raise RuntimeError(detail or f"capyc exited with {result.returncode}")
|
|
return result.stdout
|
|
|
|
|
|
def wasm(capyc, source, root, output, abi):
|
|
command(capyc, [source, "-o", output, "--source-map", str(output) + ".source-map",
|
|
"--abi-version", abi, "--source-root", root])
|
|
return pathlib.Path(output).read_bytes()
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("tree", type=pathlib.Path, help="source tree to check")
|
|
parser.add_argument("--capyc", type=pathlib.Path, default=pathlib.Path("bin/capyc"))
|
|
parser.add_argument("--allow-backtrace", action="store_true",
|
|
help="allow the approved site/tests/capy-backtrace.capy exception")
|
|
args = parser.parse_args()
|
|
tree = args.tree.resolve()
|
|
capyc = args.capyc.resolve()
|
|
if not tree.is_dir():
|
|
parser.error(f"source tree does not exist: {tree}")
|
|
abi_text = (capyc.parent.parent / "src/wasm/abi.h").read_text()
|
|
abi = re.search(r"#define BEARER_WASM_CORE_ABI_VERSION\s+(\d+)", abi_text)[1]
|
|
paths = sorted(tree.rglob("*.capy"))
|
|
counts = {"same": 0, "changed": 0, "exception": 0, "failed": 0}
|
|
failures = []
|
|
with tempfile.TemporaryDirectory(prefix="capy-format-accept-") as scratch_name:
|
|
scratch = pathlib.Path(scratch_name)
|
|
work_root = scratch / "source"
|
|
shutil.copytree(tree, work_root)
|
|
for relative in (path.relative_to(tree) for path in paths):
|
|
source = work_root / relative
|
|
original_bytes = source.read_bytes()
|
|
try:
|
|
formatted_bytes = command(capyc, ["--format", "-"], input_bytes=original_bytes)
|
|
if command(capyc, ["--format", "-"], input_bytes=formatted_bytes) != formatted_bytes:
|
|
raise RuntimeError("formatter is not idempotent")
|
|
output = scratch / "unit.wasm"
|
|
before = wasm(capyc, source, work_root, output, abi)
|
|
after = before
|
|
if original_bytes != formatted_bytes:
|
|
source.write_bytes(formatted_bytes)
|
|
after = wasm(capyc, source, work_root, output, abi)
|
|
if before != after:
|
|
if args.allow_backtrace and relative.as_posix() == "tests/capy-backtrace.capy":
|
|
counts["exception"] += 1
|
|
print(f"EXCEPTION {relative}: embedded source locations change backtrace bytes")
|
|
else:
|
|
raise RuntimeError("Wasm differs")
|
|
elif original_bytes == formatted_bytes:
|
|
counts["same"] += 1
|
|
else:
|
|
counts["changed"] += 1
|
|
except Exception as error:
|
|
counts["failed"] += 1
|
|
failures.append((relative.as_posix(), str(error)))
|
|
print(f"FAIL {relative}: {error}")
|
|
finally:
|
|
source.write_bytes(original_bytes)
|
|
summary = {"count": len(paths), "counts": counts, "failures": failures}
|
|
print(json.dumps(summary, indent=2))
|
|
return 1 if failures else 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|