DevOps для firmware

Часто слышал мнение, что в embedded программировании в принципе не может быть никакого DevOps (а).  Якобы вот есть GUI (ня) в IAR и там надо много мышкой водить. «Ты же не будешь ставить шаговые двигатели для сдвигания мышки» и т. п.

В этом тексте я намерен пофантазировать каким мог бы быть абстрактный процесс разработки firmware с точки зрения DevOps. И перечислить атрибуты такого процесса.

1. Репозиторий с кодом (репа)

Это может быть. Git, SVN, Mercurial, ClearCase, Perforce. Репозиторий нужен не только для хранения, распространения кодовой базы среди разработчиков, но и в случае поломки сборок репозиторий позволит откатиться в истории на прежние версии кода, когда все было относительно хорошо. Можно восстановить случайно удаленные файлы. Репозиторий позволяет распределить работу среди нескольких вкладчиков. Контроль версий запоминает все шаги. С репозиторием намного спокойнее работать.  

Плохие примеры когда код в компании передают через DropBox, USB-Flash (ку) или архивом в электронном письме. В этом случае нет никаких гарантий, что у каждого разработчика та же версия, что у остальных.

2. Проект должен собираться из скриптов

Многие IDE (IAR, Code Composer Studio) могут запускать сборки из командной строки Windows. Запускать сборки со скриптов полезно еще и по той причине, что окна GUI (ни) в IDE IAR часто зависают. А в окно лога сборки в CCS не помещается весь текст 6 минут работы компилятора и теряются сообщения о критических предупреждениях. Если вы собираете проект из скриптов, то у вас останется log файл  компилятора и вы сможете проанализировать все сообщения.

3. Сборок должно быть много

Чтобы создать хорошую модульную базу с полной изоляцией компонентов надо собирать проект по частям. Например если это IoT устройство то надо подготовить сборку только с GNSS, сборку только с LTE модемом. Сборку для проверки качества пайки платы. Загрузчик, Приложение. Сборка с тестами. Также полезно сделать сборку для какого-н другого микроконтроллера (ESP32, STM, TI). Это даст гарантию, что код переносимый. Если каждая сборка собирается без ошибок, то это значит, что код достаточно модульный.

4. Проект собирать из Make файлов

Если Make файлы являются для вас исходниками, то вы можете делать супер модульный код. Буквально одной строчкой в make добавлять и исключать компоненты из сборок. Если же вы привыкли пользоваться IDE, то вам для добавления одного компонента придется в одной вкладке IDE добавить пути к include (ам) в другом окне добывать переменные препроцессора, в третьем окне IDE добавлять сами исходники и так для всех 55 сборок. И это все мышковозня. 

В Make это будет 1 строка. IDE хороши для прототипирования. В промышленном же подходе надо готовить Make файлы.

5. Статические анализаторы

После компиляции код следует подвергать статическому анализу. Можно воспользоваться бесплатным CppCheck. Это позволит выявить и исправить еще некоторые критические ошибки.

6. Автосборки 

Можно клонировать код из репозитория и выяснить, что он уже как полгода не собирается и никто об этом не догадывался так как код собирался только локально на DeskTop (е) разработчика. Забыли подвергнуть версионному контролю какие-то файлики.

Кодовая база в репозитории должна быть валидной каждый день. То есть сборки должны собираться без ошибок и без предупреждений компилятора из кода, что в репозитории. Кто за этим будет следить? Когда есть репозиторий и скрипты сборки можно автоматизировать запуск сборок при помощи бесплатной программы Jenkins. Это утилита с Web интерфейсом, которая запускает скрипты. Когда работает Jenkins, то можно не только удостовериться, что код синтаксически правильный, но и всегда каждый день брать свежие артефакты. Там есть удобная навигация и сортировка по категориям Job (ов). Можно даже открыть доступ Jenkins (у) клиентам продукта и они не будут беспокоить разработчиков вопросами, где же нам взять прошивку. Клиенты сами смогут взять ту прошивку, которая им нужна из Jenkins.

7. Сервер сборки

Настроить Jenkins это весьма кропотливая работа и много мышковозни. Запускать CI на каждом компьютере каждого разработчика это еще и бессмысленная повторная работа. Плюс нагрузка LapTop (а), лишний шум во время работы от кулера. Но это достаточно сделать 1 раз для всех разработчиков. Нужен какой-то общий компьютер. Можно задействовать дешевенький Win (довый) NetTop PC (Зомбик). Запустить на нем Jenkins и оставить работать 24/7. За ночь он соберет все сборки. Когда кому-то понадобился артефакт, то он подключается по Win Remote Desktop Connection или TeamViewer к зомбику и скачивает себе артефакт, посмотрит все ли в порядке с остальными сборками и в случае поломки сделает коммиты, чтобы починить код.

8. Модульные тесты

То что код собирается в репе это еще ни о чем не говорит. Код может собираться без единого предупреждения, а при загрузке на target плата будет бесконечно Reset (тся). Код должен корректно стартовать и исполняться. Как это проверить автоматически? Это можно сделать при помощи модульных тестов. Модульные тесты это просто функции, которые запускают другие функции и проверяют output. Должна быть сборка для тестирования программы изнутри (методом белого ящика). В идеале надо тестировать все нетривиальные функции. В каждом firmware проекте есть hardware зависимый  код и hardware независимый код (математика, строки, абстрактные структуры данных). Часть кода, который не зависит от железа можно собирать, запускать и тестировать прямо на x86–64. Памяти на PC много  и все тесты поместятся. В Jenkins должен быть отдельный Job для запуска тестов на PC.

9. Hardware In The Loop (HIL) стенд 

Остаются аппаратно-зависимые тесты. Их надо запускать и выполнять прямо на target (е). Чтобы это автоматизировать надо минимум 3 вещи. Устройство, загрузчик и CLI. Соединив Target c NetTop компьютером по UART получится HIL стенд. Загрузчик позволит автоматически обновлять прошивку по тому же UART. CLI позволит подключиться к target по UART и запустить тесты, вычитать лог и сохранить отчет. Все это можно упаковать в отдельные Job (ы) на Jenkins. 

3ecbfb03c176e86284cb25b87cdbd301.png

10. Code Coverage (advanсed)

Как понять, что составлено достаточно модульных тестов? Может случится, что какие-то строки протестированы 100 раз, а другие ни разу. Ответить на этот вопрос можно только если как-то считать по каким строкам код прошил во время тестов, а какой код является недостижимым. Для этого есть специальные проприетарные Tool (ы) например Testwell CTC++.  

Вывод

При налаженном DevOps можно организовать полностью удаленную работу даже для процесса разработки firmware. Разработчику достаточно делать коммиты в репозиторий и анализировать Job (ы) Jenkins (а) и изредка подключаться к HIL стенду. В теории не обязательно даже локально устанавливать ToolChain, ничего кроме своего любимого текстового редактора и браузера. Так 1 человек может контролировать до 20 сборок. Надеюсь этот текст поможет кому-нибудь автоматизировать свои проекты. Если есть дополнения, то предлагаю обсудить их в комментариях.

© Habrahabr.ru