Coverage for src / rhiza_hooks / check_makefile_targets.py: 100%
40 statements
« prev ^ index » next coverage.py v7.13.3, created at 2026-02-08 08:53 +0000
« prev ^ index » next coverage.py v7.13.3, created at 2026-02-08 08:53 +0000
1#!/usr/bin/env python3
2"""Check that Makefile contains expected targets for rhiza projects."""
4from __future__ import annotations
6import argparse
7import re
8import sys
9from pathlib import Path
11# Common targets expected in rhiza-based projects
12RECOMMENDED_TARGETS = {
13 "install",
14 "test",
15 "fmt",
16 "help",
17}
19# Pattern to match Makefile target definitions
20TARGET_PATTERN = re.compile(r"^([a-zA-Z_][a-zA-Z0-9_-]*)\s*:", re.MULTILINE)
23def extract_targets(content: str) -> set[str]:
24 """Extract target names from Makefile content.
26 Args:
27 content: Contents of a Makefile
29 Returns:
30 Set of target names found
31 """
32 matches = TARGET_PATTERN.findall(content)
33 return set(matches)
36def check_makefile(filepath: Path) -> list[str]:
37 """Check a Makefile for recommended targets.
39 Args:
40 filepath: Path to the Makefile
42 Returns:
43 List of warning messages (empty if all recommended targets exist)
44 """
45 warnings: list[str] = []
47 try:
48 content = filepath.read_text()
49 except FileNotFoundError:
50 return [f"File not found: {filepath}"]
52 targets = extract_targets(content)
54 # Only check the main Makefile for recommended targets
55 if filepath.name == "Makefile":
56 missing = RECOMMENDED_TARGETS - targets
57 if missing:
58 warnings.append(f"Missing recommended targets: {', '.join(sorted(missing))}")
60 return warnings
63def main(argv: list[str] | None = None) -> int:
64 """Main entry point for the hook."""
65 parser = argparse.ArgumentParser(description="Check Makefile for recommended targets")
66 parser.add_argument(
67 "filenames",
68 nargs="*",
69 help="Filenames to check",
70 )
71 parser.add_argument(
72 "--strict",
73 action="store_true",
74 help="Exit with error if recommended targets are missing",
75 )
76 args = parser.parse_args(argv)
78 retval = 0
79 for filename in args.filenames:
80 filepath = Path(filename)
81 warnings = check_makefile(filepath)
82 if warnings:
83 print(f"{filename}:")
84 for warning in warnings:
85 print(f" - {warning}")
86 if args.strict:
87 retval = 1
89 return retval
92if __name__ == "__main__":
93 sys.exit(main())