はじめに
記事Elixir NIFを使ってみるでNIFを使ってみました。
この記事では、NIFの動作を確認できたんですが、ちょっと心配な事があります。
Nervesの場合、ファームウエアの作成(=compile)はhostで行い、実行はtargetのマシーンで行われます。
hostでcompileするときに、targetのアーキテクチャでクロスコンパイルされる必要があるんですが、その考慮を何もしていません。
適切にクロスcompileされるか、確かめてみます
やってみる
プロジェクトを作成
mix nerves.new test001
mixのdepsにhello_nifを加える
{:hello_nif, git: "https://github.com/masahiro-999/hello_nif", branch: "main"}
mix deps.get
とmix firmware
を実行
hello_nifがコンパイルされています。
==> hello_nif
gcc -o multiply.so -shared -fPIC -I/home/masa/.asdf/installs/erlang/27.0.1/erts-15.0.1/include multiply.c
Compiling 1 file (.ex)
Generated hello_nif app
実行結果1
案の定、エラーが発生して、再起動を繰り返しました
multiply.soが含まれていない様子です。
クロスコンパイル以前に、パッケージとしての問題がありました。
修正
load_nifでカレントディレクトリのファイルを取得するようにしていたことが問題
multiply.soを prev ディレクトリに置く様に修正。
:code.priv_dir(:hello_nif)
を使って、場所を取得するように修正
def load_nif() do
:erlang.load_nif(:code.priv_dir(:hello_nif) ++ ~c"/multiply", 0)
end
実行結果2
mix firmware
を実行すると次のエラーが発生
Updating base firmware image with Erlang release...
scrub-otp-release.sh: ERROR: Unexpected executable format for '/home/masa/nerves/test001/_build/m5stack_core_mp135_dev/_nerves-tmp/rootfs_overlay/srv/erlang/lib/hello_nif-0.1.0/priv/multiply.so'
Got:
readelf:Advanced Micro Devices X86-64;0x0
Expecting:
readelf:ARM;0x5000400, Version5 EABI, hard-float ABI
This file was compiled for the host or a different target and probably
will not work.
Check the following:
1. If this file comes from a library, that library may be compiling to
/home/masa/.nerves/artifacts/nerves_system_m5stack_core_mp135-portable-0.0.7/scripts/scrub-otp-release.sh: line 153: deps: command not found
its source directory under . Manually clean up the source
directory and try building again.
2. Are you using a path dependency in your mix deps? If so, run
'mix clean' in that directory to avoid pulling in any of its
build products.
3. Did you recently upgrade or change your Nerves system? If so,
try cleaning and rebuilding this project and its deps.
4. Are you building outside of Nerves' mix integration? If so,
make sure that you've sourced 'nerves-env.sh'.
If you are very sure you know what you are doing, you may place an empty
file in the same directory as the offending file(s) called '.noscrub'.
This will explicitly disable scrubbing for that directory.
If you're still having trouble, please file an issue on Github
at https://github.com/nerves-project/nerves_system_br/issues.
ああ~~。やっぱりクロスコンパイルできてない事がわかりました。
Nervesで使えているライブラリーを真似する
正しい方法がわかりません。Nerves projectの方のパッケージを参考にすることにしました。
Circuits i2cのMakefileのMakefileを見てみます。
コンパイルするコマンドが$(CC)となっていました。
この変更で、ターゲットに適したコンパイラーが使われるようになり、エラーが解消されました。
PREFIX = $(MIX_APP_PATH)/priv
BUILD = $(MIX_APP_PATH)/obj
NIF = $(PREFIX)/multiply.so
SRC = c_src/multiply.c
ERLANG_INCLUDE_DIR = $(shell erl -eval 'io:format("~s", [code:lib_dir(erl_interface, include)])' -s init stop -noshell)
ERTS_INCLUDE_DIR = $(shell erl -eval 'io:format("~s", [code:root_dir() ++ "/erts-" ++ erlang:system_info(version) ++ "/include"])' -s init stop -noshell)
all: compile
compile:
mkdir -p $(PREFIX)
$(CC) -o $(NIF) -shared -fPIC -I$(ERTS_INCLUDE_DIR) $(SRC)
clean:
rm -f $(NIF)
実行結果
Core MP135(ARM CPU)で実行して、実行できました
iex(2)> HelloNif.multiply(2,3)
6
iex(3)>