Проверить VSIX после разделения и принять размерный эталон SDK

This commit is contained in:
Александр Петров
2026-09-16 15:00:12 +03:00
parent 4ee45f0ace
commit d25f28e1cd
4 changed files with 105 additions and 48 deletions
+4 -4
View File
@@ -86,12 +86,12 @@ sdbg-tests: tools lib
$(PYTHON) -m unittest discover -s tests/sdbg -v
# Размерный регресс: сверить _CODE всех программ с docs/size_baseline.tsv.
size-check:
$(PYTHON) toolchain/size_check.py
size-check: tests
$(PYTHON) toolchain/size_check.py --require $(TESTS)
# Принять текущие размеры как эталон (после осознанных изменений).
size-baseline:
$(PYTHON) toolchain/size_check.py --update
size-baseline: tests
$(PYTHON) toolchain/size_check.py --update --require $(TESTS)
clean:
$(MAKE) -C toolchain/mkexe clean
+20 -10
View File
@@ -249,14 +249,14 @@ debug packages, носители, снимки, внешние источник
их документы и историю. Критерий: каждый продукт собирается и проходит
доступные host/MAME проверки из изолированного клона без Examples и
других приложений.
6. **Реализовано; остаётся ручная проверка установленного VSIX после разделения — выделить extension.** Научить VS Code находить SDK DAP независимо от
6. **Выполнено — выделить extension.** Научить VS Code находить SDK DAP независимо от
workspace, запускать build/run/debug приложения и показывать ошибки
разрешения путей/версий. Проверить VSIX в отдельном workspace SprPoP
или Volkov, а не только в `C-Compiler`. Критерий: F5 строит debug package,
ждёт DSS, доходит до `main`, принимает breakpoint/logpoint и клавиатуру;
опция родного окна MAME работает на macOS. Windows остаётся явно
ограниченной до отдельной реализации host transport.
7. **Выполнено с открытым размерным baseline — очистить SDK и документы.** Заменить корневые цели, release-скрипт,
7. **Выполнено — очистить SDK и документы.** Заменить корневые цели, release-скрипт,
README, `AGENTS.md`, инструкции автотеста/отладки и рабочие ссылки.
Проверить `make`, `make -C libc`, `make -C libbgi`, `make size-check`,
SDK host/sdbg tests и отдельные проекты. Критерий: в SDK нет tracked
@@ -276,15 +276,25 @@ SprPoP, Volkov и PoP-Archive PoC. Живые последовательные D
Extension воспроизводимо упакован через `npm ci && npm run package` и VSIX
версии 0.2.0 установлен в локальный VS Code. Рецепт и `package-lock.json`
хранятся в VSCode-Sprinter.
хранятся в VSCode-Sprinter. Изолированный профиль VS Code с файлами
установленного VSIX выполнил F5-сценарий SprPoP: задача `make SRC_DEBUG=1
hdd` завершилась, DAP остановился в `src/sprpop.c:264` (`main`). Проверка
шла через API extension host; автоматизировать физическое нажатие F5 через
macOS Accessibility из текущего окружения не удалось.
`make size-check` пока не принят: текущий baseline показывает 12 старых
увеличений (обычно +9 байт, `openenv` +188) и несколько исчезнувших прежних
тестов. Реорганизация не меняла libc/libbgi, поэтому эталон автоматически не
перезаписывался; расхождения нужно разобрать отдельно. После разделения ещё
нужен ручной F5-прогон установленного VSIX в чистом workspace. Несколько
одновременных MAME, маршрутизация MCP между ними и Windows transport остаются
явно непроверенными сценариями.
Эталон `make size-check` разобран и обновлён. Десять тестов, использующих
`open()`, выросли на 9 байт из-за исправления таблицы режимов ESTEX после
предыдущего эталона (ревизия `879f2ba`). `openenv` вырос на 188 байт: те же
9 байт и новый тест записи через `O_RDONLY`. Для `w3bgfx` прежняя запись
5243 байта была несвежей: пересборка в отдельном worktree самой ревизии
`879f2ba` тем же SDCC дала 5257 байт, как и текущая сборка. Других
изменений размеров свежих карт нет. Теперь `size-check` сначала собирает
обязательные SDK-тесты, требует их свежие карты и пропускает устаревшие
артефакты необязательных тестов; `size-baseline` сохраняет их прежние
значения, пока эти тесты не собраны заново.
Несколько одновременных MAME, маршрутизация MCP между ними и Windows
transport остаются явно непроверенными сценариями.
Каждый этап заканчивается проверкой в отдельном репозитории и просмотром Git
diff. Новые Git remote для выделенных репозиториев и публикация их релизов
+13 -13
View File
@@ -1,4 +1,4 @@
# Эталон размеров _CODE (байт); обновление: python3 toolchain/size_check.py --update
# Эталон размеров _CODE (байт); обновление: make size-baseline
accfill 3797
accop 7067
argv 3445
@@ -14,18 +14,18 @@ bgitest 3762
bios_text 4475
blitperf 5860
blitw 4939
cat 927
cblstream 5944
cat 936
cblstream 5953
cbltest 6090
cblwav 6183
cblwav 6192
conio 4619
conio2 3943
convbench 3508
dec_test 874
errno 5980
fbench 8149
fdmax 6087
filetest 10611
errno 5989
fbench 8158
fdmax 6096
filetest 10620
fpsdiv 4870
gets 523
gfx_dbuf 5207
@@ -40,26 +40,26 @@ ls 4849
malloc 4475
mem_test 4569
mouse 4396
openenv 6138
openenv 6326
pageflip 6334
palfile 5406
ptime 5758
rt_test 5216
scroll 6744
seek 4202
seek 4211
simple 969
solidt 11595
solidt 11604
spranim 9431
spriteclip 4119
sprites 6791
stattest 7563
stattest 7572
stdlib 6657
stest2 3658
strtest 1354
text_palette 5038
timedir 5468
w0page 8831
w3bgfx 5243
w3bgfx 5257
w3big 3673
w3huge 3700
w3probe 3556
1 # Эталон размеров _CODE (байт); обновление: python3 toolchain/size_check.py --update # Эталон размеров _CODE (байт); обновление: make size-baseline
2 accfill
3 accop
4 argv
14 bios_text
15 blitperf
16 blitw
17 cat
18 cblstream
19 cbltest
20 cblwav
21 conio
22 conio2
23 convbench
24 dec_test
25 errno
26 fbench
27 fdmax
28 filetest
29 fpsdiv
30 gets
31 gfx_dbuf
40 malloc
41 mem_test
42 mouse
43 openenv
44 pageflip
45 palfile
46 ptime
47 rt_test
48 scroll
49 seek
50 simple
51 solidt
52 spranim
53 spriteclip
54 sprites
55 stattest
56 stdlib
57 stest2
58 strtest
59 text_palette
60 timedir
61 w0page
62 w3bgfx
63 w3big
64 w3huge
65 w3probe
+68 -21
View File
@@ -4,14 +4,18 @@ size_check.py — размерный регресс-тест: сравнение
docs/size_baseline.tsv.
Использование:
python3 toolchain/size_check.py # сверка с эталоном
python3 toolchain/size_check.py --update # перезаписать эталон
make size-check # собрать обязательные тесты и сверить с эталоном
make size-baseline # принять свежие размеры, сохранив необязательные
Логика: собираем _CODE из .map каждой программы под tests/, сравниваем с
эталоном. Любое отличие печатается; выход 1 если есть РОСТ (случайное
Логика: собираем _CODE из свежей .map каждой программы под tests/, сравниваем с
эталоном. Карты, оставшиеся от другой цели или более старые, чем входы
сборки, пропускаются. Любое отличие печатается; выход 1 если есть РОСТ (случайное
разжирение libc), 0 если только уменьшения/без изменений. Новые
программы (нет в эталоне) предупреждение, добавить через --update.
Запускать после `make all`.
--update заменяет размеры собранных тестов, сохраняя эталоны необязательных
тестов, которые сейчас не собирались.
Прямой запуск скрипта рассчитан на уже собранные тесты; Make-цели сами
собирают обязательный набор и передают его через --require.
ТОЛЬКО tests/, без отдельного репозитория Examples: регресс ловит разжирение libc, а для этого
хватает мелких тестов каждый тянет свой кусок библиотеки и пересобирается
@@ -20,18 +24,20 @@ docs/size_baseline.tsv.
`make all` они убраны (см. корневой Makefile).
"""
import argparse
import glob
import os
from pathlib import Path
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."""
"""Имя программы → размер _CODE (байт) по свежим .map своей цели."""
sizes = {}
skipped = set()
pats = [
os.path.join(ROOT, "tests", "*", ".sprinter-cc-*", "*.map"),
]
@@ -41,13 +47,43 @@ def collect():
# .map должен лежать в своей папке .sprinter-cc-<name>
if os.path.basename(os.path.dirname(map_path)) != ".sprinter-cc-" + name:
continue
project = Path(map_path).parents[1]
makefile = project / "Makefile"
if not makefile.is_file():
continue
declared = re.search(r"^EXAMPLE\s*[:?+]?=\s*(\S+)",
makefile.read_text(), re.M)
if not declared or declared.group(1) != name:
# Например, старый .sprinter-cc-w3bankgfx рядом с w3bgfx.
continue
flags = makefile.read_text()
safe = "--safe" in flags
gfx = "--gfx" in flags
variant = "_safe" if safe else ""
# app.mk сам по себе не является входом линкера: его изменения
# учитываются сборкой через sdbg-config и CC_FLAGS. Проверяем
# только реальные входы, иначе правка run-цели объявит все карты
# устаревшими, хотя размер программы не менялся.
dependencies = [makefile, Path(ROOT) / "bin/sprinter-cc",
Path(ROOT) / "toolchain/mkexe/mkexe",
Path(ROOT) / f"lib/sprinter{variant}.lib"]
if gfx:
dependencies.append(Path(ROOT) / f"lib/bgi256{variant}.lib")
dependencies.extend(project.glob("*.c"))
dependencies.extend(project.glob("*.h"))
dependencies.extend((Path(ROOT) / "runtime").glob("*.s"))
if any(not path.is_file() or
os.path.getmtime(map_path) + 1 < path.stat().st_mtime
for path in dependencies):
skipped.add(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
return sizes, skipped
def load_baseline():
@@ -64,23 +100,33 @@ def load_baseline():
def save_baseline(sizes):
accepted = load_baseline()
accepted.update(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")
"make size-baseline\n")
for name in sorted(accepted):
f.write(f"{name}\t{accepted[name]}\n")
def main():
update = "--update" in sys.argv
sizes = collect()
parser = argparse.ArgumentParser(description="Проверка размера _CODE SDK-тестов")
parser.add_argument("--update", action="store_true")
parser.add_argument("--require", nargs="*", default=[], metavar="TEST")
args = parser.parse_args()
sizes, skipped = collect()
missing = set(args.require) - sizes.keys()
if missing:
print("size_check: нет свежей карты для обязательных тестов: " +
", ".join(sorted(missing)))
return 2
if not sizes:
print("size_check: .map не найдены — сначала `make all`")
print("size_check: свежие .map не найдены — сначала `make all`")
return 2
if update:
if args.update:
save_baseline(sizes)
print(f"size_check: эталон обновлён ({len(sizes)} программ) → "
print(f"size_check: эталон обновлён ({len(sizes)} свежих программ) → "
f"{os.path.relpath(BASELINE, ROOT)}")
return 0
@@ -90,17 +136,18 @@ def main():
return 2
grew = 0
for name in sorted(set(sizes) | set(base)):
for name in sorted(sizes):
new, old = sizes.get(name), base.get(name)
if new is None:
print(f" {name}: исчез из сборки (был {old} Б)")
elif old is None:
if 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 skipped:
print(f"size_check: {len(skipped)} устаревших карт пропущено: " +
", ".join(sorted(skipped)))
if grew:
print(f"size_check: {grew} программ(ы) ВЫРОСЛИ — проверить правки libc")
return 1
@@ -109,4 +156,4 @@ def main():
if __name__ == "__main__":
sys.exit(main())
raise SystemExit(main())