Добавить неизменяемое окно firmware-info v1 0x1200 из 12 слов для FC03 и служебного CAN, запретить запись. Сохранить текущую технологическую карту и логгер v4 с записью 12 слов. Подключить общий генератор версии 1.0.0 для Balsam-166-PM35, prebuild CCS и локальный выпуск BIN с manifest и SHA-256. Проверять соответствие встроенных слов манифесту до публикации; старый OUT без паспорта отклонять. Проверка: реальные обработчики RS/CAN и CRC под GCC O3, прежний тест логгера, анализатор установленного SetGUI, полная сборка TI C2000 22.6.0.LTS. BIN 30248 байт при границе параметров 32508 байт. Аппаратная проверка требует установки нового образа и перезапуска платы.
36 lines
5.7 KiB
Python
36 lines
5.7 KiB
Python
"""Подготавливает паспорт 166 тем же генератором firmware-info, что используется в 167.""" # Версия, время UTC и ревизии фиксируются до компиляции.
|
||
import argparse # Разбирает одинаковые с проектом 167 пути prebuild.
|
||
import importlib.util # Подключает общую реализацию без копирования генератора в прошивку.
|
||
import json # Объединяет идентификаторы продукта с версией из C-заголовка.
|
||
from pathlib import Path # Нормализует пути независимо от рабочего каталога CCS.
|
||
import re # Проверяет единственное допустимое целое определение каждого компонента версии.
|
||
import tempfile # Хранит объединённые настройки только на время вызова общего генератора.
|
||
def read_version(header): # Сохраняет правила исходного адаптера 167 и ограничения firmware-info v1.
|
||
text = re.sub(r"/\*.*?\*/|//[^\n]*", "", header.read_text(encoding="utf-8-sig"), flags=re.S) # Не принимает комментарии или примеры за действующую версию.
|
||
result = [] # Накапливает major, minor и patch в порядке регистров контракта.
|
||
for part, limit in (("MAJOR", 255), ("MINOR", 255), ("PATCH", 999)): # Два первых компонента ограничены октетом, исправление допускает 0..999.
|
||
name = "FIRMWARE_VERSION_" + part # Использует тот же интерфейс заголовка версии, что 167.
|
||
definitions = re.findall(r"^\s*#\s*define\s+" + name + r"\b([^\n]*)", text, re.M) # Требует единственного определения без условных альтернатив.
|
||
if len(definitions) != 1 or not re.fullmatch(r"(?:0|[1-9][0-9]*)[uU]?", definitions[0].strip()): # Не допускает выражений, шестнадцатеричных литералов и неоднозначной версии.
|
||
raise ValueError(f"{header}: define {name} exactly once as a decimal integer") # Останавливает сборку вместо выпуска ложного паспорта.
|
||
value = int(definitions[0].strip().rstrip("uU")) # Суффикс C не является частью десятичного числа Python.
|
||
if value > limit: # Проверяет границы до записи какого-либо заголовка сборки.
|
||
raise ValueError(f"{name} must be in 0..{limit}") # Сохраняет ограничения отображения и Modbus-контракта 167.
|
||
result.append(value) # Записывает проверенную компоненту в правильное поле паспорта.
|
||
return result # Номер версии формируется только из firmware_version.h.
|
||
def prepare(project, templates, output): # Генерирует заголовок и JSON для одной конкретной конфигурации CCS.
|
||
settings = json.loads((project / "firmware_identity.json").read_text(encoding="utf-8-sig")) # Сохраняет собственный product 166 и тот же target TMS320F28335.
|
||
settings["version"] = read_version(project / "firmware_version.h") # Не дублирует SemVer в двух редактируемых файлах.
|
||
spec = importlib.util.spec_from_file_location("shared_firmware_identity", templates / "c/firmware-info/tools/identity.py") # Берёт проверенную общую реализацию из той же зависимости, что 167.
|
||
identity = importlib.util.module_from_spec(spec) # Создаёт изолированный модуль генератора без изменения системного Python.
|
||
spec.loader.exec_module(identity) # Загружает формат заголовка, UTC и правила формирования Build ID.
|
||
with tempfile.TemporaryDirectory(prefix="balsam166-identity-") as directory: # Удаляет только временные объединённые настройки этого вызова.
|
||
config = Path(directory) / "settings.json" # Настройки не записываются в исходники и не попадают в историю Git.
|
||
config.write_text(json.dumps(settings), encoding="utf-8") # Передаёт общему генератору проверенную версию и product.
|
||
return identity.prepare(project, templates, config, output) # Создаёт обязательные generated/firmware_identity.h и JSON до компиляции.
|
||
if __name__ == "__main__": # Импорт из теста не запускает изменение файлов сборки.
|
||
parser = argparse.ArgumentParser(description=__doc__) # Объясняет назначение prebuild при ручном вызове.
|
||
for name in ("project", "templates", "output"): parser.add_argument("--" + name, type=Path, required=True) # Совместим с параметрами prebuild исходного проекта 167.
|
||
args = parser.parse_args() # Проверяет обязательные каталоги перед генерацией.
|
||
print(json.dumps(prepare(args.project.resolve(), args.templates.resolve(), args.output.resolve()))) # Показывает фактическую идентичность текущей сборки.
|