Apple SiliconにおけるHomebrewのベストプラクティス

# 開発環境としてのApple Silicon

みなさんApple SiliconなMac mini/Macbook Air/Macbook Proは買いましたか？

DockerやVirtualBoxが2020年11月18日時点では動作しないので開発環境としては絶望的ですが、Visual Studio Codeは86エミュレーションでも比較的快適に動いてくれるので、せめてCLIまわりは整備できないかなと思って試行錯誤した結果をまとめてみました。

# 概要

ターミナルからはアーキテクチャを意識せず混在して実行できるけど、2020年11月18日時点のHomebrewではarm64とx86_64でバイナリが分かれてしまうので、arm64のパッケージが入れられるものはそれを使うけど、入れられないものはx86_64のバイナリを使うための比較的快適な環境を構築する。

# Apple SiliconにおけるターミナルとHomebrewの動作について

* Rosetta2により子プロセスのアーキテクチャは混在できる
    * x86_64のzshからarm64のhtopを起動できる
* Homebrewのインストール先は、これまでは/usr/local配下だったが、arm64ビルドでは/opt/homebrew配下となる
    * 今後ユニバーサルバイナリが提供されるようになることに期待
* Homebrewにてインストールするarm64のパッケージは、現状バイナリが提供されていないのでソースからビルドされる
    * ビルドできるパッケージは一部に限られ、以下に確認済みのパッケージがまとまっている<br>
      [macOS 11\.0 Big Sur compatibility on Apple Silicon · Issue \#7857 · Homebrew/brew · GitHub](https://github.com/Homebrew/brew/issues/7857)
* x86_64のシェルからarm64のHomebrewのパッケージはインストールできない
    * 逆もしかり

# 対応方針

arm64ビルドのものを優先して使えるようにし、arm64ビルドのないコマンドはx64_64ビルドのコマンドを実行する。
（Rosetta2によるオーバーヘッドを減らすため、それでも十分良いパフォーマンスは出ているけど）

* arm64でビルドできるパッケージは、arm64版をインストールする
* ディフォルトのシェルはarm64ビルドのものを利用する
* ビルドに時間がかかって時間が惜しい場合やビルドできない場合は、x86_64のパッケージをインストールする

# 対応内容

## Homebrewのインストール

### x86_64

ターミナルが開いていれば閉じてからFinderからターミナルの「情報を見る」を開き「Rosettaを使用して開く」にチェックを付け実行したうえで、
[macOS（またはLinux）用パッケージマネージャー — Homebrew](https://brew.sh/index_ja)
どおりにインストールする。

```shell
$/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
```

### arm64

ターミナルが開いていれば閉じてからFinderからターミナルの「情報を見る」を開き「Rosettaを使用して開く」のチェックを外し実行したうえで、
[Installation — Homebrew Documentation](https://docs.brew.sh/Installation)
のAlternative Installs - Untar anywhereに記載のものを参考にインストールする。

```shell
$ cd /opt
$ sudo mkdir homebrew
$ sudo chown $USER:admin homebrew
$ curl -L https://github.com/Homebrew/brew/tarball/master | tar xz --strip 1 -C homebrew
```

## シェルのアーキテクチャ切り替えをコマンドで実現する

Homebrewのパッケージをインストールする際、ビルドターゲットがシェルの実行アーキテクチャに依存しているようで、異なるアーキテクチャのパッケージをインストール（もしくはビルド）することができない。

標準で入っているコマンドはUniversal対応となっている。

```shell:lipoで指定されたバイナリの対象アーキテクチャを表示
$ lipo -archs /bin/zsh
x86_64 arm64e
```

ユニバーサルバイナリとなっているコマンドについては、実行されているターミナルのアーキテクチャから自動的にシェルのアーキテクチャが決定される。
Homebrewのインストールでも操作していたように、Finderからターミナルの「情報を見る」にて「Rosettaを使用して開く」にチェックを付けることでx86_64のシェルを起動できる。

```shell:「Rosettaを使用して開く」が有効なターミナルのアーキテクチャ
$ uname -m
x86_64
```
```shell:「Rosettaを使用して開く」が無効なターミナルのアーキテクチャ
$ uname -m
arm64
```

しかしながら、アーキテクチャの切り替えが面倒なのと、ターミナル内でコマンドを実行する際にアーキテクチャを指定して実行する方法が現状ないと思われるので、Homebrewからarm64のzshとx86_64のzshの両方を導入する。（他のシェルを使いたい場合も同様）

### x86_64ビルドのzshパッケージをインストール

ターミナルが起動している場合には終了し、Finderからターミナルの「情報を見る」にて「Rosettaを使用して開く」にチェックを付けて起動したうえで以下のコマンドを実行する。

```shell
$ /usr/local/bin/brew install zsh
```

以降、x86_64版のシェルに切り替える場合には以下を実行する。

```shell
$ /usr/local/bin/zsh
$ uname -m
x86_64
```

### arm64ビルドのzshパッケージをインストール

ターミナルが起動している場合には終了し、Finderからターミナルの「情報を見る」にて「Rosettaを使用して開く」のチェックを外し起動したうえで以下のコマンドを実行する。

```shell
$ /opt/homebrew/bin/brew install zsh
```

以降、arm64版のシェルに切り替える場合には以下を実行する。

```shell
$ /opt/homebrew/zsh
$ uname -m
arm64
```

## パスの設定

```shell
export PATH=/opt/homebrew/bin:/usr/local/bin:$PATH
```

## パッケージのインストール

対応表（[macOS 11\.0 Big Sur compatibility on Apple Silicon · Issue \#7857 · Homebrew/brew · GitHub](https://github.com/Homebrew/brew/issues/7857)）で対応しているもので、比較的ビルドが早そうで軽量なパッケージはarm64のパッケージをインストールする。

### x86_64のパッケージをインストールする

```shell
$ uname -m
arm64
# シェルの実行アーキテクチャがarm64の場合はx86_64のシェルを起動する
$ /usr/local/bin/zsh
$ uname -m
x86_64
$ /usr/local/bin/brew install sl
$ which sl
/usr/local/bin/sl
$ sl
```

実行されているシェルのアーキテクチャを判別してbrewコマンドの実行アーキテクチャを切り替えることも考えたが、今のシェルはどっちのアーキテクチャなのか意識せず絶対混乱しそうなので、面倒だけどbrewコマンドのパスは明示的に指定することにした。

### arm64のパッケージをインストールする

```shell
$ uname -m
x86_64
# シェルの実行アーキテクチャがx86_64の場合はarm64のシェルを起動する
$ /opt/homebrew/zsh
$ uname -m
arm64
$ /opt/homebrew/bin/brew install sl
$ which sl
/opt/homebrew/sl
$ sl
```

x86_64のパッケージもインストールされているが、arm64のパッケージを優先して使うようにしたことでx86_64のパッケージが不要なら、必要に応じてアンインストールする。

```shell
$ uname -m
arm64
# シェルの実行アーキテクチャがx86_64の場合はarm64のシェルを起動する
$ /usr/local/bin/zsh
$ uname -m
x86_64
$ /usr/local/bin/brew uninstall sl
```

# おまけ: シェルプロンプトに実行中のシェルのアーキテクチャ名を表示する

実行中のアーキテクチャをunameでいちいち確認するのも面倒なので、プロンプトに表示しておく。

### zsh

```shell:~/.zshrc
export PROMPT="%n@%m(`uname -m`) %1~ %# "
```

### fish

```shell:~/.config/fish/functions/fish_prompt.fish
# 以下をfish_promptの関数内の冒頭に追加する、なければ定義する
echo -n "("(uname -m|sed "s/\r//")")"
```

# 最後にひとこと

絶対混乱する