Иногда бывает такая ситуация, что надо чтобы прошивка или любой другой артефакт сам сказал в каком состоянии был GIT репозиторий с кодовой базой, когда эту прошивку собирали. Надо как-то добавить в Flash память такие метаданные как "из какой ветки собрали прошивку", "какой была контрольная сумма последнего коммита".
Эта инфа окажется очень полезна, если в prod(акшене) найдут ошибку в коде и надо будет понять, когда прошивка была собрана и из чего.
Можно конечно вручную прописывать эти контрольные суммы в FlashFs, но это очень плохая идея.
Лучше написать *.bat скрипт sign_code.bat, который будет перед компиляцией подписывать прошивку. И добавить скрипт sign_code в скрипт основной сборки артефактов до вызова препроцессора. Вот так как показано на строчке 14.
cls
@echo off
set project_dir=%~dp0
echo project_dir=%project_dir%
cd %project_dir%
set workspace_dir=%project_dir%\..\..\..\
echo workspace_dir=%workspace_dir%
If exist "*.exe" (
Echo *.exe exist
del *.exe
)
call %workspace_dir%\tool\sign_code.bat %project_dir%
cd %project_dir%
make all
В папке с проектом надо создать файл auto_version.h. Это шаблон для метаданных про версию репозитория.
#ifndef AUTO_VERSION_H
#define AUTO_VERSION_H
#define GIT_BRANCH "GIT_BRANCH_AUTO_REPLACE"
#define GIT_LAST_COMMIT_HASH "GIT_LAST_COMMIT_HASH_AUTO_REPLACE"
#endif /*AUTO_VERSION_H*/
Надо чтобы скрипт sign_code на место строк GIT_BRANCH_AUTO_REPLACE и GIT_LAST_COMMIT_HASH_AUTO_REPLACE вставлял реальные значения, которые скажет утилита git (git branch, git rev-parse --short HEAD ).
После отработки скрипта sign_code получится валидный файл.
#ifndef AUTO_VERSION_H
#define AUTO_VERSION_H
#define GIT_BRANCH "main"
#define GIT_LAST_COMMIT_HASH "e53349f7"
#endif /*AUTO_VERSION_H*/
Вот полный код скрипта sign_code.bat
echo off
echo %0
set project_dir=%1
set workspace_dir=%project_dir%\..\..\..\
echo sing_firmware
echo ....
echo workspace_dir=%workspace_dir%
echo project_dir=%project_dir%
COPY /Y %workspace_dir%auto_version_pattern.h %project_dir%\auto_version.h
set tool_path=%workspace_dir%tool\
echo tool_path=%tool_path%
FOR /F "tokens=* delims=" %%a IN ('git branch') DO (
set branch_var=%%a
)
FOR /F "tokens=*delims=" %%a IN ('git rev-parse --short HEAD') DO (
set last_hash_var=%%a
)
rem echo branch_var=%branch_var%
set branch_var=%branch_var:~2%
echo branch_var=%branch_var%
echo last_hash_var=%last_hash_var%
python %tool_path%replace_word_in_file.py %project_dir%\auto_version.h GIT_BRANCH_AUTO_REPLACE %branch_var%
python %tool_path%replace_word_in_file.py %project_dir%\auto_version.h GIT_LAST_COMMIT_HASH_AUTO_REPLACE %last_hash_var%
Скрипт sign_code сначала вычисляет абсолютные адреса к утилитам, затирает предыдущие настройки версии репозитория в файле auto_version.h, берет нынешние настройки репозитория из консольных git команд и прописывает эти текущие настройки репозитория в файл auto_version.h
Как видно, внутри sign_code.bat фигурирует Python(ский) скрипт replace_word_in_file.py. Дело в том, что авто замена в файле реализуется на Python проще чем на Batch. У скрипта replace_word_in_file три аргумента: файл, нежелательный token, желательный token
#!/usr/bin/python
import sys
print ('Number of arguments:', len(sys.argv), 'arguments.')
print ('Argument List:', str(sys.argv))
arguments = len(sys.argv) - 1
position = 1
while (arguments >= position):
print ("Parameter %i: %s" % (position, sys.argv[position]))
position = position + 1
with open(sys.argv[1], 'r') as file :
filedata = file.read()
# Replace the target string
filedata = filedata.replace(sys.argv[2], sys.argv[3])
# Write the file out again
with open(sys.argv[1], 'w') as file:
file.write(filedata)
Когда прошивка подписалась, собралась и загрузилась, то в логе загрузки можно увидеть метаданные про репозиторий: branch, lastCommit. Успех!
Можно было бы и на С(ях) написать одну монолитную утилиту sign_code.exe, которая бы авто заменяла ключевые слова в соответствии с состоянием репозитория, однако тогда пришлось бы еще писать такую утилиту для каждой операционной системы, плюс писать makefile, набор исходников и поддерживать эту утилиту на плаву, или подвергать версионному контроль артефакт *.exe который может еще и не запуститься на другом PC. Потом появились бы временные файлы сборки (*.o) , которые бы засоряли жесткий диск.
Скрипты удобнее для простых задач.
Вывод
Как видите чтобы подписать прошивку потребовалось 55 строк кода. Подпись прошивок это простой и эффективный DevOps приём. Подпись потом поможет Вам в случае выявленного осечки откатиться назад, собрать прошивку с той кодовой базой, что была и понять причину в условиях максимально приближенных к реальности.
Подписывайте свои прошивки. В этом нет ничего сложного.
Если вы знаете как решить задачу инкапсуляции состояния репозитория в артефакт лаконичнее и не в ущерб понимания кода, то пишите в комментариях.
Контрольные вопросы:
— Зачем собирать артефакты из скриптов, если всегда можно мышкой щелкнуть на треугольник в GUI‑IDE?
Комментарии (9)
devprodest
25.04.2023 14:49+2Можно сделать проще:
Используем make
GIT_SHA := $(shell git rev-parse --short HEAD) # ... DEFINES += GIT_SHA=0x0$(GIT_SHA) # можно сделатьстроку если надо C_DEFS = $(addprefix -D,$(DEFINES)) # ...
Используем в коде
printf("FW version : %d.%d.%d (%08x)\n", FIRMWARE_VERSION_MAJOR, FIRMWARE_VERSION_MINOR, FIRMWARE_PATCH, GIT_SHA)
Для идентификации имя ветки избыточно, достаточно хэша комита
aabzel Автор
25.04.2023 14:49Гениально! 3 строчки.
GIT_SHA := $(shell git rev-parse --short HEAD) OPT += -DGIT_SHA=0x0$(GIT_SHA) ..... LOG_INFO(SYS,"GitSha: 0x%08x", GIT_SHA);
devprodest
25.04.2023 14:49+1Если вам не нужно числовое представление, то лучше сразу в строку загонять, будет еще лаконичнее
LOG_INFO(SYS,"GitSha: " GIT_SHA);
к тому же, без лишних вычислений.
aabzel Автор
25.04.2023 14:49-3Вот поэтому и надо пользоваться makefile(лами).
В makefile всё делается лаконично.
А GUI-IDEшкам такое и не снилось!
Sap_ru
А почему бы не править исходники хуками в момент добавления в репозиторий? Ну, например, автоматическую сквозную нумерацию версий добавить. Тогда исходные тексты будут содержать видимую человеком необходимую информацию, что очень хорошо и правильно.
А у вас сборка из архива без использования git как будет происходить? Вооот...
aabzel Автор
Дело в том что при отладке прошивок нам приходится собирать прошивки локально из WorkSpace (до загрузки в репу). Поэтому надо чтобы подпись отрабатывала локально.
Sap_ru
И кстати, исходники при сборке править не нужно - это вообще крайне дурная практика, так как у вас код не совпадает с эталоном и нет повторяемости сборки. Для этого есть опции линкера, позволяющие задавать значения символов через опции командной строки. Увлекательно, но зато правильно.
Ну, вот, чтобы всё работало хорошо и нужно один раз напрячься и прикрутить автоинкремент версии при добавлении в репозиторий. И установку флагов и дат при переносе в релизные и пререлизные ветки.
Для локальных неррелизный сборок никто не запрещяет использовать флаги версий и ревизий. И добавление даты сборки, если сильно хочется.
А у вас куча, судя по всему, оно ещё и криво всё сделано. У вас, что при сборке обновляется дерево исходников? Это плохо.
Если хочется вот такой велосипед, то вынесите версию (именно верснию) в отдельный файл в корне проекта, например. И прикрутите автоикремент при релизной сборке или комите в репозиторий. И дату фиксации исходников туда же. И флаги релизная/пререлизная/тестовая сборка. И номер билда туда же.
А сборку настройте так, чтобы версия бралась их этого файла, но при этом не обновляла файлы в дереве исходников (чтобы факт сборки не влиял на исходники). Желательно через ключи линкера - всё отлично уместиться в несколько 32-битных символов.
Вот тогда правильно будет. У вас будет удобная человеко-читаемая автоматически обноляемая информация о версии, и при этом сохранится повторяемость сборки. И не будет зависимости от репозитория. И при заливке в репозиторий в резлизные ветки, чтобы тэги по версии автоматов выставлялись. Вот это будет правильно и по-взрослому.
А сейчас у вас бардак и в версиях и в репозитории. Как узнать какая версия новее? Как узнать релизная ли это версия или тестовая? Что вообще даёт эта "подпись", если у вас под рукой нет репозитория? Что делать при сборке из другого репозитория? Что делать, если нужно собрать версию где-то в полях - как потом понять, что это вообще за версия? Что будет, если вам нужно будет по техническим причинам перенести репозиторий? А если нужно будет отрефакторить историю? А если нужно из архива собрать? А если нужно для заказчика сдать/зафиксировать версию? Весь репозиторий с историей будете ему в арахив пихать? Где повторяемость сборки бинарных артефактов? Как это потом можно сертифицировать в случае необходимости?
devprodest
Нечего в исходниках делать переменной информации.
А гит или что-то аналогичное использовать полезно ????
Sap_ru
Нечего версии продукта делать в исходниках? смело.