Nervesで「nerves_system_*」の都合により「Major version mismatch between host and target Erlang/OTP versions」が発生する場合の対処方法


ナウでヤングでcoolなNervesを、久々に触った際に遭遇した事象。
Eralng/OTP22(erlang: 22.3.2、elixir: 1.10.3-otp-22 )の環境でNerves 1.6.3を利用していた際に、以下のエラーが発生。

** (Mix) Major version mismatch between host and target Erlang/OTP versions
  Host version: 22
  Target version: 23

メッセージを読むに、Erlang/OTPのバージョン23が要求されている。

しかし、Nerves 1.6.3のHexDocsのインストール箇所を確認したが、2020/7/29時点でasdfのインストールで指定されているのはErlang/OTP22であった。

※Nerves1.6.3のインストール手順:
https://hexdocs.pm/nerves/1.6.3/installation.html#all-platforms

ギャップがあったので、エラー発生理由と対処方法を調べてみた。

エラー発生理由

mix.exsに定義している「nerves_system_*」(nerves_system_rpi0など)で指定されているバージョンが、Erlang/OTP23を要求しているため。

nerves_system_rpi0のリリースタグを例に挙げると、v1.11.13v1.12.0では要求されるErlangのバージョンが異なっている。

  • v1.11.13(2020/5/27 10:00JSTリリース): Erlang 22.3.4.1
  • v1.12.0(2020/6/17 4:11JSTリリース):  Erlang/OTP 23.0.2

https://github.com/nerves-project/nerves_system_rpi4/releases/tag/v1.11.3
https://github.com/nerves-project/nerves_system_rpi4/releases/tag/v1.12.0
https://github.com/nerves-project/nerves_system_rpi0/blob/main/CHANGELOG.md

他の「nerves_system_*」のリリースも同様にv1.11とv1.12(nerves_system_bbbのみ、v2.6とv2.7)を境に、Erlang/OTP23が要求される。

そして、2020/07/29時点にNerves1.6.3でmix nerves.newでプロジェクトを作成すると、mix.exsで定義される「nerves_system_*」にはv1.12(と2.7)が指定されていた。

mix.exs(deps箇所の抜粋)

  # Run "mix help deps" to learn about dependencies.
  defp deps do
    [
      # Dependencies for all targets
      {:nerves, "~> 1.6.0", runtime: false},

〜中略〜

      # Dependencies for specific targets
      {:nerves_system_rpi, "~> 1.12", runtime: false, targets: :rpi},
      {:nerves_system_rpi0, "~> 1.12", runtime: false, targets: :rpi0},
      {:nerves_system_rpi2, "~> 1.12", runtime: false, targets: :rpi2},
      {:nerves_system_rpi3, "~> 1.12", runtime: false, targets: :rpi3},
      {:nerves_system_rpi3a, "~> 1.12", runtime: false, targets: :rpi3a},
      {:nerves_system_rpi4, "~> 1.12", runtime: false, targets: :rpi4},
      {:nerves_system_bbb, "~> 2.7", runtime: false, targets: :bbb},
      {:nerves_system_x86_64, "~> 1.12", runtime: false, targets: :x86_64},
    ]
  end

このため、mix deps.get後にmix firmwareを行うと、要求されるErlangのバージョンが23になるため、Erlang/OTP23でビルドしていないとエラーとなってしまう。

補足:エラーメッセージ全文

以下、自分の環境で出力されたエラーメッセージの全文。

** (Mix) Major version mismatch between host and target Erlang/OTP versions
  Host version: 22
  Target version: 23

This will likely cause Erlang code compiled for the target to fail in
unexpected ways.

The easiest way to resolve this issue is to install the same version of
Erlang/OTP on your host. See the Nerves installation guide for doing this
using the `asdf` version manager.

The Nerves System (nerves_system_*) dependency determines the OTP version
running on the target. It is possible that a recent update to the Nerves
System pulled in a new version of Erlang/OTP. If you are using an official
Nerves System, you can verify this by reviewing the CHANGELOG.md file that
comes with the release. Run 'mix deps' to see the Nerves System version and
go to that system's repository on https://github.com/nerves-project.

If you need to run a particular version of Erlang/OTP on your target, you can
either lock the nerves_system_* dependency in your mix.exs to an older
version. Note that this route prevents you from receiving security updates
from the official systems. The other option is to build a custom Nerves
system. See the Nerves documentation for building a custom system and then
run 'make menuconfig' and look for the Erlang options.

対処方法

以下、2種類の対処方法がある。

  • OTPのバージョンを上げる
  • OTPのバージョンを上げずに、nerves_system_*側で対応する

対処方法1: OTPのバージョンを上げる

こちらは正攻法。
Erlang23をインストールし、その後にElixirをOTP23でインストールする。
Erlangのインストールに時間がかかる点がネック。

asdfのlocalコマンドを利用すれば、特定のディレクトリ配下だけをErlang/OTPを変更可能なので、Nervesを利用する以外ではErlangのバージョンを上げたくない場合に活用できる。

$asdf install erlang 23.0.3
$asdf local erlang 23.0.3
$asdf install elixir 1.10.4-otp-23
$asdf local elixir 1.10.4-otp-23

対処方法2: OTPのバージョンを上げずに、nerves_system_*側で対応する

こちらは、暫定的な対応。
諸事情により、Erlang/OTPのバージョンを上げるのが難しい場合に実施する。

最終的には、Nervesのバージョンが上がっていくに連れてErlang/OTPのバージョンを上げざるを得なくなるが、バージョンアップよりは対応にかかる時間は(瞬間的には)少なく済む。

具体的な実施内容としては、以下の通り。

  1. mix.exsの「nerves_system_*」関連の指定バージョン値を変更する
  2. mix deps.update --allを実行する。(すでにmix deps.getを実施している場合のみ。未実施の場合は、mix deps.getで良い。)

まず、mix.exs内のdeps箇所で指定されている「nerves_system_*」のバージョンを、自分の環境のErlang/OTPのバージョンで動作可能なバージョンに変更する。指定バージョンは、CHANGELOG.mdを参考にして決める。
※以下の例では、Erlang 22.3.2に対応するために、「nerves_system_*」のバージョンをv1.11.1ベース(bbbのみ2.6)に修正する。

mix.exs(deps箇所の抜粋)

  # Run "mix help deps" to learn about dependencies.
  defp deps do
    [
      # Dependencies for all targets
      {:nerves, "~> 1.6.0", runtime: false},

〜中略〜

      # Dependencies for specific targets
      {:nerves_system_rpi, "~> 1.11.1", runtime: false, targets: :rpi},
      {:nerves_system_rpi0, "~> 1.11.1", runtime: false, targets: :rpi0},
      {:nerves_system_rpi2, "~> 1.11.1", runtime: false, targets: :rpi2},
      {:nerves_system_rpi3, "~> 1.11.1", runtime: false, targets: :rpi3},
      {:nerves_system_rpi3a, "~> 1.11.1", runtime: false, targets: :rpi3a},
      {:nerves_system_rpi4, "~> 1.11.1", runtime: false, targets: :rpi4},
      {:nerves_system_bbb, "~> 2.6", runtime: false, targets: :bbb},
      {:nerves_system_x86_64, "~> 1.11.1", runtime: false, targets: :x86_64},
    ]
  end

mix.exsの修正後、mix deps.getを実行する。
ただし、既にmix deps.getを実行している場合は、以下のようなエラーが出る。

** (Mix) Hex dependency resolution failed, change the version requirements of your dependencies or unlock them (by using mix deps.update or mix deps.unlock). If you are unable to resolve the conflicts you can try overriding with {:dependency, "~> 1.0", override: true}

そのため、エラーメッセージにあるように、mix deps.updateを実行する。
ただし、引数なしで実施すると、以下のように引数を指定するようにエラーメッセージが出る。

$ mix deps.update
** (Mix) "mix deps.update" expects dependencies as arguments or the --all option to update all dependencies

メッセージに従い、--allを付与して実行する。

$ mix deps.update --all

実行後、コンソールにダウングレードの情報が出力される。

Resolving Hex dependencies...
Dependency resolution completed:

〜(中略)〜

Downgraded:
  nerves_system_bbb 2.7.1 => 2.6.1
  nerves_system_br 1.12.0 => 1.11.2
  nerves_system_rpi 1.12.1 => 1.11.1
  nerves_system_rpi0 1.12.1 => 1.11.1
  nerves_system_rpi2 1.12.1 => 1.11.1
  nerves_system_rpi3 1.12.1 => 1.11.1
  nerves_system_rpi3a 1.12.1 => 1.11.1
  nerves_system_rpi4 1.12.1 => 1.11.2
  nerves_system_x86_64 1.12.1 => 1.11.1
New:
  nerves_system_linter 0.3.0
* Updating nerves_system_rpi (Hex package)
* Updating nerves_system_rpi0 (Hex package)
* Updating nerves_system_rpi2 (Hex package)
* Updating nerves_system_rpi3 (Hex package)
* Updating nerves_system_rpi3a (Hex package)
* Updating nerves_system_rpi4 (Hex package)
* Updating nerves_system_bbb (Hex package)
* Updating nerves_system_x86_64 (Hex package)
* Updating nerves_system_br (Hex package)
* Getting nerves_system_linter (Hex package)

Nerves environment
  MIX_TARGET:   rpi0
  MIX_ENV:      dev

Resolving Nerves artifacts...
  Resolving nerves_system_rpi0
  => Trying https://github.com/nerves-project/nerves_system_rpi0/releases/download/v1.11.1/nerves_system_rpi0-portable-1.11.1-38F032F.tar.gz
|==================================================| 100% (141 / 141) MB
  => Success
  Cached nerves_toolchain_armv6_rpi_linux_gnueabi
$

この後でmix firmwareを実行すれば、Major version mismatch between host and target Erlang/OTP versionsのエラーは発生しなくなる。

まとめ

Nerves1.6.3のリリース時期は「2020/5/7 3:52JST」のため、その後にリリースされた「nerves_system_*」の関連情報がHexDocsに反映されていないのは致し方ないとは言え、少しモヤっとはします。

ただし、GitHub上のdocs/Installation.md自体は、2020/6/17時点で更新はされていました。HexDocsへの反映が未だけのようです。
https://github.com/nerves-project/nerves/commit/50ef2a6933eeae4ca05254f88677badc70cc5c28#diff-045d5d7186acf52b74848a7dfbdde200

今後のバージョンアップでも同様なことが起こる可能性はあるので、Nerves自体のGitHubリポジトリとあわせてnerves_system_*のGitHubリポジトリをWatchしておくと良いかもしれません。

と、この記事を書いている間にも、2020/7/30 5:35JSTに「v2.0.0-rc.0」のTagが出ていましたね。
https://github.com/nerves-project/nerves_system_rpi0/releases/tag/v2.0.0-rc.0

正式版のv2.0.0のリリース、およびNervesのバージョンアップも近いかも。

参考情報:

ElixirでIoT#4.1:Nerves開発環境の準備(2020年7月版)