ラズパイに向けてRustをクロスコンパイル！

# 追記
[こちら](tomoyuki-nakabayashi.github)に有志による和訳を見つけました。

環境構築は、こちらのほうを読んだほうがわかりやすいです。

こういうドキュメントの和訳は本当に助かります。ありがとうございます。

# 想定読者
- Rustが好きだ！Rustが使いたいんだ！でも、ラズパイに向けたクロスコンパイル方法がわからない！
- クロスコンパイルのやり方は知らなくても、どんなのかは知っている
- Dockerにアレルギー反応を起こさない
- アーキテクチャの名前にアナフィラキシー・ショックを起こさない
- ツールチェイン、リンカという言葉に拒絶反応を起こさない

という人向けの、初心者による初心者のための記事です。
（というか私の備忘録です。）

# 環境
**開発環境**
OS：Ubuntu 20.04 LTS
アーキテクチャ：x86_64

**実行環境**
ハードウェア：RaspberryPi4 typeB 4GB
OS：Raspbian(Version: May 2020)
アーキテクチャ：ARMv8

（2020/12/01;ラズバイのアーキテクチャを修正。ARMv8はARMv7と互換性[^1]があるためARMv7のソフトウェアを実行できます。）

[^1]: [マイナビニュース「ARM、64ビットアーキテクチャ「ARMv8」の概要を公開」](https://news.mynavi.jp/article/20111031-arm_v8/2)

# 目標
[rust-pushord](https://docs.rs/rust-pushrod/0.4.27/pushrod/)を用いたRust製GUIアプリケーションをRaspberryPi用にクロスコンパイルして、Raspbian上でも動かすこと。

# 流れ
1. [rustの公式Dockerイメージ](https://hub.docker.com/_/rust)を使って開発環境を整える
2. コンテナ内に「Hello World」を用意する。
3. CargoへARMv7向けにコンパイルするように設定する
4. ツールチェインとリンカを用意する
5. コンパイルの確認
6. [rust-pushord](https://docs.rs/rust-pushrod/0.4.27/pushrod/)の依存ライブラリを用意する（SDL2）
7. [rust-pushord](https://docs.rs/rust-pushrod/0.4.27/pushrod/)のexampleをコンパイルする
8. RaspberryPi上で実行する
9. 喜びの舞を踊る（*option*）

# 1. [rustの公式Dockerイメージ](https://hub.docker.com/_/rust)を使って開発環境を整える
Dockerのインストール等は割愛させていただきます。

インストールが終わったら早速、Rustの公式イメージゲットだぜ！

```bash:bash
docker container run -v ホスト:コンテナ -it --rm rust:latest /bin/bash
```
（注：もしかしたらsudoがいるかもね。）

将来的なことを考えるとDockerfileを用意したほうがいい気がするけど、ここでは「初心者による初心者のための」ということで作りません。

# 2. コンテナ内に「Hello World」を用意する。
初めに、コンテナ内で「Hello World」のコードを作り、確認のため普通（開発環境アーキテクチャ向け）に実行します。

```bash:bash
root@abcd12345:/# export USER=root
root@abcd12345:~# cd
root@abcd12345:~# cargo new hello
root@abcd12345:~# cd hello
root@abcd12345:~/hello# cargo run
```
これで「Hello World」が帰ってきたらOK。


# 3. CargoへARMv7向けにコンパイルするように設定する
次に「.cargo/config」を作成しクロスコンパイルをするための設定を記述します。


```bash:bash
root@abcd12345:~/hello# mkdir .cargo
root@abcd12345:~/hello# cat <<EOF > .cargo/config
[target.armv7-unknown-linux-gnueabihf]
linker = "arm-linux-gnueabihf-gcc"
EOF
```

これは
「cargoでtargetにarmv7-unknown-linux-gnueabihfが指定されたら、リンカにarm-linux-gnueabihf-gccを使ってね！」
とcargoにお願いするためのものです。

# 4. ツールチェインとリンカを用意する
さて、次に用意しないと行けないのが「ARMv7用の、ツールチェインとリンカ」です。

```bash:bash
# ツールチェインの追加
rustup target add armv7-unknown-linux-gnueabihf

# 備考：用意されているツールチェインは次のコマンドで表示できます
# rustup target list

# リンカの用意
apt install gcc-arm-linux-gnueabihf
```

# 5. コンパイルの確認
さぁ、準備ができたので、コンパイルがちゃんと通るか確かめて見ましょう！

```bash:bash
root@abcd12345:~/hello# cargo build --target=armv7-unknown-linux-gnueabihf
```

実際にARMv7上で動くかは、RaspbianのDockerイメージを使ってみたり、実機でテストしてください。

# 6. [rust-pushord](https://docs.rs/rust-pushrod/0.4.27/pushrod/)の依存ライブラリを用意する（SDL2）
CUIだと寂しいのでGUIアプリをコンパイルしてみましょう！

クロスプラットフォームでさくっとできるのに[rust-pushord](https://docs.rs/rust-pushrod/0.4.27/pushrod/)というのがあるので、これを使います。

任意の場所にGitHubからクローンして、コンパイルの設定を行い、exampleをコンパイルしましょう。
（2020/07/03追記）

```bash:bash
root@abcd12345:~# git clone https://github.com/KenSuenobu/rust-pushrod.git
root@abcd12345:~# cd rust-pushrod
root@abcd12345:~/rust-pushrod# mkdir .cargo 
root@abcd12345:~/rust-pushrod# cat <<EOF > .cargo/config
[target.armv7-unknown-linux-gnueabihf]
linker = "arm-linux-gnueabihf-gcc"
EOF
root@abcd12345:~/rust-pushrod# cargo build --target=armv7-unknown-linux-gnueabihf --example timer
```

残念なことに、失敗しますね。。かなしみ。


```エラーコード
... "-ldl" "-lutil"
  = note: /usr/lib/gcc-cross/arm-linux-gnueabihf/8/../../../../arm-linux-gnueabihf/bin/ld: cannot find -lSDL2
          /usr/lib/gcc-cross/arm-linux-gnueabihf/8/../../../../arm-linux-gnueabihf/bin/ld: cannot find -lSDL2_image
          /usr/lib/gcc-cross/arm-linux-gnueabihf/8/../../../../arm-linux-gnueabihf/bin/ld: cannot find -lSDL2_ttf
          collect2: error: ld returned 1 exit status
          

error: aborting due to previous error

error: could not compile `rust-pushrod`.

To learn more, run the command again with --verbose.

```

どうやら[rust-pushord](https://docs.rs/rust-pushrod/0.4.27/pushrod/)に必要なSDL2が足りていないようです。

では「依存関係を解決するためにapt install libsdl2-devすればいいのか」というと、そうは問屋がおろしません。

なぜなら、必要としているSDL2は「ARMv7向け」だからです。

ということで、次のようにしないといけません。

```bash:bash
# マルチアーキテクチャでarmhfを有効にする。
# また有効にしたあと、updateが必要です。（2020/07/03追記）
dpkg --add-architecture armhf
apt update

# 本当は、armhf向けのパッケージが必要とする必須の
# 基本的なarmhf向けのアプリをインストールする必要があります。
# しかし、Rustの公式Dockerイメージには、もうすでに入ってるのでスキップします。
# apt install crossbuild-essential-armhf

# 必要なSDL2のライブラリをインストール
apt install libsdl2-dev:armhf libsdl2-image-dev:armhf libsdl2-ttf-dev:armhf
```

これで依存ライブラリも揃いました。もう一度コンパイルして、うまく行くことを確認してみてください。

```bash:bash
root@abcd12345:~/rust-pushrod# cargo build --target=armv7-unknown-linux-gnueabihf --example timer
```

# 8. RaspberryPi上で実行する
コンパイルしてできたものを共有フォルダに移し、ホスト側に取り出し、RaspberryPiに持っていってください。

そして実行ししっかりと動くか確認してみましょう。
ついに、RustがRaspberryPi上で動くはずです。

（画像は明日くらいに載せます。（現在：2020/6/30））
（2020/07/03追記）
![Screenshot from 2020-07-03 17-11-04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/665043/078c604b-4810-30c7-eb15-af221f93c47d.png)

# 9. 喜びの舞を踊る（*option*）
♪(((*^-^)八(^∇^*)))♪

お疲れ様でした。今回、私自身が「Rustで組み込み！初クロスコンパイル！」ということで、情報を漁るも、簡単には見つからなかったため、記事にさせていただきました。（特にライブラリの依存関係を解消するあたり）

誰かの参考になれば幸いです。