Buildroot の使い方 - br2-external 編

[「Buildroot の使い方 - 基本編」](https://qiita.com/pu_ri/items/75c80e388c79fe0d3f0b)では Raspberry Pi 3 用の実行環境を構築するための最低限の手順を説明しました。
今回はその内容をカスタマイズする方法について説明します。

Buildroot が提供するカスタマイズ内容には以下のものがあります。

* Buildroot 自体の設定
* Linux Kernel、BusyBox 等の設定
* ターゲットファイルシステムの内容

カスタマイズを行う際はそのためのファイルをどこかに保存する必要があります。
どこに保存しているかは Buildroot の設定の中に記述します。
また、カスタマイズのためのファイルは Buildroot ツリーの中に置くこともできますし外側に置くこともできます。
Buildroot ツリーの外側に置く場合は br2-external という機能を使うことになります。
ここでは特に br2-external の使い方を説明します。


**ここで説明するのは私が使用したことがあるもののみで全てを網羅するものではありません。**

カスタマイズ用ディレクトリツリー
=============================

公式ドキュメントから引用したカスタマイズファイルを配置するディレクトリツリー構成を以下に記します。

```
+-- board/
|   +-- <company>/
|       +-- <boardname>/
|           +-- linux.config
|           +-- busybox.config
|           +-- <other configuration files>
|           +-- post_build.sh
|           +-- post_image.sh
|           +-- rootfs_overlay/
|           |   +-- etc/
|           |   +-- <some file>
|           +-- patches/
|               +-- foo/
|               |   +-- <some patch>
|               +-- libbar/
|                   +-- <some other patches>
|
+-- configs/
|   +-- <boardname>_defconfig
|
+-- package/
|   +-- <company>/
|       +-- Config.in (if not using a br2-external tree)
|       +-- <company>.mk (if not using a br2-external tree)
|       +-- package1/
|       |    +-- Config.in
|       |    +-- package1.mk
|       +-- package2/
|           +-- Config.in
|           +-- package2.mk
|
+-- Config.in (if using a br2-external tree)
+-- external.mk (if using a br2-external tree)
```

br2-external を使用する場合は \<company\> や \<boardname\> は不要なので以下のように簡略化できます。

```
+-- board/
|   +-- linux.config
|   +-- busybox.config
|   +-- <other configuration files>
|   +-- post_build.sh
|   +-- post_image.sh
|   +-- rootfs_overlay/
|   |   +-- etc/
|   |   +-- <some file>
|   +-- patches/
|       +-- foo/
|       |   +-- <some patch>
|       +-- libbar/
|           +-- <some other patches>
|
+-- configs/
|   +-- <boardname>_defconfig
|
+-- package/
|   +-- package1/
|   |    +-- Config.in
|   |    +-- package1.mk
|   +-- package2/
|       +-- Config.in
|       +-- package2.mk
|
+-- Config.in (if using a br2-external tree)
+-- external.mk (if using a br2-external tree)
```

br2-external で使用するディレクトリツリーのことを **br2-external tree** と呼びます。

br2-external tree には最低限以下の3つのファイルを置く必要があります。

* external.desc
* external.mk
* Config.in 

## external.desc
external.desc には br2-external tree の名前(name)と概要(desc)を記述します。

例えば以下のように記述します。

```
name: RPI3_EX
desc: Raspberry Pi 3 external tree.
```

### name
name は ASCII 文字の **[A-Za-z0-9_]** のみで構成する必要があります。
name に指定した文字列は br2-external 関連の変数名の一部に使用されます。
例えば **BR2_EXTERNAL_$(NAME)_PATH** という変数があり、上記例の場合だと以下のように変換されます。

```
BR2_EXTERNAL_RPI3_EX_PATH
```

この変数は Kconfig ファイル内や Makefile から参照できるようになります。

### desc
desc には br2-external tree の概要を記述します。
使用できる文字の特に制限はなく自由に記述できます。(40文字以下が推奨されています)
desc の内容は defconfig ファイルの一覧を表示した際や menuconfig に表示されます。
また、desc の内容は **BR2_EXTERNAL_$(NAME)_DESC** 変数で参照することができます。


## externa.mk, Config.in

これらのファイルはパッケージを追加する際に使用します。
パッケージを追加しない場合は中身は空にしておきます。
ただし、ファイル自体は存在する必要があります。
これらについての説明は割愛します。(実は使ったことがない...)


## configs/ ディレクトリ

**configs/** ディレクトリには **defconfig** ファイルを置くことができます。
ここに置いた defconfig ファイルは `make list-defconfigs` コマンドで出力される一覧に表示されます。
また、`make <name>_defconfig` のように make コマンドを実行するとここに置いた **\<name\>_defconfig** ファイルが読み込まれます。

## rootfs_overlay

Buildroot によって生成されたターゲットファイルシステムの内容の一部のファイルを上書きしたり、ファイルを追加したい場合があります。
そんな場合は **Root filesystem overlays(以降 rootfs_overlay)** の仕組みが使えます。
これは **BR2_ROOTFS_OVERLAY** という変数に上書きや追加をしたファイルを配置したディレクトリを指定しておくと Buildroot によるビルド完了後にそれらのファイルがターゲットファイルシステムにコピーされるというものです。

## post build script

Buildroot によって生成されたターゲットファイルシステムから一部のファイルを削除したり変更したりしたい場合があります。
そんな場合は **Post-build scripts** の仕組みが使えます。

**BR2_ROOTFS_POST_BUILD_SCRIPT** にスクリプトファイルを指定しておくと、ビルドが完了してファイルシステムイメージの生成を行う前に実行させることができます。
今回の説明では割愛します。

br2-external 環境の構築
======================

ここからは実際に br2-external 環境を構築しながら説明していきます。

Raspberry Pi 3 用のイメージを作成するので、まず Raspberry Pi 3 の defconfig を読み込んでおきます。

```
cd buildroot-2017.02.7
make raspberrypi3_defconfig
```


## br2-external tree 作成

br2-external tree として使用するディレクトリツリーを作成します。
今回は buildroot ディレクトリと同階層に以下のような構成でディレクトリツリーを構成することにします。

```
buildroot-2017.02.7/
ex_rpi3/
+-- board/
|   +-- rpi3_ex1/
|       +-- busybox.config
|       +-- overlay/
|           +-- etc/
|           |  +-- fstab
|           +-- usr/
|               +-- local/
|                   +-- bin/
|                       +-- hello
+-- configs/
|   +-- rpi3_ex_defconfig
+-- Config.in
+-- external.mk
+-- external.desc
```

external.desc の内容は前述のまま以下の内容にします。

```
name: RPI3_EX
desc: Raspberry Pi 3 external tree.
```

external.mk と Config.in は空のままにします。
それ以外についてはおいおい説明していきます。


## br2-external の有効化

br2-external は `make` を実行する際に **BR2_EXTERNAL** 変数に **br2-external tree** のパスを指定することで有効になります。
例えば以下のように実行します。

```
make list-defconfigs BR2_EXTERNAL=../ex_rpi3
```

**BR2_EXTERNAL** に指定した内容は output ディレクトリの **.br-external.mk** ファイルに保存され、以降の `make` では BR2_EXTERNAL を指定しなくても自動的に参照されるようになります。
尚、buildroot トップディレクトリ直下にも **.br-external.mk** ファイルがありますがこれは更新されません。(最初これに気付かず暫く悩みました)


**output/.br-external.mk** の内容は以下のようになっています。
※`<your work dir>` は ex_rpi3/ が存在するディレクトリのフルパス

```
$ cat output/.br-external.mk 
#
# Automatically generated file; DO NOT EDIT.
#

BR2_EXTERNAL ?= <your work dir>/ex_rpi3
BR2_EXTERNAL_NAMES = 
BR2_EXTERNAL_DIRS = 
BR2_EXTERNAL_MKS = 

BR2_EXTERNAL_NAMES += RPI3_EX
BR2_EXTERNAL_DIRS += <your work dir>/ex_rpi3
BR2_EXTERNAL_MKS += <your work dir>/ex_rpi3/external.mk
export BR2_EXTERNAL_RPI3_EX_PATH = <your work dir>/ex_rpi3
export BR2_EXTERNAL_RPI3_EX_DESC = Raspberry Pi 3 external tree.
```

## make menuconfig

**BR2_EXTERNAL** を設定している状態で `make menuconfig` を実行すると下端に **External options** の項目が表示されるようになります。

```
  +------------------ Buildroot 2017.02.9 Configuration -------------------+
  |  Arrow keys navigate the menu.  <Enter> selects submenus ---> (or      |
  |  empty submenus ----).  Highlighted letters are hotkeys.  Pressing <Y> |
  |  selects a feature, while <N> excludes a feature.  Press <Esc><Esc> to |
  |  exit, <?> for Help, </> for Search.  Legend: [*] feature is selected  |
  | +--------------------------------------------------------------------+ |
  | |        Target options  --->                                        | |
  | |        Build options  --->                                         | |
  | |        Toolchain  --->                                             | |
  | |        System configuration  --->                                  | |
  | |        Kernel  --->                                                | |
  | |        Target packages  --->                                       | |
  | |        Filesystem images  --->                                     | |
  | |        Bootloaders  --->                                           | |
  | |        Host utilities  --->                                        | |
  | |        Legacy config options  --->                                 | |
  | |        External options  --->                                      | |
  | |                                                                    | |
  | +--------------------------------------------------------------------+ |
  +------------------------------------------------------------------------+
  |        <Select>    < Exit >    < Help >    < Save >    < Load >        |
  +------------------------------------------------------------------------+
```

External options メニューに入ると設定している br2-external tree の概要とパスが表示されます。


## Busybox の設定

Busybox の設定は以下のコマンドで行うことができます。

```
make busybox-menuconfig
```

その際に設定した内容は **output/build/busybox-1.26.2/.config** に保存されます。
しかし、`make clean` を実行すると **output/** ディレクトリ以下は削除されてしまいます。
`make busybox-update-config` を実行することで Busybox のコンフィグ内容を保存することができます。
保存先は **BR2_PACKAGE_BUSYBOX_CONFIG** に設定されているパスになります。

**BR2_PACKAGE_BUSYBOX_CONFIG** は `make menuconfig` の **Target package --->** の **BusyBox configuration file to use?** で設定できます。
br2-external tree のパスは **BR2_EXTERNAL_RPI3_EX_PATH** で参照できるので以下のように設定することができます。

```
  +------------------ BusyBox configuration file to use? -------------------+
  |  Please enter a string value. Use the <TAB> key to move from the input  |
  |  field to the buttons below it.                                         |
  | +---------------------------------------------------------------------+ |
  | |$(BR2_EXTERNAL_RPI3_EX_PATH)/board/rpi3_ex1/busybox.config           | |
  | +---------------------------------------------------------------------+ |
  |                                                                         |
  +-------------------------------------------------------------------------+
  |                         <  Ok  >      < Help >                          |
  +-------------------------------------------------------------------------+

```

`make menuconfig` にてこの設定をして変更内容を保存した後で以下のコマンドを実行します。

```
make busybox-update-config
```

これで以下のファイルに Busybox の設定が保存され、以降 Busybox のビルド時はこのファイルが読み込まれます。

```
ex_rpi3/
+-- board/
|   +-- rpi3_ex1/
|       +-- busybox.config
```

## rootfs_overlay

rootfs_overlay ディレクトリにターゲットファイルシステムを上書きするファイルを追加します。
ここでは以下の2点を追加します。

* /etc/fatab の上書き
* /usr/local/bin/hello の追加  
  (hello は **Hello, world!** と出力するだけの自作スクリプトです)

これらのファイルをターゲットファイルシステムに入れるために以下のように配置します。

```
ex_rpi3/
+-- board/
|   +-- rpi3_ex1/
|       +-- overlay/
|           +-- etc/
|           |  +-- fstab
|           +-- usr/
|               +-- local/
|                   +-- bin/
|                       +-- hello
```

**BR2_ROOTFS_OVERLAY** に **overlay/** ディレクトリのパスを以下のように指定します。

```
BR2_ROOTFS_OVERLAY="$(BR2_EXTERNAL_RPI3_EX_PATH)/board/rpi3_ex1/overlay/"
```

**BR2_ROOTFS_OVERLAY** は `make menuconfig` の **System configuration --->** の **Root filesystem overlay directories** から設定できます。

```
  +------------------ Root filesystem overlay directories ------------------+
  |  Please enter a string value. Use the <TAB> key to move from the input  |
  |  field to the buttons below it.                                         |
  | +---------------------------------------------------------------------+ |
  | |$(BR2_EXTERNAL_RPI3_EX_PATH)/board/rpi3_ex1/overlay/                 | |
  | +---------------------------------------------------------------------+ |
  |                                                                         |
  +-------------------------------------------------------------------------+
  |                         <  Ok  >      < Help >                          |
  +-------------------------------------------------------------------------+
```

## br2-external 用 defconfig の作成

ここまでの設定内容を defconfig として保存します。

`make savedefconfig` を実行すると現在の設定からデフォルト設定値を省いた内容が **defconfig** という名前で保存されます。
その際に **BR2_DEFCONFIG** 変数に出力先ファイルパスを指定していると指定されたファイルに保存されます。

```
make savedefconfig BR2_DEFCONFIG='$(BR2_EXTERNAL_RPI3_EX_PATH)/configs/rpi3_ex_defconfig'
```

尚、**BR2_DEFCONFIG** 自体は defconfig には保存されません。.config には保存されます。
また、**BR2_EXTERNAL_RPI3_EX_PATH** を指定していますが、.config には展開後の実際のディレクトリパスが格納されます。
この設定値は `make menuconfig` 実行時の **Build options --->** の **Location to save buildroot config** に表示されますが、こちらも展開後のディレクトリパスになっています。


出力された defconfig の内容は以下のようにデフォルト設定から変更した差分のみになっています。

```
BR2_arm=y
BR2_cortex_a7=y
BR2_ARM_FPU_NEON_VFPV4=y
BR2_PACKAGE_HOST_LINUX_HEADERS_CUSTOM_4_4=y
BR2_TOOLCHAIN_BUILDROOT_CXX=y
BR2_SYSTEM_DHCP="eth0"
BR2_ROOTFS_OVERLAY="$(BR2_EXTERNAL_RPI3_EX_PATH)/board/rpi3_ex1/overlay/"
BR2_ROOTFS_POST_BUILD_SCRIPT="board/raspberrypi3/post-build.sh"
BR2_ROOTFS_POST_IMAGE_SCRIPT="board/raspberrypi3/post-image.sh"
BR2_ROOTFS_POST_SCRIPT_ARGS="--add-pi3-miniuart-bt-overlay"
BR2_LINUX_KERNEL=y
BR2_LINUX_KERNEL_CUSTOM_GIT=y
BR2_LINUX_KERNEL_CUSTOM_REPO_URL="https://github.com/raspberrypi/linux.git"
BR2_LINUX_KERNEL_CUSTOM_REPO_VERSION="1ebe8d4a4c96cd6a90805c74233a468854960f67"
BR2_LINUX_KERNEL_DEFCONFIG="bcm2709"
BR2_LINUX_KERNEL_DTS_SUPPORT=y
BR2_LINUX_KERNEL_INTREE_DTS_NAME="bcm2710-rpi-3-b"
BR2_PACKAGE_BUSYBOX_CONFIG="$(BR2_EXTERNAL_RPI3_EX_PATH)/board/rpi3_ex1/busybox.config"
BR2_PACKAGE_RPI_FIRMWARE=y
BR2_TARGET_ROOTFS_EXT2=y
BR2_TARGET_ROOTFS_EXT2_4=y
# BR2_TARGET_ROOTFS_TAR is not set
BR2_PACKAGE_HOST_DOSFSTOOLS=y
BR2_PACKAGE_HOST_GENIMAGE=y
BR2_PACKAGE_HOST_MTOOLS=y
```

ほとんどが Raspberry Pi 3 用の設定で以下の2行が今回追加で設定したものになります。

```
BR2_ROOTFS_OVERLAY="$(BR2_EXTERNAL_RPI3_EX_PATH)/board/rpi3_ex1/overlay/"
BR2_PACKAGE_BUSYBOX_CONFIG="$(BR2_EXTERNAL_RPI3_EX_PATH)/board/rpi3_ex1/busybox.config"
```

## make list-defconfigs

br2-external 用の defconfig を保存した後で、`make list-defconfigs` を実行すると、末尾に **BR2_EXTERNAL** に指定したディレクトリの **configs/** ディレクトリの defconfig ファイルが表示されます。

```
$ make list-defconfigs
Built-in configs:
    :
    :
  zynq_zed_defconfig                  - Build for zynq_zed
  zynq_zybo_defconfig                 - Build for zynq_zybo

External configs in "Raspberry Pi 3 external tree.":
  rpi3_ex_defconfig                   - Build for rpi3_ex
```

## br2-external を使ったビルド

ここまでで br2-external に必要なものができました。
あとは、以下のコマンドを実行することでカスタマイズした設定での Raspberry Pi 3 用イメージが出来上がります。

```
$ cd buildroot-2017.02.7/
$ make list-defconfigs BR2_EXTERNAL=../ex_rpi3
$ make rpi3_ex_defconfig
$ make
```

## Buildroot がアップデートされた場合

Buildroot のバージョンがアップデートされた場合は新しいものをダウンロードしてきて以下のコマンドを実行することで新しいバージョンの環境で Raspberry Pi 3 用のイメージがビルドできます。

```
$ tar xf buildroot-2017.02.9.tar.bz2
$ cd buildroot-2017.02.9/
$ make list-defconfigs BR2_EXTERNAL=../ex_rpi3
$ make rpi3_ex_defconfig
$ make
```