みんなで使える組み込み開発環境 Zephyr RTOS編

この記事はAkerun Advent Calendar 6日目の記事です。

はじめまして、2月にフォトシンスに入社した いとう です。 Akerun入退室管理システムの中のハードウェアを動作させるファームウェアという奴をぼちぼち開発しています。

IoT機器を開発しているフォトシンスでは Zephyr RTOS をやっていこう!という話になり、今回はその開発環境構築の取り組みについて書いてみようと思います。あんまりZephyr本体の話は出てきません。

TL;DR

  • 開発環境はコンテナだよ
  • GitLabでDockerfile管理してGitLab CIでイメージ生成してGitLab Container Registry でイメージ管理するよ
  • VSCode の Remote Development 使えばシャッキリポン

組み込みファームウェアの開発環境について

所感ですが、これまでのファームウェアエンジニアは開発環境について良くも悪くもあまり頭を悩ませずにきたと思います。なぜなら採用したマイコン・SoCベンダーからIDE一式が提供(推奨)され、それをサンプルコードそのまま使うケースがほとんどだからです(観測範囲狭し)。まあ、開発環境を作りたいのではなく製品を作りたいので当たり前といえば当たり前ですが・・・。

これまではそれでよかったのかもしれません。IDEのビルドボタンをポチッと押してビルドして、書き込みできれば仕事はできます。しかし、せっかく環境を一新するのであればこれまであった不満を解消したいじゃないですか。

  • 不満① Git管理に向いていない

どこのIDEも同じですがIDEのプロジェクトファイル、Gitで管理しにくいですよね?プロジェクトファイル開いてビルドして閉じただけで差分が出てしまうしデバッガ使っただけで差分が出ます。明らかにプロジェクトファイルに変更がなければコミットしなければいいんですが、ファイルを追加したりインクルードディレクトリ追加したりしたらコミットせざるをえません。自動生成されるファイル類もGitで管理対象とすべきかどうか明文化されていないし。

  • 不満② 環境構築に時間がかかる

今はPrallels Desktopで環境構築しているので最初にセットアップすればイメージファイルを移動させるだけで良いのですが、前職ではPCリプレイスするたびに丸1日以上かけて環境を構築していました。ARM純正のコンパイラ入れて、それにパッチ当てて、ライセンス設定して・・・

  • 不満③ 気軽に開発体験できない

そして、個人的な1番の不満はこれです。弊社、最近エンジニアが増えてきていて、中にはウェブエンジニアが垣根を超えて組み込み開発やってみたいっていう危篤奇特な人達が何人かいるんですけど、みんな macOS なんですよね。Akerunのファームウェアをいじって遊べれば楽しいと思うんですが、Windowsでしか動作しないIDEコンパイラ・・・気軽に体験してもらえないんです。

方針

Zephyr RTOSガイド を見ればわかる通り Zephyr RTOS では Ubuntu, macOS, Windowsがサポートされていてセットアップ方法も懇切丁寧に記載されてます。つまりWindows縛りはありません。しかも cmake + ninja をベースとしたIDEレスな開発環境です。ただ、やはりあれこれとインストールしなければならないのは変わりませんので、これを解決するために開発環境をDockerコンテナで構築することにします。

開発環境をコンテナで

開発環境をDockerコンテナで構築するというのは今となっては珍しい話ではありませんね。Zephyr RTOSでは本家がCI用で使っているDockerfileをGitHubで公開していますし有志の方が公開しているDockerfileもあります。これを使えばいいのですがそのほかに必要なツール類も一括管理するために独自でDockerfileを作成しました。

FROM ubuntu:18.04

ARG SDK_VERSION=0.10.3
ENV ZEPHYR_SDK_SETUP            zephyr-sdk-$SDK_VERSION-setup.run
ENV ZEPHYR_TOOLCHAIN_VARIANT    zephyr
ENV ZEPHYR_SDK_INSTALL_DIR      /opt/zephyr-sdk
ENV PROJECT_DIR                 /hogehoge

RUN apt-get update
RUN DEBIAN_FRONTEND=noninteractive apt-get -y upgrade
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y git cmake ninja-build gperf \
  ccache dfu-util device-tree-compiler wget \
  python3-pip python3-setuptools python3-tk python3-wheel xz-utils file \
  make gcc gcc-multilib \
  locales \
  clang-format
RUN pip3 install cmake west requests docopt
RUN cd /tmp && wget https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v${SDK_VERSION}/${ZEPHYR_SDK_SETUP}
RUN chmod 755 /tmp/${ZEPHYR_SDK_SETUP}
RUN /tmp/${ZEPHYR_SDK_SETUP} -- -d ${ZEPHYR_SDK_INSTALL_DIR}
RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

RUN locale-gen en_US.UTF-8
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8

## 中略 〜秘密〜 中略

WORKDIR $PROJECT_DIR

ENTRYPOINT ["/bin/bash", "-c"]
CMD [ "/bin/bash", "-c" ]

今見返したらほぼ参考にした marshall/zephyr-docker と同じでした。実はここからUbuntuベースではなくAlpine Linuxベースに変更しようとしたのですがパッケージマネージャから必要なツールが入手できずに断念した結果です(確かdevice-tree-copiler)。Zephyrはお好きなコンパイラ選べるんですが本家がリリースしているSDKを採用しています。(Locale設定しているのはなんでだっけ?)

イメージ生成、配布は GitLabで

さて、Dockerfileができたので皆にこれを使ってもらえばいいのですがまだ問題があります。それはapt-getpipなどのパッケージマネージャで取得したパッケージが更新されるとイメージを作る時期によって生成されるイメージが変わってしまい、さらにはパッケージレジストリのメンテが終了するとイメージ自体の生成に失敗してしまいます。この対策としてDockerイメージ生成して、それを共有することにしました。DockerイメージのレジストリはDocker Hubが有名ですが、ちょうど社内に導入した GitLab に Container Registry機能があったため、こちらを使用することとしました。

GitLabはCIが内蔵されており(最近GitHubでもできるようになりましたね)ブランチやタグのpushなどと連動して、GitLab Runnerを走らせているマシンで任意の処理を実行させることができます。試しに開発環境用Dockerfileのプロジェクトファイルのルートに .gitlab-ci.yml という名称のファイルを作り↓の内容記載してGitLabにプッシュすると、Dockerビルド〜GitLabにDockerイメージをプッシュするところまでできてしまいました。

stages:
  - build
  - test
  - deploy
  
variables:
  IMAGE_BRUNCH: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG
  IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_BUILD_TAG

before_script:
  - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY

build:
  stage: build
  script:
    - docker build -t $IMAGE_BRUNCH .
    - docker push $IMAGE_BRUNCH
  tags:
    - build-docker

tag-build:
  stage: build
  only:
    - tags
    - triggers
  script:
    - docker build -t $IMAGE_TAG .
    - docker push $IMAGE_TAG
  tags:
    - build-docker

たったこれだけで下記のように GitLab にイメージが格納されプロジェクトのメンバーであれば誰でもイメージが取得できるようになりました。しかもGit側でタグ打てば連動して Container Registry上でもタグが打たれてタグでバージョン管理ができます。

f:id:photosynth-inc:20191206115334p:plain
GitLab Container Registry

上記のイメージを引っ張ってきつつdocker runするには↓

docker run --name saituyo-env -it -v ~/host_no_repo/zephyr:/hogehoge odorenotokorono.gitlabnourl.com:5555/hogehoge/dev-env-docker:0.1.1

これでいつでも誰でも使える開発環境+再現可能な開発環境ができました!めでたしめでたし。

一歩進んで VS Code Remote Development

さて、めでたしめでたしなんですが、"みんなで使える"とうたった割には Docker自体の知識が少し必要だったり敷居が高いことは否めません。実際に自分で使ってみて課題も出てきました。

  • Dockerの使い方を勉強する必要がある(メンテ用コマンドとか)
  • 特定のブランチ、タグがどのバージョンの開発環境に対応しているのか不明
    • ソースコードも開発環境もどちらも最新を使っていればいいんですが、古いソースコードの再現確認のためリポジトリを遡った時にどの開発環境か?っていうのがわからなくなります。連動したいですよね
  • コンテナ生成初回におまじないがある
    • これはZephyr固有の問題ですがセットアップ時にあれだけ色々インストールしているのに、ビルドするためには追加のpythonのパッケージが必要となります。しかもこの必要パッケージのリストはZephyrのソースコードの中にあるため、どうしても開発環境コンテナの中には組込めませんでした。
    • あと各種の環境変数をセットアップするのにやはりソースコード内のスクリプトを実行する必要がありこれも開発環境コンテナの中には組込めませんでした。

どうしたか?

世の中便利になったもので VSCode にはDockerコンテナの中でプロジェクトを編集できる VSCode Remote Development 機能拡張があります。この機能拡張をインストールしたVSCodeでプロジェクト直下に.devcontainer/devcontainer.jsonを配置していると記載した設定にしたがってプロジェクト全体をDockerコンテナ上でマウントした状態で立ち上げてくれます。そしてdevcontainer.jsonに下記のように記載すると、なんと!自動的にイメージをGitLabから取ってきて、さらには Pythonの必要パッケージのインストール、環境変数のセットアップまでやってくれます。

{
    "name": "hogehoge firmware",
    "image": "odorenotokorono.gitlabnourl.com:5555/hogehoge/dev-env-docker:0.1.1",
    "extensions": [
        "ms-vscode.cpptools",
        "EditorConfig.EditorConfig",
        "plorefice.devicetree",
        "twxs.cmake",
        "eamodio.gitlens",
    ],
    "runArgs": [ "-v", "${env:HOME}${env:USERPROFILE}/.ssh:/root/.ssh-localhost:ro" ],
    "postCreateCommand": "pip3 install -r zephyr/scripts/requirements.txt && echo 'source ./zephyr/zephyr-env.sh' >> ~/.bashrc && mkdir -p ~/.ssh && cp -r ~/.ssh-localhost/* ~/.ssh && chmod 700 ~/.ssh && chmod 600 ~/.ssh/*"
}

(sshをごにょごにょしているのはコンテナ内でもホスト環境のssh鍵を継承してgitにアクセスするためです)

そして、この.devcontainer自体もgit管理プロジェクトとして組み込んでしまえば特定バージョンのソースコードに連動した開発環境をVSCodeが自動的に引っ張ってきてくれるようになります。実際はソースコードリポジトリ.devcontainerをコミットしたわけでなく独立したGitLabのプロジェクトとして存在して、Zephyr (west) の複数Gitリポジトリ管理機能でファームウェアソースコードリポジトリと同期させてます。

これでVSCodeを使う限りDockerの知識がなくてもソースコードディレクトリを開くだけで追加おまじない不要なビルド環境が付いてくる、"みんなで使える"開発環境が用意できました!ただ、開発環境をVSCodeで完全に縛ってしまうと夜道でVimmerにさっくりやられてしまう世知辛い世の中ですので、純Dockerで使える環境はキープするのが全体的に幸せと思われます。

まとめると

  • 各自の環境にインストールすのは git / python3 / west / VSCode / Docker だけ
  • west initリポジトリ取ってきてVSCodeで開き、おもむろにTerminalで west build hogehoge とするともうファームウェアが出来ちゃう

という個人的には理想に近い環境が出来上がりました!あとはこの環境でみんなで開発を進めていくだけです。

オチ

回路エンジニア(Windows)の方が環境構築に挑戦したところ、実はWindows HomeではHyper-Vなしのため、VSCode Remote Developmentが使えないことが判明し即刻挫折しました 😩!ケチケチするなよMicr◯s◯ft!Windows 10 Pro ならいけるんじゃないでしょうか?知らんけど。

おわり

・・・

株式会社フォトシンスでは、一緒にプロダクトを成長させる様々なレイヤのエンジニアを募集しています。 recruit.jobcan.jp

Akerun Proの購入はこちらから akerun.com