Claude CodeでZephyr RTOSをwestビルドで開発する手順
Zephyr RTOSアプリのファイル構成とwestビルド、devicetreeオーバーレイの差し替えをClaude Codeでどう回すか、権限設定とhooksの具体例つきでまとめます。
Claude CodeでZephyr RTOSのアプリを開発するとき、鍵になるのはCMakeベースのビルドシステムと、devicetreeオーバーレイによるハードウェア差分の管理です。Arduino CLIとは設定を当てる単位が違うため、westコマンドと設定ファイルの役割をどう伝えるかで仕上がりが変わります。この記事ではアプリケーションの構成からビルド、devicetreeオーバーレイの差し替え、CLAUDE.mdとhooksでの安定化までを順に扱います。
Zephyrアプリのディレクトリ構成とClaude Codeの役割分担
Zephyrの公式ドキュメントは、アプリケーションの置き場所で3つの型を区別しています。zephyrリポジトリ内に置く「repository application」、westワークスペース内でzephyrリポジトリの外に置く「workspace application」、ワークスペースの外に独立して置く「freestanding application」です。freestandingの場合はZEPHYR_BASE環境変数を適切に設定する必要があります。
最小構成のアプリケーションは次の5ファイルで成立します。
<app>
├── CMakeLists.txt
├── app.overlay
├── prj.conf
├── VERSION
└── src
└── main.cCMakeLists.txtはビルドシステムの入口、app.overlayはハードウェア設定を上書きするdevicetreeオーバーレイ、prj.confはソフトウェア機能を有効化するKconfigフラグメントです。Claude CodeはEditツールでこの3種類の設定ファイルとsrc以下のソースコードを編集し、ビルド・書き込みの実行だけをBashツールに任せる分担がそのまま当てはまります。
ゼロから作る場合、CMakeLists.txtには最低限次の3行が要ります。
cmake_minimum_required(VERSION 3.28.0)
find_package(Zephyr)
project(my_zephyr_app)
target_sources(app PRIVATE src/main.c)find_package(Zephyr)がZephyrのビルドシステムを取り込み、CMakeターゲットappを作ります。project()の呼び出しはこの後に置く必要があり、順序を逆にするとZephyr側のproject(Zephyr-Kernel)と競合します。
ファイルを1から書かせるより、公式のexample-applicationリポジトリを土台にする方が要る設定の過不足を防げます。
cd <home>/zephyrproject
git clone https://github.com/zephyrproject-rtos/example-application my-app既存のワークスペース内にクローンすれば、カスタムボードポートやCI設定(twister)まで含んだ構成をそのまま流用できます。Claude Codeにはこのテンプレートの各ファイルをEditツールで書き換えさせ、CMakeLists.txtの構造は変えない指示にしておくと安全です。
westビルドをBashツールに任せる
Zephyrのビルドは2段階です。まずCMakeがbuild.ninjaなどのビルド定義を生成し、次にNinja(またはMake)がソースを実際にコンパイルします。標準のビルドツールであるwestはこの2段階をまとめて実行するラッパーで、内部でCMakeと生成したビルドツールを呼び出します。Windowsではジェネレータがninja固定で、makeは使えません。Linux/macOSはninjaとmakeのどちらも選べます。
west build -b reel_board samples/hello_world-bでボードを指定し、末尾にアプリケーションのディレクトリを渡します。設定ファイルを一時的に差し替えたいときはCONF_FILEを--区切りで渡します。
west build -b <board> -- -DCONF_FILE=prj.alternate.confBOARD・CONF_FILE・DTC_OVERLAY_FILEの3つの変数は、コマンドライン引数(-D)・環境変数・CMakeLists.txt内のset()の3通りで指定でき、この順に優先されます。Claude Codeに繰り返しビルドさせるときは、環境変数で固定するよりコマンドライン引数で明示させた方が、どの設定でビルドしたかが会話ログに残ります。
書き込みまで自動化すると確認プロンプトが挟まって流れが止まるので、プロジェクトの.claude/settings.jsonに許可ルールを追記しておきます。
{
"permissions": {
"allow": ["Bash(west build *)", "Bash(west flash *)"]
}
}Bash(west build *)は-t cleanや-t pristineのようなターゲット指定も含めて許可されます。ワイルドカードはサブコマンドの後ろに置くのが安全です。Bash(west *)まで広げるとwest initやwest updateまでノー確認で通るようになるため、ワークスペースの構成自体を変えるコマンドをどこまで許可するかは別途判断します。
QEMUエミュレータで実機なしに動作確認する
書き込み自体はwest flashをBashツールから実行すれば自動化できますが、ボードをUSBに接続する作業やリセットボタンの操作、シリアル出力の目視確認はClaude Codeの手が届かず、人の手を挟む工程として残ります。Zephyrはこの制約を、QEMUによるソフトウェアエミュレーションで部分的に埋められます。qemu_x86をx86向け、qemu_cortex_m3をArm Cortex-M3向けのエミュレータボードとして指定できます。
west build -b qemu_x86 samples/hello_world
timeout 15 west build -t run > qemu.log 2>&1 || truewest build -t run(ninja runでも同じ)を実行すると、Ctrl+A、Xでの手動停止が前提になるため、Claude Codeに読ませるときはtimeoutで打ち切ってログをファイルへ落とす形にします。実機を用意する前の段階でロジックの検証を進められるのは、コンパイル確認までで止まりやすい他の組み込み開発の構成との違いです。
devicetreeオーバーレイとKconfigの差分をClaude Codeに読ませる
ビルドが通らない、あるいは動作が想定と違うときの多くは、app.overlay(ハードウェア)とprj.conf(ソフトウェア)のどちらに手を入れるべきかを取り違えています。ビルドシステムはデフォルトでapp.overlayとprj.confをそれぞれ自動的に探すので、追加のオーバーレイやKconfigフラグメントを使うときだけDTC_OVERLAY_FILE・EXTRA_DTC_OVERLAY_FILE・CONF_FILE・EXTRA_CONF_FILEで明示します。複数ファイルを渡すときはセミコロン区切りです。
1つのソースを複数のボード・製品バリアント向けにビルドする場合はFILE_SUFFIXが使えます。FILE_SUFFIX=mouseを指定すると、prj.confの代わりにprj_mouse.confが優先され、該当するボード用オーバーレイ(例: boards/native_sim_mouse.overlay)が無ければnative_sim.overlayにフォールバックします。Claude Codeに新しいバリアントを追加させるときは、この命名規則をCLAUDE.mdに書いておくと生成されるファイル名が揃います。
実験的な機能を有効化する場合はCONFIG_WARN_EXPERIMENTAL=yをprj.confに加えておくと、対象のKconfigオプションを有効にした時点でCMakeの設定段階に警告が出ます。Claude Codeにビルドログを読ませるとき、この警告文字列を拾わせておけば、実験的APIへの依存を後から見つけやすくなります。
CLAUDE.mdとhooksでビルドループを安定させる
ボード名やリビジョン、ワークスペースの構成はプロジェクトごとに固定なので、CLAUDE.mdに書いておくとClaude Codeが毎回コマンドを組み立て直さずに済みます。
## ビルド規約
- ボードは `nrf9160dk@0.14.0/nrf9160/ns` のようにリビジョン付きで指定する
- 設定ファイルを変更したら `west build -t pristine` を実行してから再ビルドする
- devicetreeの差分は `app.overlay` に集約し、`DTC_OVERLAY_FILE` で分岐させない
- `west build` は必ず `west build ... 2>&1 | tee build/build.log` の形で実行し、出力を `build/build.log` に残す
- ビルド後は `<app>/build/zephyr/.config` と `build/build.log` の両方を確認する最後の1行は、ninjaの標準出力にエラーが出なくても.configの値が意図とずれていることがあるためです。.configにはビルドに使われた設定値、zephyr.elfには最終的な実行バイナリが入ります。
ビルドの成否をClaude Codeに正しく伝えるには、west build実行後にログを確認するフックを挟む構成が有効です。PostToolUseイベントのifフィールドはBashの引数まで見て判定できるので、west buildのときだけスクリプトを動かせます。
{
"hooks": {
"PostToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "scripts/check-zephyr-build.sh",
"if": "Bash(west build *)"
}
]
}
]
}
}PostToolUseフックはツール実行後に動くため、stderrをそのまま出してもexit 0で終わるとClaude Codeには届きません(デバッグログに残るだけです)。Claudeにエラー内容を読ませるにはexit 2で終了し、拾いたい行をstderrへ出す必要があります。ただしwest build自体は標準出力をファイルへ保存しないため、フック側でログを探す前に、CLAUDE.mdのビルド規約どおりteeでbuild/build.logに出力を落としておくのが前提です。check-zephyr-build.shは次のように、そのbuild/build.logからerror:またはwarning:を含む行を探し、見つかればその行をstderrへ出してexit 2する内容で足ります。
#!/usr/bin/env bash
LOG="build/build.log"
[ -f "$LOG" ] || exit 0
MATCHES="$(grep -E 'error:|warning:' "$LOG")"
if [ -n "$MATCHES" ]; then
echo "$MATCHES" >&2
exit 2
fi
exit 0west build自体はビルド失敗時にexit codeで検知できますが、devicetreeの不整合のように警告(warning:)止まりでビルド自体は成功する出力は、west buildのexit codeだけでは拾えません。ログの文字列で直接探すこの構成が効くのはそこです。
Arduino系記事との違いをどう使い分けるか
同じ組み込み開発でも、Claude CodeでArduinoを制御するやClaude CodeでESP32の開発とシリアルデバッグを進める手順で扱った構成とは、設定を当てる単位が異なります。
| 観点 | Arduino CLI(ESP32等) | Zephyr(west) |
|---|---|---|
| ハードウェア設定の変更方法 | Arduino CLI(ESP32等)--board-optionsや-Dマクロをcompileに渡す | Zephyr(west)app.overlay(devicetree)でボードの回路情報だけを上書きする |
| ソフトウェア機能の有効化 | Arduino CLI(ESP32等)スケッチのマクロ定義や#ifdef分岐 | Zephyr(west)prj.confのKconfigオプション(CONFIG_CPP=yなど)を追記する |
| 複数機種向けの構成切り替え | Arduino CLI(ESP32等)ボードごとにコマンドライン引数を変える | Zephyr(west)FILE_SUFFIXでprj_mouse.confのような派生ファイルを自動選択する |
| ビルドのやり直し | Arduino CLI(ESP32等)変更のたびにcompileから実行し直す | Zephyr(west)west build -t clean(部分)/-t pristine(全体)を使い分ける |
Arduino系はスケッチとboard-optionsで完結する分だけ着手が早く、Zephyrはファイル単位で設定を分離できる分だけ、複数機種・複数バリアントを1つのリポジトリで管理しやすくなっています。どちらを選ぶかは、対象がArduino Coreの支援するチップかどうかと、バリアント管理が要る規模かどうかで決まります。
よくあるつまずき
- Windowsで
makeが使えない: Windowsのジェネレータはninja固定です。Linux/macOSと同じ手順をwest config build.generator "Unix Makefiles"で共有しようとすると、Windows側だけ失敗します。 CONF_FILEで上書きしたはずの設定が反映されない:BOARD・CONF_FILE・DTC_OVERLAY_FILEは-D引数・環境変数・CMakeLists.txt内のset()の順に優先されます。シェルに古いCONF_FILE環境変数が残っていたり、CMakeLists.txtにset(CONF_FILE ...)が書かれていたりすると、-Dを付け忘れたコマンドではそちらの値が優先されて上書きが効きません。迷ったら常に-DCONF_FILE=...で明示指定します。- CMakeLists.txtを変更したのにビルドに反映されない: Zephyrのビルドシステムは変更箇所だけを再ビルドする設計です。まれに必要なファイルの再コンパイルを見落とし、古い設定のまま通ることがあるので、
west build -t pristine(.configまで含めて生成物を作り直す)で切り分けられます。west build -t cleanは.configを残したまま生成物だけ消すので、設定自体を疑うときはpristineが確実です。 - カスタムボードが見つからずエラーになる: 独自のボード定義を追加した場合、
-DBOARD_ROOT=<path>かCMakeLists.txt内のBOARD_ROOT指定が抜けていないか確認します。CMakeLists.txt内で指定する場合は絶対パスが必要です。 - ボードのリビジョン違いを指定できない:
<board>@<revision>の形式で指定します。例えばnrf9160dk@0.14.0/nrf9160/nsのように、リビジョンの後ろにボード修飾子を続けます。
こうした「ビルドしてログを読ませ、直す」の反復はClaude Code hooksでテストを自動実行するで扱うPostToolUseとStopの使い分けとも共通する組み方です。
まとめ
Claude CodeでZephyr RTOSアプリを開発する構成は、専用の統合機能に頼らず、CMakeLists.txt・prj.conf・app.overlayというファイル単位の役割分担をClaude Codeに明示することで成立します。westの3通りの変数指定(引数・環境変数・CMakeLists.txt)のどれを使っているかをCLAUDE.mdで固定し、ビルドログのエラーを拾うフックを挟んでおけば、Arduino系のスケッチベースの開発とは違う設定の当て方にも迷わず対応できます。