tianole/scripts/tools/structure_checks/makefiles.py

88 lines
2.0 KiB
Python

import re
from pathlib import Path
from .common import all_files, c_files, read_text
MAKEFILE_SOURCE_VARS = (
"ARCH_KERNEL_SRCS",
"ARCH_MM_SRCS",
"BOOT_SRCS",
"KERNEL_SRCS",
"MM_SRCS",
)
def expand_make_var_token(token: str, makefile: Path) -> str:
return token.replace("$(ARCH_DIR)", "arch/x86").replace(
"$(BUILD_DIR)", "build"
)
def parse_makefile_sources(root: Path, makefile: Path) -> set[Path]:
text = read_text(root, makefile)
lines = text.splitlines()
sources = set()
index = 0
while index < len(lines):
line = lines[index]
match = re.match(r"^([A-Z0-9_]+)\s*:=", line)
if match == None or match.group(1) not in MAKEFILE_SOURCE_VARS:
index += 1
continue
remainder = line.split(":=", 1)[1].strip()
while True:
continued = remainder.endswith("\\")
remainder = remainder[:-1].strip() if continued else remainder
if remainder:
for token in remainder.split():
if token.endswith(".c"):
sources.add(Path(expand_make_var_token(token, makefile)))
if not continued:
break
index += 1
if index >= len(lines):
break
remainder = lines[index].strip()
index += 1
return sources
def makefile_sources(root: Path) -> set[Path]:
sources = set()
for makefile in sorted(root.rglob("Makefile")):
if "build" in makefile.parts:
continue
sources.update(parse_makefile_sources(root, makefile.relative_to(root)))
return sources
def check_makefile_source_lists(root: Path, files: set[Path], all_mode: bool) -> list[str]:
errors = []
listed = makefile_sources(root)
existing = set(c_files(all_files(root)))
for path in sorted(listed):
if not (root / path).exists():
errors.append(f"{path}: listed in Makefile but file does not exist")
if all_mode:
for path in sorted(existing - listed):
errors.append(f"{path}: C source is not listed in a Makefile source list")
else:
for path in sorted(c_files(files)):
if path not in listed:
errors.append(
f"{path}: changed C source is not listed in a Makefile source list"
)
return errors