7-2. Yocto

Yoctoは、組込みLinuxディストリビューションを作成するためのプロジェクトです。製品固有のLinuxディストリビューションを作成、管理できるため、組込みLinux開発で広く用いられています。ここで言うLinuxディストリビューションは、Linux kernel、ライブラリ、アプリケーションを全て含みます。

日本語のまとまった書籍は、2019年現在ありませんが、雑誌インタフェースなどで、Raspberry Piの独自環境構築や、FPGAボードZynqの環境構築方法が紹介されています。みつきんのメモは、Yocto関連のノウハウが多く掲載されており、普段からお世話になっています。

ここでは、RustプロジェクトをYoctoビルド環境にインテグレーションする方法を紹介します。想定する利用方法は、ホスト上のRustツールチェインで一通り開発、デバッグを行った上で、distributionに取り込んで配布する、というものです。

Yoctoの基礎から説明するスキルが著者にないため、Yoctoを触ったことある方向けの情報になります。ご了承下さい。

ターゲット環境はRaspberry Pi3で、Yoctoのバージョンはthudです。

meta-rust

meta-rustは、既存のRustプロジェクトをYoctoでビルドできるようにするための、Yoctoのレイヤです。

まず、raspberry pi3環境をビルドするためのレイヤを取得します。

mkdir -p rpi-thud/layers
cd rpi-thud/layers
git clone git://git.yoctoproject.org/poky.git -b thud
git clone git://git.yoctoproject.org/meta-raspberrypi -b thud
git clone git://git.openembedded.org/meta-openembedded -b thud

次に、Rustのパッケージを含んでいるmeta-rustをcloneします。

git clone https://github.com/meta-rust/meta-rust.git

環境変数を読み込みます。

source layers/poky/oe-init-build-env build

ビルド対象のレイヤを追加します。

bitbake-layers add-layer ../layers/meta-openembedded/meta-oe
bitbake-layers add-layer ../layers/meta-openembedded/meta-python
bitbake-layers add-layer ../layers/meta-openembedded/meta-networking
bitbake-layers add-layer ../layers/meta-raspberrypi
bitbake-layers add-layer ../layers/meta-rust

local.confを修正します。

ターゲットをRaspberry Pi3にします。

MACHINE = "raspberrypi3"

Rustのサンプルパッケージをrootfsにインストールするようにします。

IMAGE_INSTALL_append = " rust-hello-world"

ビルドします。

bitbake core-image-base

ddコマンドでマイクロSDカードにイメージを書き込みます。

sudo dd if=tmp/deploy/images/raspberrypi3/core-image-base-raspberrypi3.rpi-sdimg of=/dev/sdX bs=100M

/sdXは使用している環境に合わせて適宜変更して下さい。

raspberry pi3を起動して、rust-hello-worldを実行します。

# rust-hello-world
Hello, world!

無事、実行できます。

cargo-bitbake

cargo-bitbakeは、既存のCargoプロジェクトからmeta-rustのYoctoレシピを作成してくれるCargoの拡張機能です。

cargo-bitbakelibssl-devを使用するため、インストールします。

sudo apt install libssl-dev

cargo-bitbakeをインストールします。

cargo install cargo-bitbake

Rust製grepツールのripgrepを取り込んでみます。

git clone https://github.com/BurntSushi/ripgrep.git
cd ripgrep
cargo bitbake

これで、レシピが自動生成されます。

head ripgrep_11.0.1.bb 
# Auto-Generated by cargo-bitbake 0.3.10
#
inherit cargo

# If this is git based prefer versioned ones if they exist
# DEFAULT_PREFERENCE = "-1"

# how to get ripgrep could be as easy as but default to a git checkout:
# SRC_URI += "crate://crates.io/ripgrep/11.0.1"
SRC_URI += "git://github.com/BurntSushi/ripgrep.git;protocol=https"

LIC_FILES_CHKSUMだけは、手動で変更する必要があります。

# FIXME: update generateme with the real MD5 of the license file
LIC_FILES_CHKSUM=" \
file://Unlicense OR MIT;md5=generateme \
"

ripgrepでは、COPYINGファイルにライセンス情報が記載されています。md5sumコマンドでチェックサムを計算して、レシピを修正します。

md5sum COPYING
034e2d49ef70c35b64be514bef39415a  COPYING

レシピは次のようになります。

LIC_FILES_CHKSUM=" \
file://COPYING;md5=034e2d49ef70c35b64be514bef39415a \
"

layers/meta-rust/recipes-example/ripgrep/ディレクトリを作成し、自動生成されたレシピファイルをコピーします。

mkdir ../layers/meta-rust/recipes-example/ripgrep/
cp <path to ripgrep>/ripgrep_11.0.1.bb ../layers/meta-rust/recipes-example/ripgrep/

ビルドします。

bitbake ripgrep

これで、ripgrepがビルドできます。

meta-rust-bin

meta-rustでは、LLVM、Rustコンパイラ、CargoをビルドしてRustツールチェインを構築するため、ビルド時間が大幅に増加します。それにも関わらず、Yoctoで作成したクロス開発環境には、このツールチェインが含まれません。純粋に、Rustのプロジェクトをビルドするだけであれば、既存のRustツールチェインバイナリを取得する方がよほどお手軽です。

そこで、Rustのツールチェインバイナリを取得して、Rustプロジェクトをビルドするmeta-rust-binがあります。

meta-rustと異なり、こちらは、pokyのバージョンがsumoまでしか対応されていません (2019/6/22現在)。

mkdir -p rpi-sumo/layers
cd rpi-sumo/layers
git clone git://git.yoctoproject.org/poky.git -b sumo
git clone git://git.yoctoproject.org/meta-raspberrypi -b sumo
git clone git://git.openembedded.org/meta-openembedded -b sumo
git clone https://github.com/rust-embedded/meta-rust-bin

環境変数を読み込みます。

source layers/poky/oe-init-build-env build

ビルド対象のレイヤを追加します。

bitbake-layers add-layer ../layers/meta-openembedded/meta-oe
bitbake-layers add-layer ../layers/meta-openembedded/meta-python
bitbake-layers add-layer ../layers/meta-openembedded/meta-networking
bitbake-layers add-layer ../layers/meta-raspberrypi
bitbake-layers add-layer ../layers/meta-rust-bin

meta-rust-binには、レシピ例が同梱されていないため、サンプルアプリのレシピを作成します。meta-rustrust-hello-worldレシピがそのまま利用できます。

cp -r <path to meta-rust>/recipes-example/rust-hello-world/ ../layers/meta-rust-bin/

local.confを修正します。

ターゲットをRaspberry Pi3にします。

MACHINE = "raspberrypi3"

Rustのサンプルパッケージをrootfsにインストールするようにします。

IMAGE_INSTALL_append = " rust-hello-world"

ビルドします。

bitbake core-image-base

ddコマンドでマイクロSDカードにイメージを書き込みます。

sudo dd if=tmp/deploy/images/raspberrypi3/core-image-base-raspberrypi3.rpi-sdimg of=/dev/sdX bs=100M

/sdXは使用している環境に合わせて適宜変更して下さい。

raspberry pi3を起動して、rust-hello-worldを実行します。

# rust-hello-world
Hello, world!

無事、実行できます。

meta-rust-binでもripgrepをビルドしてみます。レシピの用意は簡単です。

inherit cargo

SUMMARY = "ripgrep recursively searches directories for a regex pattern"
HOMEPAGE = "https://github.com/BurntSushi/ripgrep"
LICENSE = "MIT"

SRC_URI = "git://github.com/BurntSushi/ripgrep.git;tag=${PV};protocol=https"
S = "${WORKDIR}/git"

LIC_FILES_CHKSUM = "file://LICENSE-MIT;md5=8d0d0aa488af0ab9aafa3b85a7fc8e12"
bitbake ripgrep

これで、ripgrepがビルドできます。

meta-rustとの比較

ラズパイ3のcore-image-baserust-hello-worldを追加したイメージのフルビルドにかかる時間を計測したろころ、meta-rustが約220分、meta-rust-binが約75分でした。Yoctoのバージョンが異なるため、完全なベンチマークとは言えませんが、meta-rust-binの方がビルド時間がかなり短いです。

meta-rust-binはビルド済みのRustツールチェインを利用するため、Rustコンパイラをカスタマイズしてビルドする、ということができません。Rustが公式にサポートしていないアーキテクチャをターゲットにする場合は、meta-rustの利用が必要です。

また、ビルド済みのRust標準ライブラリを利用するため、カスタムビルドされた標準ライブラリよりパフォーマンスが低い可能性があります。

cargo-bitbakeで自動生成するレシピは、meta-rust-binのclassとは互換性がありません。meta-rust-binのレシピを用意するのは、それほど難しくないため、大きなデメリットではありません。

参考