この記事の前提
- C-10「Yoctoの地図」で扱ったrecipe、layer、imageといった用語を前提にします
- 対象は、主に自社で開発するユーザーアプリケーションです
- 前編は運用と設計判断に集中します。BitBake内部の詳しい動きは後編で扱います
はじめに
現場で、こんな会話をしたことはないでしょうか。
「現地の装置、アプリのバージョンはいくつ?」
「画面には
1.2.3と出ているけど、packageを調べると1.0なんだよね……どっちが本当?」
笑い話のようですが、長期保守する製品ではかなり困ります。画面、アプリのソース、Yocto recipeへ同じ版をそれぞれ手入力すると、どこかの更新を忘れた瞬間に「同じ名前で中身が違うもの」が生まれます。
さらに、一つのrecipeからGUI、常駐デーモン、診断ツールをまとめて作っていると、そもそも「このrecipeの版は、どのアプリの版なのか」まで曖昧になります。
この記事では、まず結論をはっきりさせます。
先に結論
私なら、次の三つを設計ルールにします。
- 独立してリリースする単位ごとにrecipeを分ける
- 同じ版を複数箇所へ手入力しない。生成するか、CIで同期を検査する
- 実際に出荷した組み合わせは、製品マニフェストで確定する
この三つを先に決めておけば、recipeの細かな変数を理解する前でも、版の食い違いを生みにくい運用へ近づけます。GUIは版を新しく手入力する場所ではなく、ビルド時に作った記録を表示する場所にします。
ここで「1アプリ=1 recipe」と決め切らず、1リリース単位=1 recipeと考えるのがポイントです。一つのリポジトリから複数の実行ファイルを作っていても、常に同時に試験・出荷するなら一つのリリース単位です。逆に、同じリポジトリに置かれていても独立して更新するなら、分離を検討します。
そして、すべてを一つの番号で表そうとしません。現場で欲しい「正」は、質問によって違うからです。
| 層 | 正とする情報 | 答えたい問い |
|---|---|---|
| アプリのリリース | アプリ版、release tag | どの機能版か |
| ソースの特定 | 固定したcommit ID | どのソースを使ったか |
| 製品の出荷 | 製品版、Build ID、構成マニフェスト | その個体へ何を組み合わせたか |
つまり、各層の中では正を一つにし、層と層の間を自動生成した記録で結ぶのが基本方針です。
この図では、「どの機能版か」「どのソースを使ったか」「実際に何を出荷したか」を分けています。GUIは新しい「正」を作る場所ではなく、各層の記録から現物を追跡する入口です。
recipeとアプリ、どちらを「正」にするか
アプリが単独でも使われるなら、アプリ側を意味上の正にする
PC版もある、SDKとして別製品へ提供する、複数のOSイメージへ載せる。このようなアプリでは、アプリリポジトリのversion fileまたはrelease tagを意味上の正にします。
Yocto recipeは、そのアプリ版と対応するcommitを採用する側です。recipeの版もアプリ版へ合わせますが、同じ文字列を人が二重に書いて終わりにはしません。CIで次の対応を検査します。
アプリのVERSION = release tag = recipeの版
release tagが指すcommit = recipeが取得するcommit
この構成なら、Yoctoを通さずにビルドするアプリでも、自分の版を名乗れます。
製品専用で必ずYoctoからビルドするなら、recipeから注入してもよい
一方、特定製品専用で、必ず同じrecipeからしかビルドしないアプリもあります。この場合はrecipeの版をビルドオプションとしてアプリへ渡し、バイナリの版情報を生成する構成も成立します。
大事なのは、アプリのソースへ同じ版をもう一度直書きしないことです。
ただし、版が同じでもcommitが違えば中身が同じとは限りません。人が理解するアプリ版と、ソースを一意にするcommit IDは両方残します。
「一つの真実」と「三つの正」は矛盾しない
少しややこしく見えますが、整理すると単純です。
- アプリ版という値の入力元は一つにする
- ソースを特定する値はcommit IDにする
- 出荷物全体は製品マニフェストで確定する
一つの値を三か所へ手入力しない、という意味では「一つの真実」です。しかし、アプリ版だけに製品全体の追跡責任まで背負わせない、という意味では「三つの正」です。
一つのrecipeに複数アプリがある場合
判断基準は、ファイル数でもプロセス数でもありません。
それらを別々にリリースしたいか
ここで決めます。
更新周期が独立するならrecipeを分ける
GUI、通信デーモン、データ収集サービスが、それぞれ別の担当・別のリリース周期を持つなら、別recipeにするのが素直です。
recipes-product/
├── product-gui/product-gui_2.3.0.bb
├── comm-daemon/comm-daemon_1.8.2.bb
├── data-agent/data-agent_4.1.0.bb
└── packagegroups/packagegroup-product-apps.bb
最後にpackagegroupやimage recipeで組み合わせます。GUIだけを更新したとき、通信デーモンの版まで意味なく上げずに済みます。不具合や脆弱性の影響範囲も追いやすくなります。
同じソース、同じ試験、同じ出荷なら一つでもよい
一つのmonorepoからGUIとデーモンを作り、必ず同じ組み合わせで試験・出荷するなら、一つのrecipeをbundleのリリース単位として扱えます。
この場合、recipeの版はGUI単体の版ではなく、bundle版です。必要なら実行ファイルごとの内部版も持たせ、製品マニフェストへ列挙します。
事情があって分けられない場合
既存のビルド構成や組織上の事情で、独立して更新するアプリをすぐに別recipeへ分けられないこともあります。その場合は、次の折衷が現実的です。
- recipeには出荷セットとしてのbundle版を付ける
- 各アプリは個別のアプリ版とcommit IDを持つ
- GUIや診断機能はマニフェストから個別版を表示する
- 将来分離する対象だと分かるよう、依存関係と担当範囲を記録する
packageの一行だけで、異なる更新周期を持つすべての真実を表現しようとしないことです。
製品マニフェストを採用する
recipeをきれいに分けても、それだけでは「ある製品へ何が入ったか」は分かりません。製品イメージにはカーネル、設定、複数のアプリ、layerなどが組み合わされるためです。
そこで、ビルド時に機械可読な製品マニフェストを生成します。
{
"product_version": "3.6.0",
"build_id": "PROD-20260721-1842",
"components": [
{
"name": "product-gui",
"app_version": "2.3.0",
"package_version": "2.3.0-r1.4",
"source_revision": "a1b2c3d4e5f6..."
},
{
"name": "comm-daemon",
"app_version": "1.8.2",
"package_version": "1.8.2-r0.7",
"source_revision": "8f7e6d5c4b3a..."
}
]
}
このファイルをGUIと診断コマンドが共通で読めば、画面のソースへ版を直書きせずに済みます。ビルドサーバ側にも同じマニフェストを成果物として保存し、Build IDで装置とビルド記録を結びます。
GUIには何を表示するか
利用者向け画面へ40桁のcommit IDや全package一覧を常時並べる必要はありません。通常表示と保守表示を分けます。
通常表示
製品ソフトウェア : 3.6.0
GUI : 2.3.0
通信サービス : 1.8.2
Build ID : PROD-20260721-1842
現場から写真を一枚送ってもらうなら、この四つが同じ画面へ収まるようにします。
詳細/保守表示
product-gui
App version : 2.3.0
Package version : 2.3.0-r1.4
Source revision : a1b2c3d4e5f6...
comm-daemon
App version : 1.8.2
Package version : 1.8.2-r0.7
Source revision : 8f7e6d5c4b3a...
Build ID : PROD-20260721-1842
詳細情報を「診断情報をコピー」「USBへ書き出す」といった操作でJSONまたはテキストとして取得できると、転記ミスが減ります。起動ログの先頭にも製品版とBuild IDを残しておくと、回収したログとビルドを結びやすくなります。
不具合が見つかったときの追跡経路
理想は、現地の一画面からソースまで次のように戻れることです。
ここで大事なのは、画面の版を飾りではなく、追跡経路の入口として設計することです。
なお、追跡情報がそろっていても、同一バイナリを再生成できるとは限りません。ビルド環境や取得物の固定まで含めた「再現可能性」は、追跡可能性より一段広い問題です。
避けたい運用
- アプリ、recipe、GUIへ同じ版を手作業で三重入力する
- 量産recipeで常に最新commitを取得する
- 独立して更新する複数アプリを、理由なく一つのrecipeへ詰め込む
- packageの改訂番号だけをGUIの「アプリ版」として表示する
- Build IDを時刻だけで作り、対応するCI成果物を保存しない
- マニフェストを人が手で編集する
- SBOMを作るだけで、現物から対応するSBOMへ辿る鍵を残さない
現場コラム:「版が言えない装置」は、直せない装置
長く保守する装置で本当に困るのは、壊れることそのものより、その個体が何を動かしていたか言えないことです。
版が3.6.0としか分からず、Build IDもcommitもなければ、開発環境で再現しようとしても「たぶんこの辺のソース」で止まります。この「たぶん」が、数日から数週間の調査時間に化けます。
版は、きれいな番号を名乗るためだけに出すものではありません。あとから戻れるようにするために出すものです。利用者向けの分かりやすい製品版と、開発者が追跡に使うBuild IDを同じ画面へ置くだけでも、保守の景色はかなり変わります。
実務で採用しやすい最小構成
最初から大規模な構成管理システムを作らなくても、次の六つから始められます。
- アプリごとにversion fileまたはrelease tagを一つ決める
- recipeの版をアプリ版へ合わせ、ビルド対象のcommitを固定する
- 独立して更新するアプリはrecipeを分ける
- 製品イメージへ製品版と一意なBuild IDを付ける
- 最終package版、source revision、layer revisionをマニフェストへ記録する
- GUIへ主要版を表示し、詳細情報をコピーまたは出力できるようにする
まとめと後編
- 基本単位は「1アプリ」ではなく「1リリース単位=1 recipe」
- 意味上のアプリ版は一か所から生成し、同じ値を人が複数箇所へ転記しない
- アプリ版、commit ID、製品マニフェストは別の問いへ答える情報として結び付ける
- GUIには製品版、主要アプリ版、Build IDを表示し、詳細情報からソースまで戻れるようにする
- 実際に出荷した組み合わせの正は、自動生成した製品マニフェストに置く
後編では、この方針をBitBakeの中から確かめます。PV、PR、PKGV、SRCREVがいつ決まり、複数packageやPR Service、buildhistoryがどう関係するのかを詳しく見ていきます。


