Files
retroSoC/scripts/check_format.py
T
maksyuki 8a64c160c6 refactor: refactor parameter naming conventions and update RTL style
- Changed parameter names in various modules from uppercase to camel case for consistency.
- Updated local parameters in testbenches to follow the same naming convention.
- Modified the RTL style manifest to include additional prefixes for better organization.
- Enhanced the check_format and check_rtl_style scripts to improve file tracking and naming validation.
- Added new tests to ensure compliance with the updated naming conventions and allowed local parameters in specific contexts.
2026-08-13 15:08:24 +08:00

147 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", "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