сборка: гигиена (П5) — stale .rel, все тесты в make all, размерный регресс
- lib/Makefile: stale .rel удаляются сверкой списка модулей перед упаковкой; штамп build/.modules триггерит перелинковку при любом изменении состава исходников (при смене списка архив сносится — на exFAT гранулярность mtime грубая, сравнение времён ненадёжно) - top-level TESTS: все каталоги tests/ теперь собираются make all (43 программы; banktest переименован из banked.exe — конфликт имён с tests/banked); mdview2 добавлен в APPS - размерный регресс: toolchain/size_check.py сверяет _CODE всех программ с docs/size_baseline.tsv; make size-check / size-baseline - заголовки: контракт затенения SDCC задокументирован в docs/libc-headers.md; новый string.h (include_next + strlwr/strupr); из sprinter_compat.h убраны макросы min/max — конфликтовали с функциями из stdlib.h, и в Solid-C min/max тоже функции Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
size_check.py — размерный регресс-тест: сравнение _CODE всех тестов и
|
||||
приложений с эталоном docs/size_baseline.tsv.
|
||||
|
||||
Использование:
|
||||
python3 toolchain/size_check.py # сверка с эталоном
|
||||
python3 toolchain/size_check.py --update # перезаписать эталон
|
||||
|
||||
Логика: собираем _CODE из .map каждой программы (tests/*, examples/*),
|
||||
сравниваем с эталоном. Любое отличие печатается; выход 1 — если есть
|
||||
РОСТ (случайное разжирение libc), 0 — если только уменьшения/без
|
||||
изменений. Новые программы (нет в эталоне) — предупреждение, добавить
|
||||
через --update. Запускать после `make all`.
|
||||
"""
|
||||
|
||||
import glob
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
BASELINE = os.path.join(ROOT, "docs", "size_baseline.tsv")
|
||||
|
||||
|
||||
def collect():
|
||||
"""Имя программы → размер _CODE (байт) по свежим .map."""
|
||||
sizes = {}
|
||||
pats = [
|
||||
os.path.join(ROOT, "tests", "*", ".sprinter-cc-*", "*.map"),
|
||||
os.path.join(ROOT, "examples", "*", ".sprinter-cc-*", "*.map"),
|
||||
]
|
||||
for pat in pats:
|
||||
for map_path in glob.glob(pat):
|
||||
name = os.path.splitext(os.path.basename(map_path))[0]
|
||||
# .map должен лежать в своей папке .sprinter-cc-<name>
|
||||
if os.path.basename(os.path.dirname(map_path)) != ".sprinter-cc-" + name:
|
||||
continue
|
||||
with open(map_path, encoding="utf-8", errors="replace") as f:
|
||||
for line in f:
|
||||
m = re.match(r"^_CODE\s+([0-9A-Fa-f]+)\s+([0-9A-Fa-f]+)", line)
|
||||
if m:
|
||||
sizes[name] = int(m.group(2), 16)
|
||||
break
|
||||
return sizes
|
||||
|
||||
|
||||
def load_baseline():
|
||||
base = {}
|
||||
if os.path.exists(BASELINE):
|
||||
with open(BASELINE) as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if not line or line.startswith("#"):
|
||||
continue
|
||||
name, size = line.split("\t")
|
||||
base[name] = int(size)
|
||||
return base
|
||||
|
||||
|
||||
def save_baseline(sizes):
|
||||
with open(BASELINE, "w") as f:
|
||||
f.write("# Эталон размеров _CODE (байт); обновление: "
|
||||
"python3 toolchain/size_check.py --update\n")
|
||||
for name in sorted(sizes):
|
||||
f.write(f"{name}\t{sizes[name]}\n")
|
||||
|
||||
|
||||
def main():
|
||||
update = "--update" in sys.argv
|
||||
sizes = collect()
|
||||
if not sizes:
|
||||
print("size_check: .map не найдены — сначала `make all`")
|
||||
return 2
|
||||
|
||||
if update:
|
||||
save_baseline(sizes)
|
||||
print(f"size_check: эталон обновлён ({len(sizes)} программ) → "
|
||||
f"{os.path.relpath(BASELINE, ROOT)}")
|
||||
return 0
|
||||
|
||||
base = load_baseline()
|
||||
if not base:
|
||||
print("size_check: эталона нет — создать: size_check.py --update")
|
||||
return 2
|
||||
|
||||
grew = 0
|
||||
for name in sorted(set(sizes) | set(base)):
|
||||
new, old = sizes.get(name), base.get(name)
|
||||
if new is None:
|
||||
print(f" {name}: исчез из сборки (был {old} Б)")
|
||||
elif old is None:
|
||||
print(f" {name}: НОВЫЙ ({new} Б) — добавить в эталон (--update)")
|
||||
elif new != old:
|
||||
mark = "РОСТ" if new > old else "ок, меньше"
|
||||
print(f" {name}: {old} -> {new} ({new - old:+d} Б) [{mark}]")
|
||||
if new > old:
|
||||
grew += 1
|
||||
if grew:
|
||||
print(f"size_check: {grew} программ(ы) ВЫРОСЛИ — проверить правки libc")
|
||||
return 1
|
||||
print(f"size_check: OK ({len(sizes)} программ, роста нет)")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user