はじめに
先日、最小 NIF を自作して AtomVM(ESP32-S3)上で動かしてみました。
その過程で、前提として知っておくべき用語やルールがいくつか出てきました。仕組みをしっかり理解していないと、NIF の実装自体が正しくても AtomVM 側に組み込めず、切り分けが難しくなりそうです。
忘れないうちに整理しておきます。
※ 写真はイメージです
概要
現時点で理解できた内容を、先に整理します。
- AtomVM(ESP32)のビルドは、ESP-IDF のコンポーネントの集まりとして構成されている
- 自作 NIF も ESP-IDF のコンポーネントとして追加する
- コンポーネントを追加する最小セット
CMakeLists.txt- (任意だが便利)
Kconfig
- NIF が読み込まれないときの主な原因
- リンクで落ちて、登録コードが最終成果物に含まれていない
-
CONFIG_...のシンボル名が、Kconfig と C 側の条件で一致していない - NIF 名の文字列(
"Elixir.Module:fun/arity")が一致していない
AtomVM 側のコンポーネントの置き場所
AtomVM の ESP32 向けプロジェクトは、だいたい次のディレクトリで作業します。
my_atomvm_esp32_path="$HOME/Projects/atomvm/AtomVM/src/platforms/esp32"
cd "$my_atomvm_esp32_path"
この配下に components/ があり、ESP-IDF のコンポーネントを追加します。主に2つのやり方が考えられます。
-
components/配下に追加したいコンポーネントの実体を置く -
components/配下へ追加したいコンポーネントをシンボリックリンクする
my_atomvm_esp32_path="$HOME/Projects/atomvm/AtomVM/src/platforms/esp32"
my_nif_src="$HOME/Projects/atomvm_hello_nif"
my_nif_dest="$my_atomvm_esp32_path/components/atomvm_hello_nif"
ln -sfn "$my_nif_src" "$my_nif_dest"
この形にしておくと、自作部品を AtomVM 本体から切り離して管理できます。
自作部品の構成
ESP-IDF のコンポーネントとして扱うために、最低限そろえておきたい構成を整理します。
CMakeLists.txt- (任意)
Kconfig - ソースコード
nifs/*.c
- ヘッダファイル
nifs/include/*.h
CMakeLists.txt
CMakeLists.txt でやりたいことは、主に 2 つです。
- C ファイルを ESP-IDF のコンポーネントとして登録する(
idf_component_register) - リンク時に登録コードが落ちないようにする(
--whole-archiveを効かせる)
まず、コンポーネント登録はこの形になります。
idf_component_register(
SRCS ${SAMPLE_APP_HELLO_COMPONENT_SRCS}
INCLUDE_DIRS "nifs/include"
PRIV_REQUIRES "libatomvm" "avm_sys"
)
リンク時に参照されていないと判断されると、NIF 登録コードごと最終成果物から消えることがあるようです。
自分の場合は、次のように --whole-archive を明示して回避できました。
idf_build_set_property(
LINK_OPTIONS "-Wl,--whole-archive;${CMAKE_CURRENT_BINARY_DIR}/lib${COMPONENT_NAME}.a;-Wl,--no-whole-archive"
APPEND
)
Kconfig
Kconfig は必須ではありませんが、idf.py menuconfig からコンポーネントの ON/OFF を切り替えられるので、切り分けが楽になります。
重要なのはKconfig の config FOO は C 側では CONFIG_FOO になることです。ここが一致していないと、設定を有効にしても登録コードがコンパイルされません。
たとえば Kconfig が次の定義なら、
config AVM_SAMPLE_APP_HELLO_NIF_ENABLE
bool "Enable sample hello NIF"
default n
C 側はこの名前になります。
#ifdef CONFIG_AVM_SAMPLE_APP_HELLO_NIF_ENABLE
REGISTER_NIF_COLLECTION(
sample_app_hello,
sample_app_hello_init,
sample_app_hello_destroy,
sample_app_hello_get_nif
)
#endif
自分はここが不一致になっていて、結果として REGISTER_NIF_COLLECTION(...) がビルドに入っていませんでした。menuconfig で有効にしても変化がない場合、まずここを疑うのが早そうです。
NIF 名
AtomVM が NIF を探すときのキーは文字列です。1 文字でも違うと解決されません。
基本形は次のとおりです。
"Elixir.モジュール名:関数名/アリティ"
おわりに
AtomVM(ESP32)は ESP-IDF のコンポーネントとして組み立てられているため、自作 NIF もその流儀に合わせて追加するのが近道でした。
そしてNIF がロードされない問題は、NIF 実装そのものよりも、部品の組み込み(リンク、Kconfig、名前)で起きることが多い、というのが今回の学びでした。
🎌 🎌 🎌
