feat: enhance CI configuration and code formatting tools

- Updated  to validate C, Makefile, and RTL formatting in addition to existing checks for Python, YAML, and GitHub Actions.
- Added new dependencies to  for improved formatting and documentation tools.
- Introduced a new script  to format or verify tracked self-owned Makefile and RTL sources.
- Improved RTL code formatting across multiple files by ensuring consistent spacing and line endings.
- Refactored various RTL modules to enhance readability and maintainability.
- Added unit tests for the new formatting functionality to ensure correct behavior.
This commit is contained in:
maksyuki committed 2026-07-21 14:15:40 +08:00
1 parent 91b4f78892
commit 921f8caa64
35 files changed
+349 -134

No files matched your search

+141
View File
@@ -0,0 +1,141 @@
#!/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"),
}
def tracked_files(root: Path) -> list[Path]:
"""Return paths tracked by Git, relative to *root*."""
result = subprocess.run(
["git", "-C", str(root), "ls-files", "-z"],
check=True,
stdout=subprocess.PIPE,
)
return [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 = format_files(tracked_files(root), args.kind)
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