Часть 1, Часть 2

Давайте добавим Arduino Zero. Модифицируем часть программы, которая устанавливает ядро AVR, и добавляем другой код:

# Установка ядер Arduino
arduino-cli core install arduino:avr
arduino-cli core install arduino:samd

Давайте также поменяем код для компиляции наших скетчей:

# Компилируем все файлы с расширениями *.ino для Arduino Uno
for f in {,**/}*.ino ; do
    arduino-cli compile -b arduino:avr:uno $f
    arduino-cli compile -b arduino:samd:arduino_zero_native $f
done

Так вы автоматически проверите код на разных платформах и увидите, появятся ли ошибки.

Если добавить изменения и отправить их в репозиторий на GitHub, а потом перейти на вкладку Actions (Действия), то вы увидите свой новенький рабочий поток и его выходные данные:

При нажатии на название сборки вы увидите что-то подобное:

Если один из шагов закончится неудачно, вы можете нажать на его название и просмотреть подробную историю лога. Также будет полезно добавить дополнительный вывод скриптам оболочки, чтобы лучше понимать, что выполняется.

Автоматизированные сборки PlatformIO

Скрипт выше будет работать и для скетчей, и для библиотек Arduino со скетчами-образцами. Он не подходит для проектов PlatformIO. Для проектов такого типа рекомендуется создавать автоматизированные сборки с помощью интерфейса командной строки PlatformIO вместо аналога в Arduino.

С таким подходом вы сделаете непрерывную интеграцию для любого типа проектов из PlatformIO, а не только для проектов из Arduino. И это его самое большое преимущество. В целом этапы схожи. Добавляем шаг в build.yml:

…
  steps:
    - name: Checkout
      uses: actions/checkout@v2

- name: Build on PlatformIO
      run: bash ci/build-platformio.sh

Теперь добавляем скрипт оболочки build-platformio.sh:

MyPlatformIOProject
├── .github
│   └── workflows
│       └── build.yml
├── ci
│   └── build-platformio.sh
…

А дальше следуем инструкции по установке PlatformIO:

#!/bin/bash

# Немедленный выход, если у команды не нулевой статус
set -e

# Переход в рабочее пространство github
cd $GITHUB_WORKSPACE

# Устанавливаем PlatformIO CLI
export PATH=$PATH:~/.platformio/penv/bin
curl -fsSL https://raw.githubusercontent.com/platformio/platformio-core-installer/master/get-platformio.py -o get-platformio.py
python3 get-platformio.py

Теперь устанавливаем платформы для тестирования:

# Устанавливаем платформу Atmel AVR
pio platform install "atmelavr"

Если вы корректно настроили platformio.ini, вам осталось пройти всего один шаг:

# Компилируем проект
pio run

Как вариант — можно напрямую пойти в окружения, определённые в platformio.ini:

# Компилируем проект для Uno
pio run -e uno

Вот и все. Процесс оказался ещё проще, чем при работе с Arduino CLI.

Укрепляем красивый стиль форматирования кода

Инструмент clang-format (CF) помогает проверять код на соответствие определенным правилам стиля форматирования в программах на C или C++.

Стилей программирования много — выбирайте тот, что вам ближе: LLVM, GNU, Google, Mozilla или Microsoft. Вы также можете настроить инструмент проверки CF, чтобы писать код в одном стиле. Просто выберите любой из них, если не можете решить. Наличие одного приоритетного стиля важнее, чем поиск идеального. Всегда можно изменить или переопределить его позже, а всю базу кода просто переформатировать.

Если вы работаете в VSC, вы можете добавить расширение, чтобы форматировать код при каждом сохранении файла. Тогда у вас больше не будет повода волноваться об этом. Все будет автоматизировано. Для других редакторов доступны похожие плагины. Я предлагаю добавлять в проект файл .clang-format:

MyArduinoLibrary
├── .clang-format
…

Выберите основной стиль и конфигурируйте его в файле вместе с вашими конфигурационными опциями. Вот как выглядит мой .clang-format:

BasedOnStyle: Google

Language: Cpp

IndentWidth: 4
AlignConsecutiveMacros: true

Не стесняйтесь изучать остальные возможности clang-format. У этого инструмента есть что предложить.

После добавления CF в проект вы также можете интегрировать его в свой конвейер сборок, чтобы проверить правильность форматирования в базе кода.

Чтобы этого достичь, создаём новый шаг Check clang-format conformity в файле build.yml. Предлагаю добавить его прямо после шага проверки (Checkout):

…
  steps:
    - name: Checkout
      uses: actions/checkout@v2

- name: Check clang-format conformity
      run: bash ci/clang-lint.sh

- name: Build on Arduino CLI
      run: bash ci/build-arduino.sh

Так запускается скрипт оболочки clang-lint.sh. Добавляем в файл несколько строчек для установки clang-format:

#!/bin/bash

# Немедленный выход, если у команды не нулевой статус
set -e

# Включаем опцию оболочки globstar
shopt -s globstar

# Переходим в рабочее пространство github
cd $GITHUB_WORKSPACE

# Устанавливаем clang-format
sudo apt-get -y install clang-format-10

Теперь делаем цикл по всем исходным файлам с кодом. Проверяем, правильно ли отформатировано их содержимое:

# Проверка вывода clang-format
for f in {,**/}*.{h,c,hpp,cpp,ino} ; do
    if [ -f "$f" ]; then
        diff $f <(clang-format -assume-filename=main.cpp $f) 1>&2
    fi
done

Здесь есть одна главная строчка:

diff $f <(clang-format -assume-filename=main.cpp $f) 1>&2

Она запускает работу CF над файлом и сравнивает вывод с текущим содержимым файла. Если они одинаковые, значит файл отформатирован правильно. Если они отличаются, значит цель ещё не достигнута.

Вывод команды diff перенаправляется в stderr, а это значит, что любой вывод считается ошибочным. Если файл отформатирован правильно, у diff не будет вывода и не будет ошибок. Если вывод сборки будет неудачным, то вы увидите неверно форматированные строчки кода.

Очень рекомендую добавлять какой-либо вывод прямо после команды diff, чтобы лучше понимать, какие файлы проверяются. Например, вот так:

echo "Checking file ${f}"
diff $f <(clang-format -assume-filename=main.cpp $f) 1>&2

Если вы знаете как обойти скрипты оболочки, то на выходе конвейера сборок вы получите кое-что интересное:

Мысли напоследок

Мы убедились, что начать пользоваться непрерывной интеграцией для своих собственных проектов довольно просто. А после выполнения базовой настройки вы сможете перенастраивать и переиспользовать этот инструмент еще и в других своих проектах.

Если к конвейеру автоматизированных сборок добавить юнит-тесты, он станет ещё более мощным дополнением при работе над программами. Интеграция и того, и другого в ваш поток — это всего одна команда в скрипте: pio test.

Вам решать, будет ли непрерывная интеграция улучшать ваш конкретный случай. Я советую серьезно рассмотреть этот вопрос, особенно когда вы будете делиться своим кодом и работать над проектом совместно с другими.

Читайте также:

Читайте нас в телеграмме, vk и Яндекс.Дзен


Перевод статьиRaphael Stäbler: How to Create an Automated Build Pipeline for Your Arduino Project

Предыдущая статьяРаспознаём 50 видов текста на C++ с Plywood
Следующая статьяТестирование клиент-серверов на Rust для IoT