Files
retroSoC/scripts/check_format.py
T
maksyuki 9c706ba0ff feat: refactor test scripts to update paths and improve organization
- Updated paths in test files to reflect new directory structure for RTL and script files.
- Changed references from rtl/mini to rtl/ip and scripts/rtl where applicable.
- Added a new test file for Tiny product isolation, ensuring independent ABI and bus acceptance.
- Enhanced tests for memory map generation and protocol contracts for Tiny architecture.
- Ensured that all tests are aligned with the latest directory structure and naming conventions.
2026-09-25 15:44:02 +08:00

148 lines
4.8 KiB
Python

#!/usr/bin/env python3
"""Format or verify the tracked self-owned Makefile and RTL sources."""
from __future__ import annotations
import argparse
import shutil
import subprocess
import sys
import tempfile
from collections.abc import Iterable, Iterator
from pathlib import Path
RTL_SUFFIXES = {".sv", ".svh", ".v", ".vh"}
SELF_OWNED_RTL_ROOTS = {
("rtl", "demo"),
("rtl", "ip"),
("rtl", "mini"),
("rtl", "tiny"),
("rtl", "tech"),
("tests", "rtl"),
}
def tracked_files(root: Path) -> list[Path]:
"""Return repository files, including worktree files after directory moves."""
result = subprocess.run(
["git", "-C", str(root), "ls-files", "--cached", "--others", "--exclude-standard", "-z"],
check=True,
stdout=subprocess.PIPE,
)
return sorted({Path(path.decode("utf-8")) for path in result.stdout.split(b"\0") if path})
def format_files(paths: Iterable[Path], kind: str) -> list[Path]:
"""Select formatter-owned files from tracked relative paths."""
if kind == "make":
return sorted(path for path in paths if path.name == "Makefile" or path.suffix == ".mk")
if kind == "rtl":
return sorted(
path
for path in paths
if path.suffix in RTL_SUFFIXES and tuple(path.parts[:2]) in SELF_OWNED_RTL_ROOTS
)
raise ValueError(f"unknown format kind: {kind}")
def chunks(paths: list[Path], size: int = 64) -> Iterator[list[Path]]:
for start in range(0, len(paths), size):
yield paths[start : start + size]
def require_tool(tool: str) -> str:
executable = shutil.which(tool)
if executable is None:
raise RuntimeError(f"formatter executable not found: {tool}")
return executable
def run(command: list[str], root: Path) -> None:
subprocess.run(command, cwd=root, check=True)
def format_makefiles(tool: str, config: Path, paths: list[Path], root: Path) -> None:
for batch in chunks(paths):
run([tool, "format", "--config", str(config), *(str(path) for path in batch)], root)
def format_rtl(tool: str, config: Path, paths: list[Path], root: Path, verify: bool) -> None:
batches: Iterable[list[Path]] = ([path] for path in paths) if verify else chunks(paths)
for batch in batches:
action = "--verify" if verify else "--inplace"
run(
[
tool,
f"--flagfile={config}",
"--failsafe_success=false",
action,
*(str(path) for path in batch),
],
root,
)
def check_makefiles(tool: str, config: Path, paths: list[Path], root: Path) -> list[Path]:
"""Return source paths that mbake would rewrite without touching the workspace."""
with tempfile.TemporaryDirectory(prefix="retrosoc-format-") as temporary:
temporary_root = Path(temporary)
copies: list[Path] = []
for source in paths:
destination = temporary_root / source
destination.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(root / source, destination)
copies.append(destination)
format_makefiles(tool, config, copies, root)
return [
source
for source, copy in zip(paths, copies, strict=True)
if (root / source).read_bytes() != copy.read_bytes()
]
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--root", type=Path, required=True)
parser.add_argument("--kind", choices=("make", "rtl"), required=True)
parser.add_argument("--apply", action="store_true")
parser.add_argument("--mbake", default="mbake")
parser.add_argument("--verible-verilog-format", default="verible-verilog-format")
args = parser.parse_args()
root = args.root.resolve()
files = [
path
for path in format_files(tracked_files(root), args.kind)
if (root / path).is_file()
]
if not files:
print(f"no tracked {args.kind} files selected")
return 0
if args.kind == "make":
tool = require_tool(args.mbake)
config = root / ".bake.toml"
if args.apply:
format_makefiles(tool, config, files, root)
return 0
changed = check_makefiles(tool, config, files, root)
if changed:
print("Makefile formatting required:", file=sys.stderr)
print("\n".join(str(path) for path in changed), file=sys.stderr)
return 1
return 0
tool = require_tool(args.verible_verilog_format)
config = root / ".verible-format"
format_rtl(tool, config, files, root, verify=not args.apply)
return 0
if __name__ == "__main__":
try:
raise SystemExit(main())
except (RuntimeError, subprocess.CalledProcessError) as error:
print(f"format check failed: {error}", file=sys.stderr)
raise SystemExit(1) from error