みつきんのメモ

組み込みエンジニアです。Interface誌で「Yocto Projectではじめる 組み込みLinux開発入門」連載中

emacsのlanguage serverをEglotに乗り換える

はじめに

lsp-modeがあまり使いやすくないのでemacs 29から標準に入ったEglotに乗り換える。

環境設定

emacs

Ubuntu 22.04のemacsは古いのでsnapを使用する。 aptなどでインストール済みのものがある場合は削除しておく。

$ sudo snap install emacs --classic

Node.js

いくつかのlanguage serverはNode.jsで実装されているのでsnapでインストールする。

$ sudo snap install node --classic

pip3

いくつかのlanguage serverはPython3で実装されているのでpip3をインストールする。

$ sudo apt install python3-pip

language serverのインストール

下記をインストールする

  • bash-language-server
  • bitbake-language-server
  • cmake-language-server
  • html-languageservice
  • clangd
$ npm install bash-language-server
$ npm install --save vscode-html-languageservice
$ pip3 install --user bitbake-language-server
$ pip3 install --user cmake-language-server
$ pip3 install --user python-lsp-server
$ sudo apt install clangd

init.elの設定

;; eglot(LSP)
(use-package eglot
  :ensure t
  :hook
  (c++-mode . eglot-ensure)
  (sh-mode . eglot-ensure)
  (python-mode . eglot-ensure)
  (html-mode . eglot-ensure)
  (cmake-mode . eglot-ensure)
  (bitbake-mode . eglot-ensure)
  :config
  (add-to-list 'eglot-server-programs '((bitbake-mode) "bitbake-language-server"))
  :bind (("M-t" . xref-find-definitions)
     ("M-r" . xref-find-references)
     ("C-t" . xref-go-back)))

まとめ

lsp-modeはサポートしていないLSPサーバを登録するのにいろいろ書かないといけないし プロジェクトルートを登録するか毎回聞いてきてウザいなと色々不満があった。 設定が汚れてくるとまともにサーバと接続できなくて~/.emacs.d/.lsp-session-v1を削除することもしばしば。

eglotはサーバの登録も簡単だし毎回プロジェクトルートを聞いてくることもない。 今の所の使用感はかなり良い。

sshとtmuxを組み合わせる

はじめに

ssh先でもローカルでもbash起動時にtmuxが起動した状態にしたい。

設定

bashrc

bashを起動した時点でdefaultというセッションでtmuxを起動したい。 すでにdefaultセッションが存在する場合にはアタッチする。

~/.bashrcに下記を追加する

if [ -z ${TMUX} ]; then
    tmux new-session -A -s default
fi

sshへの細工

上記の内容をローカルとssh先の両方に書いた場合、tmuxセッションがネストしてしまう。 これを避けるために下記のようなスクリプト~/bin/ssh.shとして作成する。

#!/bin/sh

if [ -n "${TMUX}" ]; then
    tmux detach -E "ssh $*"
else
    ssh "$@"
fi

これをalias設定する。

alias ssh='ssh.sh'

これによりssh実行時にローカルのtmuxセッションがデタッチされる。

tmuxセッションの中から新規セッションを作成

Ctrl+bしてから:new -s <session-name>する。

tmuxセッションを切り替える

Ctrl+bしてからsを押す。

参考

まとめ

若干使い勝手が悪い部分もあるワークアラウンドだが自分がやりたいことはできているので良しとする。

YoctoProject bitbake-hashservを使ってみる(TCP/IP編)

はじめに

前回unix domain socketでローカルPC上でsstate-cacheを共有した。

今回はリモートPCでsstate-cacheを共有してみる。

bitbake-hashservの実行

サーバのIPアドレス

リモートPCから接続するためにIPアドレスを決めておく必要がある。

今回は192.168.1.100とする。

サーバ側の設定

サーバとなる初回ビルドを行う環境についてはlocal.confにこのような設定があれば良い。

BB_HASHSERVE = "auto"
BB_SIGNATURE_HANDLER = "OEEquivHash"

このままbitbakeを実行して「hashserv.db」と「sstate-cache」が生成されれば必要なデータは揃う。 もちろん初回実行時なので数時間はかかる。

サーバ側環境の構築

実際にbitbake-hashservを動かす環境を作成してみる。

$ mkdir -p ~/yocto/rpi-kirkstone
$ cd ~/yocto/rpi-kirkstone
$ git clone git://git.yoctoproject.org/poky.git -b kirkstone
$ source poky/oe-init-build-env build-hashserv
$ bitbake-layers layerindex-fetch meta-raspberrypi

local.confに下記を追加する。

MACHINE = "raspberrypi4-64"

# setup for hashserv database
BB_SIGNATURE_HANDLER = "OEEquivHash"

# enable uart
ENABLE_UART = "1"

# systemd
INIT_MANAGER = "systemd"

これでbitbakeを実行する。

$  bitbake core-image-base

ここまでは前回と同じ。

SSTATE_DIRの公開

bitbake-hashservでリモートからの接続を受け付ける前に、SSTATE_DIRに保存されているssatete-cacheをリモートからアクセスできるようにする必要がある。

httpでアクセスできるようにpythonhttp.serverを使用する。

$ cd ~/yocto/rpi-kirkstone/build-hashserv/sstate-cache
$ python3 -m http.server 9000

bitbake-hashserv

別の端末でbitbake-hashservを起動する。

$ cd ~/yocto/rpi-kirkstone
$ source poky/oe-init-build-env build-hashserv
$ bitbake-hashserv -r -l debug -d ./cache/hashserv.db -b "0.0.0.0:8687"

bitbake-hashservへの接続

クライアント環境の構築

クライアントとなる別のPCでYoctoProjectの環境を作成する。

$ mkdir -p ~/yocto/rpi-kirkstone
$ cd ~/yocto/rpi-kirkstone
$ git clone git://git.yoctoproject.org/poky.git -b kirkstone
$ source poky/oe-init-build-env
$ bitbake-layers layerindex-fetch meta-raspberrypi

local.confの設定

local.confに下記の内容を追加する。

MACHINE = "raspberrypi4-64"

# setup for connecting to bitbake-hashserv
BB_SIGNATURE_HANDLER = "OEEquivHash"
BB_HASHSERVE = "192.168.1.100:8687"
SSTATE_MIRRORS = "file://.* http://192.168.1.100:9000/PATH"

# enable uart
ENABLE_UART = "1"

# systemd
INIT_MANAGER = "systemd"

bitbake-hashservへの接続

接続のための設定をlocal.confに行ったため、bitbakeを実行してみる。

$ time bitbake core-image-base
... (snip) ...
real    7m56.090s
user    0m15.131s
sys     0m2.858s

今回使用したクライアント用のPCはビルドPCのスペックからみるとコア数が1/4のノートPCなので、通信の遅延を考慮しても 約8分でbitbakeが完了するのはやはりすごい。

応用編 kasをつかう

リモートでsstate-cacheを共有する時に最大限に効果を得るためには下記のようなことをサーバ側と揃える必要がある。

  1. 使用するレイヤ
  2. レイヤのリビジョン
  3. bitbake-hashservへの接続情報

それらを手動で揃えるのもそこまで手間ではないがkasを使用するともっと確実に環境を整えることができる。

kasのインストール

kasはpipでインストールできる

$ pip3 install --user kas

設定ファイルの作成

kasはYAML形式の設定ファイルを使用してbitbake環境を整えてくれる。bblayers.confやlocal.confまで自動生成してくれるので、 YAMLファイルを共有しておけば確実に同じ環境を作成することができる。

この内容と適当な名前で保存する。今回はtest.yamlとする。

header:
  version: 14

machine: raspberrypi4-64
distro: poky
target:
  - core-image-base

repos:
  poky:
    url: https://git.yoctoproject.org/git/poky
    path: poky
    refspec: 6cb27ba5379e0132d7ec46f1633c24855a3fa298
    layers:
      meta:
      meta-poky:
      meta-yocto-bsp:

  meta-raspberry:
    url: https://git.yoctoproject.org/meta-raspberrypi
    path: poky/meta-raspberrypi
    refspec: 9dc6673d41044f1174551120ce63501421dbcd85

bblayers_conf_header:
  standard: |
    POKY_BBLAYERS_CONF_VERSION = "2"
    BBPATH = "${TOPDIR}"
    BBFILES ?= ""

local_conf_header:
  standard: |
    CONF_VERSION = "2"
    PACKAGE_CLASSES = "package_rpm"
    SDKMACHINE = "x86_64"
    USER_CLASSES ?= "buildstats"
    PATCHRESOLVE = "noop"
  debug-tweaks: |
    EXTRA_IMAGE_FEATURES ?= "debug-tweaks"
  diskmon: |
    BB_DISKMON_DIRS ??= "\
      STOPTASKS,${TMPDIR},1G,100K \
      STOPTASKS,${DL_DIR},1G,100K \
      STOPTASKS,${SSTATE_DIR},1G,100K \
      STOPTASKS,/tmp,100M,100K \
      HALT,${TMPDIR},100M,1K \
      HALT,${DL_DIR},100M,1K \
      HALT,${SSTATE_DIR},100M,1K \
      HALT,/tmp,10M,1K"
  qemu_configuration: |
    PACKAGECONFIG:append:pn-qemu-system-native = " sdl"
  hashserv: |
    BB_SIGNATURE_HANDLER = "OEEquivHash"
    BB_HASHSERVE = "192.168.1.100:8687"
    SSTATE_MIRRORS = "file://.* http://192.168.1.100:9000/PATH"
  enable_uart: |
    ENABLE_UART = "1"
  systemd: |
    INIT_MANAGER = "systemd"

kasの実行

今回はビルド時間を計測したいので下記を実行して予めビルド環境を取得しておく。

$ kas checkout test.yaml

ビルドを実行する。書きを実行することでkasの中からbitbakeが呼び出される。

$ time kas build tests.yaml
... (snip) ...
real    7m39.846s
user    0m15.850s
sys     0m2.914s

手動で作成した環境のビルド時間とほぼ同じとなる。

「kas checkout」を実行せずに「kas build」を実行した場合でも自動的にcheckout相当の処理が実行される。 また、一度checkoutして環境を作成してしまえばkasを使用せずに通常通りbitbakeを使ってビルドすることもできる。

BSPによってはrepoツールで複数のレイヤを管理するケースもあるがrepoはリポジトリの管理しかしないため、 local.confやbblayers.confなどは自分で生成する必要がある。 サーバ設定などの反映は手動で行うか、自動化したければ自分でスクリプトなどを作成する必要がある。

まとめ

リモートで他のPCのsstate-cacheを共有する方法についてまとめた。 クライアント側の設定を解説する情報はわりとよく見かけるが、サーバ側の設定については見かけないので、調べるのに少し苦労した。 クライアント側でサーバの共通設定を共有するためにはkasは便利だが、それを使用しない場合でも共通設定だけsite.confに記述して共有する方法もある。

自前でsstate-cacheのサーバを構築できれば、社内などでYoctoProjectを使用する際の作業効率は劇的に向上するのではなかろうか。

YoctoProject bitbake-hashservを使ってみる(unix domain socket編)

はじめに

bitbakeではShared State Cache(sstate-cache)という仕組みを持っていて、今回ビルドしたいものが前回ビルドしたものと変更がなければ、成果物をそのまま再利用するようになっている。

手っ取り早い方法は複数のビルドディレクトリのlocal.confで設定されるSSTATE_DIRを同じディレクトリに設定すること。

この仕組みをリモート間でも共有できるようにbitbake-hashservというツールが提供されている。

今回はbitbake-hashservをunix domain socketで使用してみる。

Shared State Cacheの仕組み

ざっくりとsstate-cacheの仕組みを説明すると下記のようになる。

初回実行時

  1. bitbakeで実行されるタスクのインプットデータについてハッシュを取っておく
  2. そのハッシュとタスクのアウトプットデータのマッピング情報を保存しておく
  3. アウトプットデータの情報は捨てずに取っておく

再実行時

  1. 今回実行されるタスクのインプットデータのハッシュとsstate-cache内のハッシュが一致するか確認する
  2. 一致した場合そのタスクは処理しない(一致しない場合はタスクを実行して3のプロセスはスキップ)
  3. 処理しないタスクのアウトプットデータをクライアントに供給する

bitbake-hashservについて

unix domain socketでの接続

bitbake-hashservはunix domain socketでも通信できるようになっていて、ローカルのPC内でもShared Stateを共有できるようになっている。 単にSSTATE_DIRを共有することとの違いは後から参照するbitbakeによってもともとあるsstate-cacheが破壊されづらいという利点がある。 bitbake-hashservは起動時にリードオンリーモードを指定できるため、更に安全に運用することができる。

短所としてはデータがコピーされるため同一PC内ではsstate-cacheに使用されるストレージ容量が増加してしまうところ。

BB_SIGNATURE_HANDLERに「OEEquivHash」を設定してbitbakeを実行するとcache/hashserv.dbが作成される。 これはSQLiteのデータベースとなっており、タスクのインプットデータのハッシュとアウトプットデータのマッピング情報を持っている。

また、OEEquivHashの「Equiv」はEquivalenceの略で、インプットデータ中の空白など実体に関係ない差分は「同一とみなす」という処理を行っている。これによりsstate-cacheの適用可能性が高くなるようになっている。

bitbake-hashservの実行

サーバ側の設定

サーバとなる初回ビルドを行う環境についてはlocal.confにこのような設定があれば良い。

BB_HASHSERVE = "auto"
BB_SIGNATURE_HANDLER = "OEEquivHash"

このままbitbakeを実行して「hashserv.db」と「sstate-cache」が生成されれば必要なデータは揃う。 もちろん初回実行時なので数時間はかかる。

サーバ側環境の構築

実際にbitbake-hashservを動かす環境を作成してみる。

$ mkdir -p ~/yocto/rpi-kirkstone
$ cd ~/yocto/rpi-kirkstone
$ git clone git://git.yoctoproject.org/poky.git -b kirkstone
$ source poky/oe-init-build-env build-hashserv
$ bitbake-layers layerindex-fetch meta-raspberrypi

local.confに下記を追加する。

MACHINE = "raspberrypi4-64"

# setup for hashserv database
BB_SIGNATURE_HANDLER = "OEEquivHash"

# enable uart
ENABLE_UART = "1"

# systemd
INIT_MANAGER = "systemd"

試しにcore-image-baseをビルドする。 今回はtimeコマンドで時間を計測する。

$ time bitbake core-image-base
... (snip) ...
real    84m43.468s
user    0m29.526s
sys     0m9.020s

まっさらな状態でビルドした結果、約85分かかった。

bitbake-hashservの起動

この状態でcacheディレクトリを覗くと下記のようにhashserv.dbが生成されていることがわかる。

~/yocto/rpi-kirkstone/build-hashserv$ ls ./cache/
bb_codeparser.dat        bb_unihashes.dat  local_file_checksum_cache.dat
bb_persist_data.sqlite3  hashserv.db       sanity_info

これを使用してbitbake-hashserveを起動する。接続状況を把握しやすいようにログレベルをdebugに指定している。

$ bitbake-hashserv -r -l debug -d ./cache/hashserv.db -b "unix://./hashserv.sock"

今回は「unix://./hashserv.sock」で待ち受けしているため、${HOME}/yocto/rpi-kirkstone/build-hashserv/hashserv.sockに接続することでサーバと通信できる。

bitbake-hashservへの接続

クライアント側の設定

実行中のbitbake-hashservに接続するにはlocal.confに次のような設定を行う。

BB_SIGNATURE_HANDLER = "OEEquivHash"
BB_HASHSERVE = "unix://${HOME}/yocto/rpi-kirkstone/build-hashserv/hashserv.sock"
SSTATE_MIRRORS = "file://.* file://${HOME}/yocto/rpi-kirkstone/build-hashserv/sstate-cache/PATH"

BB_HASHSERVEにはbitbake-hashserv実行時に作成されたunix domain socketのファイルを指定する。

SSTATE_MIRRORSにはサーバ側の初回ビルド時に作成されたsstate-cacheの保存場所を指定する。 つまり、サーバが提供するsstate-cacheの在処となる。 PATHと指定することでsstate-cacheディレクトリ以下の構造をbitbakeが展開できるようになる。

クライアント側環境の構築

実際にbitbake-hashservに接続する環境を作成してみる。

サーバ側とは別の端末を開き下記を実行する。

$ cd ~/yocto/rpi-kirkstone
$ source poky/oe-init-build-env build-client
$ bitbake-layers add-layer ../poky/meta-raspberrypi

local.confに下記を記載する。

MACHINE = "raspberrypi4-64"

# setup for connecting to bitbake-hashserv
BB_SIGNATURE_HANDLER = "OEEquivHash"
BB_HASHSERVE = "unix://${HOME}/yocto/rpi-kirkstone/build-hashserv/hashserv.sock"
SSTATE_MIRRORS = "file://.* file://${HOME}/yocto/rpi-kirkstone/build-hashserv/sstate-cache/PATH"

# enable uart
ENABLE_UART = "1"

# systemd
INIT_MANAGER = "systemd"

bitbake-hashservへの接続

接続のための設定をlocal.confに行ったため、bitbakeを実行してみる。

$ time bitbake core-image-base
... (snip) ...
real    1m43.522s
user    0m1.438s
sys     0m0.472s

bitbake-hashservの提供するsstate-cacheを参照することでビルド時間が劇的に短くなることがわかる。

サーバ側のログ

この時のサーバ側のログの表示は下記のようになっており、外部から接続されデータを供給したことが伺える。

$ bitbake-hashserv -r -l debug -d ./cache/hashserv.db -b "unix://./hashserv.sock"
Listening on './hashserv.sock'
Client '' connected
Handling get-stream
Client disconnected

まとめ

bitbakeではもともと実装されていたsstate-cacheを他のビルド環境から参照することでビルド時間が劇的に改善することがわかった。 もちろん別のMACHINE向けのビルドだったり、参照するレシピが更新されていたりして保存されているsstate-cacheと入出力が異なる場合はタスクが実行されるためこの効果は薄いもしくは無い。

bitbake-hashservを使用することで直接SSTATE_DIRを共有しなくても、既存のsstate-cacheを参照できることがわかった。 今回はunix domain socketで同一ホストのデータを参照したが、リモートで提供されているsstate-cacheを参照できそうなこともわかった。

機会があれば実際にリモートのsstate-cacheを提供/参照する方法も試してみたい。

YoctoProject devtoolを使ってみる(modify編)

はじめに

下記ではdevtoolについて解説してきた。

devtoolでは以下のことができる。

  • レシピの新規作成
  • レシピの編集
  • レシピのアップグレード

今回はレシピの編集に注目してみる。

環境構築

作業用レイヤ

作業用レイヤとしてmeta-modifyを作成する。

$ bitbake-layers create-layer ../poky/meta-modify
$ bitbake-layers add-layer ../poky/meta-modify

サンプルレシピ

YoctoProject devtoolを使ってみる(add編)で作成したhello-autotoolsのレシピであるhello_0.0.1.bb使用する。

devtoolによるレシピの編集

ワークスペースへの登録

$ devtool modify hello

ワークスペースを確認する。

workspace
├── README
├── appends
│   └── hello_0.0.1.bbappend
├── conf
│   └── layer.conf
└── sources
    └── hello
        ├── LICENSE
        ├── Makefile.am
        ├── README.md
        ├── configure.ac
        └── src
            ├── Makefile.am
            ├── hello.cpp
            ├── hello.h
            └── main.cpp

ちなみにdevtool modifyの場合はYoctoProject devtool addでローカルのソースをレシピ化することを考えるで扱ったhelloworldの様にローカルのソースを持ったレシピでもワークスペースのsourcesディレクトリにソースを展開してくれる。

initial-revの確認

hello_0.0.1.bbappendでinitial-revを確認する。

$ grep 'initial_rev' workspace/appends/hello_0.0.1.bbappend
# initial_rev: 36ef62fd0cd05f72f589a296e39044d71371a46b

このリビジョンが修正差分の基点となる。

ソースを修正する

ソースを修正してみる。 もともと大した処理はしていないのでループにしてみる。

diff --git a/src/main.cpp b/src/main.cpp
index ae9fcb0..3ae5264 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,6 +1,11 @@
 #include "hello.h"
+#include <iostream>

 int main() {
-    Hello("world").say();
+    Hello hello("world");
+    for (int i = 0; i < 10; ++i) {
+        std::cout << "i = " << i << " : ";
+        hello.say();
+    }
     return 0;
 }

コミットする。

$ git commit -m "Modify to loop"

ついでにREADME.mdも修正してみる。

diff --git a/README.md b/README.md
index 9a047d9..fe1b69e 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,5 @@
 # hello-autotools

 This repository contains a sample project using the Autotools.
+
+The sample program says "hello" to the world.

こちらもコミットする。

$ git commit -m "Update README"

コミット履歴を見る。

$ git log --oneline
5344599 (HEAD -> devtool) Update README
0bc1094 Modify to loop
36ef62f (tag: devtool-patched, tag: devtool-base, origin/main, origin/HEAD, main) Add the program
5b8d63b Initial commit

2つコミットが積まれている。

meta-modifyに修正内容を登録

devtool finishを行いmeta-modifyにレシピの修正差分を登録する。

$ devtool finish -r hello ../poky/meta-modify

meta-modifyの内容を確認する。

../poky/meta-modify/
├── COPYING.MIT
├── README
├── conf
│   └── layer.conf
├── recipes-app
│   └── hello
│       ├── hello
│       │   ├── 0001-Modify-to-loop.patch
│       │   └── 0002-Update-README.patch
│       └── hello_%.bbappend
├── recipes-example
│   └── example
│       └── example_0.1.bb
└── recipes-recipetool

bbappendが作成されinitial-rev以降の2つのコミットがパッチとしてレシピに登録されている。 このパッチはgit format-patchによって生成されている。

この時点のworkspaceを確認する。

workspace/
├── README
├── appends
├── conf
│   └── layer.conf
└── sources

初期状態に戻っている。

まとめ

devtool modifyは文句なく便利。

YoctoProject devtool addでローカルのソースをレシピ化することを考える

はじめに

devtool addはレシピ作成の大半の処理をrecipetool createで行っているが、 どちらのツールもネットワーク越しに公開されているソースコードからレシピを作成することを前提としている。

つまり、ローカルストレージ上にあってネットワーク上で公開されていないソースファイルをこれらのツールで レシピ化するにはハードルがあるということになる。

このようなソースコードからdevtool addを使用してレシピを作成するにはどうすればよいのか考えてみる。

ソースコードの準備

前回使用したサンプルとは別のものを使用する。

$ cd 
$ git clone https://github.com/mickey-happygolucky/helloworld.git
$ cd helloworld
$ rm -rf .git

.gitを削除することでgitリポジトリではなくしている。

devtool

ワークスペースへの追加

$ devtool add ~/helloworld

ワークスペースの内容を確認してみる。

./workspace/
├── README
├── appends
│   └── helloworld.bbappend
├── conf
│   └── layer.conf
├── recipes
│   └── helloworld
│       └── helloworld.bb
└── sources

リモートのプロジェクトを指定したときと異なり、ソースがワークスペース内のsourcesディレクトリに配置されていない。

これはrecipetool createにローカルのソースツリーを指定した場合ダウンロード処理が行われず、かつ、--extract-toオプションを指定できないため、ワークスペースのへのソースツリーのダウンロードが行われないためこのような動作になっている。

helloworld.bbappendを確認するとEXTERNAL_SRCにソースツリーのパスが直接指定されるようになっている。

inherit externalsrc
EXTERNALSRC = "/home/mickey/helloworld"

このため、devtool buildなどを行っても見かけ上はワークスペース内のsourcesディレクトリにあるときと同じような動作になる。

helloworld.bbを確認するとSRC_URIが空になっていることがわかる。

# Recipe created by recipetool
# This is the basis of a recipe and may need further editing in order to be fully functional.
# (Feel free to remove these comments when editing.)

# WARNING: the following LICENSE and LIC_FILES_CHKSUM values are best guesses - it is
# your responsibility to verify that the values are complete and correct.
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://LICENSE;md5=134b970c55f7388271efa7b17c06d072"

# No information for SRC_URI yet (only an external source tree was specified)
SRC_URI = ""

inherit cmake

# Specify any options you want to pass to cmake using EXTRA_OECMAKE:
EXTRA_OECMAKE = ""

レシピのレイヤへの登録

devtool finishを実行してみる。

$ devtool finish helloworld ../poky/meta-work

下記のようなエラーになる。

NOTE: Starting bitbake server...
Traceback (most recent call last):
  File "/home/mickey/yocto/rpi-kirkstone/sources/poky/scripts/devtool", line 338, in <module>
    ret = main()
  File "/home/mickey/yocto/rpi-kirkstone/sources/poky/scripts/devtool", line 325, in main
    ret = args.func(args, config, basepath, workspace)
  File "/home/mickey/yocto/rpi-kirkstone/sources/poky/scripts/lib/devtool/standard.py", line 2060, in finish
    check_git_repo_op(srctree, [corebasedir])
  File "/home/mickey/yocto/rpi-kirkstone/sources/poky/scripts/lib/devtool/__init__.py", line 371, in check_git_repo_op
    stdout, _ = bb.process.run('git rev-parse --show-toplevel', cwd=srctree)
  File "/home/mickey/yocto/rpi-kirkstone/sources/poky/bitbake/lib/bb/process.py", line 189, in run
    raise ExecutionError(cmd, pipe.returncode, stdout, stderr)
bb.process.ExecutionError: Execution of 'git rev-parse --show-toplevel' failed with exit code 128:
fatal: not a git repository (or any of the parent directories): .git

devtool finishでは、レイヤにレシピを登録する際にワークスペース内で行った修正の差分からパッチを作成し、レシピに登録するという処理を行うため、ワークスペース登録中のレシピのソースツリーはgitリポジトリである必要がある。

前回説明した通り、devtool addでネットワーク越しのソースを指定した場合は対象がgitリポジトリじゃない場合は自動的にgitリポジトリに変換される。

ソースツリーをgitリポジトリにしてみる。

$ cd ~/helloworld
$ git init
$ git add .
$ git commit -m "Initial commit"

この状態で再度devtool finishを実行する。

$ devtool finish helloworld ../poky/meta-work

エラーの内容は変化するがfinishは失敗する。

NOTE: Starting bitbake server...
NOTE: Reconnecting to bitbake server...
NOTE: Retrying server connection (#1)...
Loading cache: 100% |##################################################################| Time: 0:00:00
Loaded 1679 entries from dependency cache.
Parsing recipes: 100% |################################################################| Time: 0:00:00
Parsing of 919 .bb files complete (917 cached, 2 parsed). 1680 targets, 67 skipped, 0 masked, 0 errors.
ERROR: Unable to find initial revision - please specify it with --initial-rev

devtoolは通常ワークスペースに登録された時点のコミットIDをinitial-revとして覚えておき、finishする際に差分を取るための基点としている。

devtool finishにはオプションでinitial-revが指定できるようになっているため実行時に指定する。

initiale-revに指定するためのコミットIDは下記のように取得する。

$ git rev-parse HEAD
1af1253de73276bc60689af176d69c99df26064a

initial-revを指定してdevtool finishを実行する。

$ devtool finish --initial-rev 1af1253de73276bc60689af176d69c99df26064a helloworld ../poky/meta-work

コマンドは成功する。

レイヤに登録されたレシピ

meta-workを確認する。

../poky/meta-work/
├── COPYING.MIT
├── README
├── conf
│   └── layer.conf
(... snip ...)
└── recipes-helloworld
    └── helloworld
        └── helloworld.bb

レイヤにはレシピは存在するが、ソースコードは存在していない。

helloworld.bbは下記のようにSRC_URIが設定されていない不完全な状態となっている。

# Recipe created by recipetool
# This is the basis of a recipe and may need further editing in order to be fully functional.
# (Feel free to remove these comments when editing.)

# WARNING: the following LICENSE and LIC_FILES_CHKSUM values are best guesses - it is
# your responsibility to verify that the values are complete and correct.
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://LICENSE;md5=134b970c55f7388271efa7b17c06d072"

# No information for SRC_URI yet (only an external source tree was specified)
SRC_URI = ""

inherit cmake

# Specify any options you want to pass to cmake using EXTRA_OECMAKE:
EXTRA_OECMAKE = ""

完全な状態にする

ソースツリーをmeta-workに配置し、SRC_URIを設定する。

$ mkdir ../poky/meta-work/recipes-helloworld/helloworld/files
$ cp -ra ~/helloworld ../poky/meta-work/recipes-helloworld/helloworld/files
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://LICENSE;md5=134b970c55f7388271efa7b17c06d072"

# No information for SRC_URI yet (only an external source tree was specified)
SRC_URI = "file://helloworld"

S = "${WORKDIR}/${BPN}"

inherit cmake

# Specify any options you want to pass to cmake using EXTRA_OECMAKE:
EXTRA_OECMAKE = ""

結局の所、肝心な部分は自動化できていない。

recipetoolでレシピを作成する

一旦devtoolで作成したレシピを削除する。

$ rm -rf ../poky/meta-work/recipes-helloworld

筆者がよくやる手順は下記のような感じになる。

  1. レイヤに格納先ディレクトリを作成する
  2. レイヤにソースコードを配置する
  3. recipetool createでレシピを作成する
  4. レシピの修正

具体的には下記のようになる。

$ mkdir -p ../poky/meta-work/recipes-app/helloworld/files
$ cp -ra ~/helloworld ../poky/meta-work/recipes-app/helloworld/files
$ recipetool create ../poky/meta-work/recipes-app/helloworld/files/helloworld -o ../poky/meta-work/recipes-app/helloworld/helloworld_0.1.bbt
$ recipetool edit helloworld

先ほどとレシピの内容は変わらず。

LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://LICENSE;md5=134b970c55f7388271efa7b17c06d072"

# No information for SRC_URI yet (only an external source tree was specified)
SRC_URI = "file://helloworld"

S = "${WORKDIR}/${BPN}"

inherit cmake

# Specify any options you want to pass to cmake using EXTRA_OECMAKE:
EXTRA_OECMAKE = ""

まとめ

ローカルストレージ上のソースからレシピを作成するためにdevtool addを使用してもあまり便利ではない。 recipetool createのほうがgitリポジトリ化の必要がない分結果的に手順が少なくなる。

ローカルにしろネットワーク越しにしろ、レシピの新規作成にはdevtool addを使用するメリットはあまり感じられなかった。

YoctoProject devtoolを使ってみる(add編)

はじめに

YoctoProjecではdevtoolというツールを提供している。 このツールはbitbake環境か拡張SDKの環境で使用することができる。

devtoolには様々なサブコマンドが用意されており、次のようなことができるようになっている。

  • レシピの新規作成
  • レシピの編集
  • レシピのアップグレード

今回はレシピの新規作成に注目してみる。

先日唐突にものすごく久しぶりにAutotoolsを使ってみたのは、 これのためのサンプルを作るため。

作業用レイヤの作成

bitbake-layersコマンドで作業用レイヤを作成する。

$ bitbake-layers create-layer ../poky/meta-work
$ bitbake-layers add-layer ../poky/meta-work
$ bitbake-layers show-layers

devtool

今回はbitbake環境で作業する。

$ source poky/oe-init-build-env

ワークスペースの作成

devtoolの初回実行時には下記のことが実行される。

  • workspaceの作成
  • workspaceのbblayers.confへの追加

これによりbitbake実行時にもworkspaceの作業内容が反映される様になる。

ワークスペースへのレシピの作成

hello-autotoolsのサンプルでレシピを作成する。

githubのデフォルトブランチはmainだが、devtoolはブランチを指定しないとmasterとみなすので注意が必要。

$ devtool add https://github.com/mickey-happygolucky/hello-autotools.git -B main

このコマンドによって下記のことが実行される。

レシピの雛形

workspace/recipes/hello_git.bbが下記の内容で作成される。

# Recipe created by recipetool
# This is the basis of a recipe and may need further editing in order to be fully functional.
# (Feel free to remove these comments when editing.)

# WARNING: the following LICENSE and LIC_FILES_CHKSUM values are best guesses - it is
# your responsibility to verify that the values are complete and correct.
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://LICENSE;md5=7d3d185c8b095f61b1f86140b6a49a93"

SRC_URI = "git://github.com/mickey-happygolucky/hello-autotools.git;protocol=https;branch=master"

# Modify these as desired
PV = "0.0.1+git${SRCPV}"
SRCREV = "36ef62fd0cd05f72f589a296e39044d71371a46b"

S = "${WORKDIR}/git"

# NOTE: if this software is not capable of being built in a separate build directory
# from the source, you should replace autotools with autotools-brokensep in the
# inherit line
inherit autotools

# Specify any options you want to pass to the configure script using EXTRA_OECONF:
EXTRA_OECONF = ""

1行目のコメントからこのレシピの雛形はrecipetoolで作成されたことがわかる。 これは、devtoolの中からreciptool createが呼び出されるようになっているため。

recipetoolはソースツリーを解析し使用されているビルドシステムを判別するようになっている。 hello-autotoolsはautotoolsを使用しているため、inherit autotoolsしている。

SRC_URIにはgithubのURLが設定されている。

ワークスペースへのソースコード配置

workspace/sources/helloソースコードが配置される。 こちらはrecipetool createの--extractオプションによって行われている。

ソースコードがgitリポジトリじゃない場合はdevtoolによって自動的にgit initが実行され、 gitリポジトリになるようになっている。

External Sourceの設定

devtoolやbitbakeがワークスペースに配置されたソースを参照する様にExternal Sourceの仕組みが使用されている。 workspace/appends/hello_git.bbappendが下記のように作成されている。

inherit externalsrc
EXTERNALSRC = "/home/mickey/yocto/rpi-kirkstone/build/workspace/sources/hello"

# initial_rev: 36ef62fd0cd05f72f589a296e39044d71371a46b

inherit externalsrcしてワークスペース上のソースのパスをを設定している。

initial_revはワークスペースにgitリポジトリが配置された最新のコミットを指しており、 ワークスペース内で修正された差分を作成する基点として扱われる。

レシピのレイヤへの登録

ワークスペースに作成したレシピをレイヤに配置し正式にレシピとして登録する。

$ devtool finish -r hello ../poky/meta-work

-rをつけることでsourceディレクトリに配置されたソースコードが削除される。 逆に言えばこのオプションをつけないとソースコードが残ったままとなる。

レシピを登録したmeta-workの内容を確認してみる。

../poky/meta-work
├── COPYING.MIT
├── README
├── conf
│   └── layer.conf
├── recipes-example
│   └── example
│       └── example_0.1.bb
└── recipes-hello
    └── hello
        └── hello_git.bb

recipes-hello/hello/hello_git.bbにレシピが配置されている。ディレクトリ名はdevtoolによって自動的に決められてしまう。

recipetoolによるレシピの作成

devtool addの処理の大半がrecipetool createだったので、recipetoolを使用してレシピを作成してみる。

$ mkdir -p ../poky/meta-work/recipes-app/hello
$ recipetool create https://github.com/mickey-happygolucky/hello-autotools.git -B main -o ../poky/meta-work/recipes-app/hello/hello_0.0.1.bb

devtoolで作成したレシピを削除する。

$ rm -rf ../poky/meta-work/recipes-hello

meta-workの内容を確認する。

../poky/meta-work
├── COPYING.MIT
├── README
├── conf
│   └── layer.conf
├── recipes-app
│   └── hello
│       └── hello_0.0.1.bb
└── recipes-example
    └── example
        └── example_0.1.bb

まとめ

devtoolは強力なツールだ。しかしレシピの新規作成に関してはレイヤに登録する際のディレクトリ名を自由に設定できなかったり、 一旦ワークスペースソースコードを展開したりしてもあまりメリットを感じない。

ワークスペース内で行うことができるdevtoolの機能については新規作成時にも使用可能だがそれをする意味もない気がする。 個人的にはdevtoolの真価はレシピの編集とレシピのアップグレードにあると思っている。

新規作成に関してはrecipetoolを使用したほうが自由がきいてよいのではないだろうか。