Проверить 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 $(PYTHON) -m unittest discover -s tests/sdbg -v
# Размерный регресс: сверить _CODE всех программ с docs/size_baseline.tsv. # Размерный регресс: сверить _CODE всех программ с docs/size_baseline.tsv.
size-check: size-check: tests
$(PYTHON) toolchain/size_check.py $(PYTHON) toolchain/size_check.py --require $(TESTS)
# Принять текущие размеры как эталон (после осознанных изменений). # Принять текущие размеры как эталон (после осознанных изменений).
size-baseline: size-baseline: tests
$(PYTHON) toolchain/size_check.py --update $(PYTHON) toolchain/size_check.py --update --require $(TESTS)
clean: clean:
$(MAKE) -C toolchain/mkexe clean $(MAKE) -C toolchain/mkexe clean
+20 -10
View File
@@ -249,14 +249,14 @@ debug packages, носители, снимки, внешние источник
их документы и историю. Критерий: каждый продукт собирается и проходит их документы и историю. Критерий: каждый продукт собирается и проходит
доступные host/MAME проверки из изолированного клона без Examples и доступные host/MAME проверки из изолированного клона без Examples и
других приложений. других приложений.
6. **Реализовано; остаётся ручная проверка установленного VSIX после разделения — выделить extension.** Научить VS Code находить SDK DAP независимо от 6. **Выполнено — выделить extension.** Научить VS Code находить SDK DAP независимо от
workspace, запускать build/run/debug приложения и показывать ошибки workspace, запускать build/run/debug приложения и показывать ошибки
разрешения путей/версий. Проверить VSIX в отдельном workspace SprPoP разрешения путей/версий. Проверить VSIX в отдельном workspace SprPoP
или Volkov, а не только в `C-Compiler`. Критерий: F5 строит debug package, или Volkov, а не только в `C-Compiler`. Критерий: F5 строит debug package,
ждёт DSS, доходит до `main`, принимает breakpoint/logpoint и клавиатуру; ждёт DSS, доходит до `main`, принимает breakpoint/logpoint и клавиатуру;
опция родного окна MAME работает на macOS. Windows остаётся явно опция родного окна MAME работает на macOS. Windows остаётся явно
ограниченной до отдельной реализации host transport. ограниченной до отдельной реализации host transport.
7. **Выполнено с открытым размерным baseline — очистить SDK и документы.** Заменить корневые цели, release-скрипт, 7. **Выполнено — очистить SDK и документы.** Заменить корневые цели, release-скрипт,
README, `AGENTS.md`, инструкции автотеста/отладки и рабочие ссылки. README, `AGENTS.md`, инструкции автотеста/отладки и рабочие ссылки.
Проверить `make`, `make -C libc`, `make -C libbgi`, `make size-check`, Проверить `make`, `make -C libc`, `make -C libbgi`, `make size-check`,
SDK host/sdbg tests и отдельные проекты. Критерий: в SDK нет tracked SDK host/sdbg tests и отдельные проекты. Критерий: в SDK нет tracked
@@ -276,15 +276,25 @@ SprPoP, Volkov и PoP-Archive PoC. Живые последовательные D
Extension воспроизводимо упакован через `npm ci && npm run package` и VSIX Extension воспроизводимо упакован через `npm ci && npm run package` и VSIX
версии 0.2.0 установлен в локальный VS Code. Рецепт и `package-lock.json` версии 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 старых Эталон `make size-check` разобран и обновлён. Десять тестов, использующих
увеличений (обычно +9 байт, `openenv` +188) и несколько исчезнувших прежних `open()`, выросли на 9 байт из-за исправления таблицы режимов ESTEX после
тестов. Реорганизация не меняла libc/libbgi, поэтому эталон автоматически не предыдущего эталона (ревизия `879f2ba`). `openenv` вырос на 188 байт: те же
перезаписывался; расхождения нужно разобрать отдельно. После разделения ещё 9 байт и новый тест записи через `O_RDONLY`. Для `w3bgfx` прежняя запись
нужен ручной F5-прогон установленного VSIX в чистом workspace. Несколько 5243 байта была несвежей: пересборка в отдельном worktree самой ревизии
одновременных MAME, маршрутизация MCP между ними и Windows transport остаются `879f2ba` тем же SDCC дала 5257 байт, как и текущая сборка. Других
явно непроверенными сценариями. изменений размеров свежих карт нет. Теперь `size-check` сначала собирает
обязательные SDK-тесты, требует их свежие карты и пропускает устаревшие
артефакты необязательных тестов; `size-baseline` сохраняет их прежние
значения, пока эти тесты не собраны заново.
Несколько одновременных MAME, маршрутизация MCP между ними и Windows
transport остаются явно непроверенными сценариями.
Каждый этап заканчивается проверкой в отдельном репозитории и просмотром Git Каждый этап заканчивается проверкой в отдельном репозитории и просмотром Git
diff. Новые Git remote для выделенных репозиториев и публикация их релизов diff. Новые Git remote для выделенных репозиториев и публикация их релизов
+13 -13
View File
@@ -1,4 +1,4 @@
# Эталон размеров _CODE (байт); обновление: python3 toolchain/size_check.py --update # Эталон размеров _CODE (байт); обновление: make size-baseline
accfill 3797 accfill 3797
accop 7067 accop 7067
argv 3445 argv 3445
@@ -14,18 +14,18 @@ bgitest 3762
bios_text 4475 bios_text 4475
blitperf 5860 blitperf 5860
blitw 4939 blitw 4939
cat 927 cat 936
cblstream 5944 cblstream 5953
cbltest 6090 cbltest 6090
cblwav 6183 cblwav 6192
conio 4619 conio 4619
conio2 3943 conio2 3943
convbench 3508 convbench 3508
dec_test 874 dec_test 874
errno 5980 errno 5989
fbench 8149 fbench 8158
fdmax 6087 fdmax 6096
filetest 10611 filetest 10620
fpsdiv 4870 fpsdiv 4870
gets 523 gets 523
gfx_dbuf 5207 gfx_dbuf 5207
@@ -40,26 +40,26 @@ ls 4849
malloc 4475 malloc 4475
mem_test 4569 mem_test 4569
mouse 4396 mouse 4396
openenv 6138 openenv 6326
pageflip 6334 pageflip 6334
palfile 5406 palfile 5406
ptime 5758 ptime 5758
rt_test 5216 rt_test 5216
scroll 6744 scroll 6744
seek 4202 seek 4211
simple 969 simple 969
solidt 11595 solidt 11604
spranim 9431 spranim 9431
spriteclip 4119 spriteclip 4119
sprites 6791 sprites 6791
stattest 7563 stattest 7572
stdlib 6657 stdlib 6657
stest2 3658 stest2 3658
strtest 1354 strtest 1354
text_palette 5038 text_palette 5038
timedir 5468 timedir 5468
w0page 8831 w0page 8831
w3bgfx 5243 w3bgfx 5257
w3big 3673 w3big 3673
w3huge 3700 w3huge 3700
w3probe 3556 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. docs/size_baseline.tsv.
Использование: Использование:
python3 toolchain/size_check.py # сверка с эталоном make size-check # собрать обязательные тесты и сверить с эталоном
python3 toolchain/size_check.py --update # перезаписать эталон make size-baseline # принять свежие размеры, сохранив необязательные
Логика: собираем _CODE из .map каждой программы под tests/, сравниваем с Логика: собираем _CODE из свежей .map каждой программы под tests/, сравниваем с
эталоном. Любое отличие печатается; выход 1 — если есть РОСТ (случайное эталоном. Карты, оставшиеся от другой цели или более старые, чем входы
сборки, пропускаются. Любое отличие печатается; выход 1 — если есть РОСТ (случайное
разжирение libc), 0 — если только уменьшения/без изменений. Новые разжирение libc), 0 — если только уменьшения/без изменений. Новые
программы (нет в эталоне) — предупреждение, добавить через --update. программы (нет в эталоне) — предупреждение, добавить через --update.
Запускать после `make all`. --update заменяет размеры собранных тестов, сохраняя эталоны необязательных
тестов, которые сейчас не собирались.
Прямой запуск скрипта рассчитан на уже собранные тесты; Make-цели сами
собирают обязательный набор и передают его через --require.
ТОЛЬКО tests/, без отдельного репозитория Examples: регресс ловит разжирение libc, а для этого ТОЛЬКО tests/, без отдельного репозитория Examples: регресс ловит разжирение libc, а для этого
хватает мелких тестов — каждый тянет свой кусок библиотеки и пересобирается хватает мелких тестов — каждый тянет свой кусок библиотеки и пересобирается
@@ -20,18 +24,20 @@ docs/size_baseline.tsv.
`make all` они убраны (см. корневой Makefile). `make all` они убраны (см. корневой Makefile).
""" """
import argparse
import glob import glob
import os import os
from pathlib import Path
import re import re
import sys
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
BASELINE = os.path.join(ROOT, "docs", "size_baseline.tsv") BASELINE = os.path.join(ROOT, "docs", "size_baseline.tsv")
def collect(): def collect():
"""Имя программы → размер _CODE (байт) по свежим .map.""" """Имя программы → размер _CODE (байт) по свежим .map своей цели."""
sizes = {} sizes = {}
skipped = set()
pats = [ pats = [
os.path.join(ROOT, "tests", "*", ".sprinter-cc-*", "*.map"), os.path.join(ROOT, "tests", "*", ".sprinter-cc-*", "*.map"),
] ]
@@ -41,13 +47,43 @@ def collect():
# .map должен лежать в своей папке .sprinter-cc-<name> # .map должен лежать в своей папке .sprinter-cc-<name>
if os.path.basename(os.path.dirname(map_path)) != ".sprinter-cc-" + name: if os.path.basename(os.path.dirname(map_path)) != ".sprinter-cc-" + name:
continue 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: with open(map_path, encoding="utf-8", errors="replace") as f:
for line in f: for line in f:
m = re.match(r"^_CODE\s+([0-9A-Fa-f]+)\s+([0-9A-Fa-f]+)", line) m = re.match(r"^_CODE\s+([0-9A-Fa-f]+)\s+([0-9A-Fa-f]+)", line)
if m: if m:
sizes[name] = int(m.group(2), 16) sizes[name] = int(m.group(2), 16)
break break
return sizes return sizes, skipped
def load_baseline(): def load_baseline():
@@ -64,23 +100,33 @@ def load_baseline():
def save_baseline(sizes): def save_baseline(sizes):
accepted = load_baseline()
accepted.update(sizes)
with open(BASELINE, "w") as f: with open(BASELINE, "w") as f:
f.write("# Эталон размеров _CODE (байт); обновление: " f.write("# Эталон размеров _CODE (байт); обновление: "
"python3 toolchain/size_check.py --update\n") "make size-baseline\n")
for name in sorted(sizes): for name in sorted(accepted):
f.write(f"{name}\t{sizes[name]}\n") f.write(f"{name}\t{accepted[name]}\n")
def main(): def main():
update = "--update" in sys.argv parser = argparse.ArgumentParser(description="Проверка размера _CODE SDK-тестов")
sizes = collect() 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: if not sizes:
print("size_check: .map не найдены — сначала `make all`") print("size_check: свежие .map не найдены — сначала `make all`")
return 2 return 2
if update: if args.update:
save_baseline(sizes) save_baseline(sizes)
print(f"size_check: эталон обновлён ({len(sizes)} программ) → " print(f"size_check: эталон обновлён ({len(sizes)} свежих программ) → "
f"{os.path.relpath(BASELINE, ROOT)}") f"{os.path.relpath(BASELINE, ROOT)}")
return 0 return 0
@@ -90,17 +136,18 @@ def main():
return 2 return 2
grew = 0 grew = 0
for name in sorted(set(sizes) | set(base)): for name in sorted(sizes):
new, old = sizes.get(name), base.get(name) new, old = sizes.get(name), base.get(name)
if new is None: if old is None:
print(f" {name}: исчез из сборки (был {old} Б)")
elif old is None:
print(f" {name}: НОВЫЙ ({new} Б) — добавить в эталон (--update)") print(f" {name}: НОВЫЙ ({new} Б) — добавить в эталон (--update)")
elif new != old: elif new != old:
mark = "РОСТ" if new > old else "ок, меньше" mark = "РОСТ" if new > old else "ок, меньше"
print(f" {name}: {old} -> {new} ({new - old:+d} Б) [{mark}]") print(f" {name}: {old} -> {new} ({new - old:+d} Б) [{mark}]")
if new > old: if new > old:
grew += 1 grew += 1
if skipped:
print(f"size_check: {len(skipped)} устаревших карт пропущено: " +
", ".join(sorted(skipped)))
if grew: if grew:
print(f"size_check: {grew} программ(ы) ВЫРОСЛИ — проверить правки libc") print(f"size_check: {grew} программ(ы) ВЫРОСЛИ — проверить правки libc")
return 1 return 1
@@ -109,4 +156,4 @@ def main():
if __name__ == "__main__": if __name__ == "__main__":
sys.exit(main()) raise SystemExit(main())