Customizing Your Own Nerves System (Nervesをカスタマイズする)

### この記事は何？（この項目のみ訳者独自記述）

この記事は、Nerves公式ドキュメントの「[Customizing Your Own Nerves System v1.7.9] (https://hexdocs.pm/nerves/customizing-systems.html)」の章の日本語訳です。対象はv.1.7.9で、2021/07/10前後に作業したものです。

https://hexdocs.pm/nerves/customizing-systems.html

勝手訳ですが、将来的に公式にマージできればいいなと思っています。
訳語はそれらしいものを選んでいますが、よく使われるものと異なっているものがある場合には、ご連絡等いただければ修正していきます。

# Customizing Your Own Nerves System

このガイドに従う前に、[The Anatomy of a Nerves System (Nervesシステムの内部構造)](https://hexdocs.pm/nerves/systems.html) について読んでおくとよいでしょう。

アプリケーションによっては、事前に構築されたNervesシステムがニーズを満たさない場合があります。たとえば、追加のLinuxパッケージを入れたい場合や、Nervesが[サポートするターゲット](https://hexdocs.pm/nerves/targets.html)のリストにないハードウェアで実行したい場合などです。ホストプラットフォーム間でビルドプロセスを一貫させるため、またLinux以外のホストでビルドを実行するために、Nervesは舞台裏でDockerコンテナを使用しています。これにより、Docker for MacまたはDocker for Windowsがこれらのプラットフォームにインストールされていれば、以下のステップは、あなたが開発に使用しているどのホストプラットフォームにも適用することが可能になります。

## Getting Setup to Build a System

ゼロからシステムを設計することもできますが、既存のシステムをコピーして変更し、公式リリースと区別するために名前を変更するのが最も簡単です。例えば、Raspberry Pi 3ボードを対象とする場合は、次のようにします。

`-b` フラグを忘れないようにしてください。mainから直接Cloning/Forkすることは安定していないと考えられます。

```
git clone https://github.com/nerves-project/nerves_system_rpi3.git custom_rpi3 -b v1.12.0
```

システムディレクトリの名前は自由ですが、この例では `custom_rpi3` とします。変更を加える前に、カスタムシステムをバージョンコントロールシステムに登録することをお勧めします。これにより、公式システムからのアップストリームの変更を後で簡単にマージすることができます。例えば、GitHubを使っていると仮定すると

```
# After creating an empty custom_rpi3 repository in your GitHub account
# GitHubアカウント上で空のcustom_rpi3レポジトリを作っておいてから

cd custom_rpi3
git remote rename origin upstream
git remote add origin git@github.com:YourGitHubUserName/custom_rpi3.git
git checkout -b main
git push origin main
```
次に、Mixプロジェクトのメタデータを調整するために、以下を更新します。

- ファイルの先頭にあるmixプロジェクトのモジュール名
- `@app`  の値を `custom_rpi3` に変更する。
- `@github_organization` の値を、GitHubのユーザー名または組織に設定します。

このファイルの構造については、[Mixプロジェクト](https://hexdocs.pm/mix/Mix.Project.html) の公式ドキュメントを参照してください。

```
# custom_rpi3/mix.exs
# =vvv= make sure to rename the module name
# defmodule NervesSystemRpi3.MixProject do
defmodule CustomRpi3.MixProject do
  use Mix.Project

  # =vvv= Rename `"nerves-project"` here to your user or ogranization name
  # @github_orgranization "nerves-project"
  @github_organization "YourGitHubUserOrOrganizationName"
  # =vvv= Rename `nerves_system_rpi3` here to `custom_rpi3`
  # @app :nerves_system_rpi3
  @app :custom_rpi3
end

# =^^^= The rest of this file remains the same
```

## Building the System

カスタムシステムディレクトリの準備ができたので、プロジェクトの `mix.exs` からそれを指すようにするだけです。この例では、`custom_rpi3` システムディレクトリが、nervesファームウェアプロジェクトのディレクトリと同じディレクトリにあると仮定しています。

```
~/projects
├── custom_rpi3
└── your_project
```

新しいプロジェクトを開始する場合は、1つのターゲットだけをサポートするように生成することができます。`rpi3`を`custom_rpi3`に更新します。

```
mix nerves.new your_project --target rpi3
```

```
#=vvv= Update your_project/mix.exs to accept your new :custom_rpi3 target

  # ...
  @all_targets [:rpi3, :custom_rpi3]
  #                    =^^^^^^^^^^=

  defp deps do
    [
      # Dependencies for all targets
      # ...

      # Dependencies for specific targets
      {:nerves_system_rpi3, "~> 1.6", runtime: false, targets: :rpi},
      {:custom_rpi3, path: "../custom_rpi3", runtime: false, targets: :custom_rpi3, nerves: [compile: true]}, # <===
    ]
  end
```

> 注意：nerves :[compile: true]オプションを依存関係に含めると、システムが自動的にコンパイルされるようになります。この動作をさせたくない場合はこのオプションを削除しますが、その場合にはファームウェアをビルドする前に、`mix compile`タスクでシステムを手動でコンパイルする必要があります。

`MIX_TARGET`にカスタムシステムを参照するように設定し、ファームウェアをビルドします。

```
cd ~/projects/your_project
export MIX_TARGET=custom_rpi3
mix deps.get
mix firmware
```

このプロセスは、初回は通常のファームウェアビルドよりもかなり時間がかかります（15～30分）。このプロセスが終了すると、公式の`rpi3`システムと同等のものを正常に構築できることが確認できます。カスタムシステムが構築された後は、アプリケーションの変更やファームウェアの再構築を通常通り行うことができます。カスタムシステムは、システムのソースプロジェクト自体に変更を加えた場合にのみ再構築されます。

## Buildroot Package Configuration

BuildrootはLinuxからしか使えないため、NervesはNervesシステム構成シェルと呼ばれる抽象化レイヤーを提供し、Linux以外のプラットフォームでLinuxベースのDockerコンテナを使うことで、LinuxとLinux以外の開発ホストで同じ手順を使えるようにしています。この環境にアクセスするには、カスタムシステムのソースディレクトリから[`mix nerves.system.shell`](https://hexdocs.pm/nerves_bootstrap/1.10.2/Mix.Tasks.Nerves.System.Shell.html) タスクを実行します。

```
$ mix deps.get
Mix environment
 MIX_TARGET:   custom_rpi3
 MIX_ENV:      dev

Running dependency resolution...
Dependency resolution completed:
# <-SNIP->
* Getting nerves (Hex package)
 Checking package (https://repo.hex.pm/tarballs/nerves-1.3.0.tar)
# <-SNIP->

$ mix nerves.system.shell
Mix environment
 MIX_TARGET:   custom_rpi3
 MIX_ENV:      dev

==> nerves
Compiling 25 files (.ex)
Generated nerves app

 Preparing Nerves Shell

Creating build directory...
Cleaning up...
Nerves  /nerves/build >
```
`Nerves /nerves/build > `シェルプロンプトに入ると、Nervesシステムをカスタマイズするためのワークフローは、 `make menuconfig` と `make savedefconfig` を使って、Nervesの外でBuildrootを使う場合と同じです。これは、Linuxと非Linuxの両方のプラットフォームで、効果的なサブシェルであることを覚えておいてください。設定の更新と、オプションとして「手動で」システムを再構築することが終わったら、`exit`を入力するか、`CTRL+D`を押すことで、通常のシェルに戻ることができます。

主なパッケージ設定のワークフローは、設定したい内容に応じて3つのカテゴリーに分かれています。

1. `make menuconfig` を実行して基本パッケージを選択する。
2. `make linux-menuconfig` で Linux カーネルとカーネルモジュールを変更する。
3. `make busybox-menuconfig` で、より多くのコマンドラインユーティリティを有効にする。

> 注：システム構成シェルの中で `make` を使って「手動」でシステムを構築することができます。これは、さまざまな変更を試しながら素早く反復するためです。プロジェクトでシステムを試す準備ができたら、シェルを終了して、プロジェクトのディレクトリから `mix firmware` で再構築を行います。そのため、Buildroot の使用経験があり、`make clean` ステップを省略できる場合を除き、`make` の前に常に `make clean` を実行することをお勧めします。

`menuconfig` インターフェースを終了すると、変更内容が一時的に保存されます。システムのソースディレクトリに戻して保存するには、以下の適切な手順に従ってください。

1 `make menuconfig`した場合
`make savedefconfig` を実行して、Systemの `nerves_defconfig` を更新します。

2 `make linux-menuconfig`した場合
カーネルの設定が完了したら、`make linux-update-defconfig`を使って、Linuxの設定をデフォルトの設定ファイルに保存します。保存先のファイルは、プロジェクトのルート(または作業しているカーネルのバージョン)にある `linux-4.9.defconfig` です。
> 注意: もし、あなたのシステムにまだカスタムのLinux設定がない場合は、 システムディレクトリにある新しいLinuxのdefconfigを指すように、 (`make menuconfig`を使って)Buildrootの設定を更新する必要があります。そのパスは、通常 `$(NERVES_DEFCONFIG_DIR)/linux-x.y_defconfig` のようなものです。

3　`make busybox-menuconfig`した場合
残念ながら、BusyBoxのdefconfigを簡単に保存する方法は今のところありません。代わりにしなければならないのは、BusyBoxの完全な設定を保存して、それを`nerves_defconfig`に含めるように設定することです。あなたがLinux以外のホストでDocker経由でNerves System Shellを使用していて、カスタムシステムのソースディレクトリが`custom_rpi3`という名前だとすると、以下のようにする必要があります（バージョン識別子はあなたにとって異なるかもしれません）。

```
cp build/busybox-1.27.2/.config /nerves/env/custom_rpi3/busybox_defconfig
```

Linux の設定と同様に、Buildroot の設定がまだの場合、カスタム設定を指すように更新する必要があります。これは、`make menuconfig` で Target Packages に移動し、BusyBox パッケージの Additional BusyBox configuration fragment files オプションを見つけることで実現できますが、このオプションは既に有効になっており、ベースコンフィグレーションが指定されているはずです。この例に沿って進めていくと、正しい設定値は以下のようになります。

```
${NERVES_DEFCONFIG_DIR}/busybox_defconfig
```

特にパッケージを追加する必要がある場合には、[Buildroot user manual](http://nightly.buildroot.org/manual.html)が非常に役立ちます。様々なNervesシステムのリポジトリには、多くの一般的な使用例があるので、それらもチェックしてみてください。

## Adding a custom Buildroot Package

Buildrootに含まれていない、[elixir_make](https://github.com/elixir-lang/elixir_make)でコンパイルするには複雑すぎるElixir以外のプログラムがある場合は、それをビルドする方法の説明をシステムに追加する必要があります。これは、「カスタムBuildrootパッケージ」と呼ばれ、Nervesシステムに追加するプロセスは、Buildrootとほぼ同じです。これは、Buildrootマニュアルの[新しいパッケージの追加](http://nightly.buildroot.org/manual.html#adding-packages)の章で説明されています。Nervesとの主な違いは、ディレクトリです。

このプロセスでは、パッケージをBuildrootにアップストリームでコントリビュートすることに意味があるかどうかを検討してください。

Nervesシステムには、カスタムシステムディレクトリのルートに以下のファイルが必要です。

1. `Config.in` - 各パッケージの `Config.in` ファイルを含みます。
2. `external.mk` - 各パッケージの `<パッケージ名>.mk` ファイルを含む。
3. `packages` - カスタムパッケージディレクトリを含むディレクトリ

`package`ディレクトリ内の各ディレクトリには、以下の2つのものが含まれています。

1. `Config.in` - パッケージ情報の定義
2. `<パッケージ名>.mk` - パッケージのビルド方法を定義します。

ですので、 `libfoo` パッケージをビルドしたい場合は、まず [`Config.in`](http://config.in/)と [`external.mk`](http://external.mk/)ファイルをシステムのベースディレクトリに作成します。

`/Config.in`:

```
menu "Custom Packages"
source "$NERVES_DEFCONFIG_DIR/packages/libfoo/Config.in"
endmenu
```

`/external.mk`:

```
include $(sort $(wildcard $(NERVES_DEFCONFIG_DIR)/packages/*/*.mk))
```

続いて、パッケージディレクトリとパッケージファイルを作成します。

```
mkdir -p packages/libfoo
touch packages/libfoo/Config.in
touch packages/libfoo/libfoo.mk
```

この時点で、これらのファイルに何を追加すべきかについては、Buildroot の公式ドキュメントに従う必要があります。最も簡単な方法は、Buildroot で類似のパッケージを見つけ、適切に名前を変更した上で内容をコピー/ペーストすることです。

## Creating an Artifact

Nervesシステムの構築は、多くのシステムリソースを必要とし、完成までに長い時間を要することがよくあります。Nervesシステムの構成に満足し、リリースの準備ができたら、アーティファクトを作成することができます。アーティファクトは、Nervesシステムのコンパイル済みバージョンで、 `mix deps.get`を呼び出すと取得できます。アーティファクトは、 `nerves_package`設定の `artifact_sites` リストで指定されたヘルパーの1つを使って取得しようとします。

現在、3種類のヘルパーがいます。

- `{:github_releases, "organization/repo"}`
- `{:github_api, "organization/repo", username: "", token: "", tag: ""}`
- `{:prefix, "url", opts \\ []}`

`artifact_sites`は、アーティファクトへの場所のパスを宣言するだけです。これは、アーティファクトの名前がNervesによって定義され、正しいものをダウンロードするために使われるからです。Nervesシステムのアーティファクト名は、 `<name>-portable-<version>-<checksum>.tar.gz`という構造になっています。ファイルの最後のチェックサムは、 `nerves_package` 設定の `チェックサム` リストで指定されたファイルとディレクトリの内容に基づいて計算されます。注意しなければならないのは、成果物の作成後にチェックサムファイルやディレクトリの内容を変更した場合、成果物は一致せず、使用できないということです。したがって、アーティファクトを作成する前に、まず `artifact_sites`を定義する必要があります。

アーティファクトを作成するには、プロジェクトをビルドして、カスタムNervesシステムのディレクトリ内から [`mix nerves.artifact`](<https://hexdocs.pm/nerves/Mix.Tasks.Nerves.Artifact.html>)を呼び出すだけです。例えば、システム名が `custom_rpi3` でバージョンが `0.1.0` の場合、現在の作業ディレクトリに `custom_rpi3-portable-0.1.0-ABCDEF0.tar.gz` のようなファイルができます。このファイルは、`artifact_sites`で指定した場所に置く必要があります。Github Releasesヘルパーを使用している場合は、Githubでタグからリリースを作成してから、ファイルをアップロードする必要があります。

これで、メインプロジェクトで  `:path` の依存関係を使う代わりに、 `:github` の依存関係を使えば、他の人との共有が容易になります。

```
# Update the `custom_rpi3` dep in your `deps/0` function.
{:custom_rpi3, github: "YourGitHubUserName/custom_rpi3", runtime: false, targets: :custom_rpi3}
```

また、[システムパッケージをhexに公開する](https://hex.pm/docs/publish)こともできます。この場合、`mix.exs`ファイルに何も変更を加える必要はありません。

```
mix hex.publish
```
メインプロジェクトに戻り、depsを更新します。

```
# make sure you check the version here.
{:custom_rpi3, "~> 1.7", runtime: false, targets: :custom_rpi3}
```

## Custom System Maintenance

Nervesシステムをカスタマイズし、成果物を作成し、パッケージを公開した後、おそらく元のシステムの最新の更新を追跡したいと思うでしょう。「Getting Setup to Build a System」で説明した`git`の手順を踏んでいれば、 `upstream`というリモートがあるはずです。これを確認するには、次のようにします。

```
$ git remote -v
origin git@github.com:YourGitHubUserName/custom_rpi3.git (fetch)
origin git@github.com:YourGitHubUserName/custom_rpi3.git (push)
upstream https://github.com/nerves-project/nerves_system_rpi3.git (fetch)
upstream https://github.com/nerves-project/nerves_system_rpi3.git (push)
```

システムをアップデートする準備ができたら(たとえばNervesが新しいバージョンを公開した後)、 `upstream`の変更をマージすればいいのです。例えば、 `v1.7.1`の `nerves_system_rpi3`で始めた場合、 `v1.7.2`が公開されたら、以下のようにしてカスタムシステムをアップグレードできます。

```
git fetch --all
git merge upstream/main
# Solve any merge conflicts
git push origin main
```

また、GitHubのインターフェイスを使って行うこともできます。

```
https://github.com/YourGitHubUserName/custom_rpi3/compare/main...nerves-project:main?expand=1
```