M1 MacではRosetta 2を使って x86, x86_64 (以下x64) のIntelアプリやライブラリを実行できるのは有名だが、ターミナルを切り替えて使うことで、ARM非対応のツールやライブラリを使ってIntel版の開発を進めることができ、かつARM版と共存させることができる。
ARMとIntelのターミナルを切り替えて使う記事は既にあるものの、2021/2/5にARMに正式対応したHomebrew 3.0.0以前の記事しか見当たらず、Homebrew 3.0.0以降の正式対応後の手順と設定を改めてまとめてみることにした。
iTermをインストール
デフォルトのターミナルでも良いのだけれど、後述の見た目を分けることができなかったり、名称変更後のターミナルがSpotlightで呼び出しにくいなどで不便が多いので、iTermをインストールしている前提で以下説明する。
Intel版 (x64版) のターミナルを作る
/Applications/iTerm.app
をコピーして、「iTerm_x64.app」などに名称変更する。(ターミナル.appの場合は、/Applications/Utilities/Terminal.app
を複製。)
複製した方の「iTerm_x64.app」を右クリックして「情報を見る」を選択し、「Rosettaを使用して開く」にチェックを入れる。
ターミナルの見た目をそれぞれで変える
2つのターミナルをそのまま使うと非常に紛らわしいので、見た目を変える。.bash_profile
などから切り替えてもいいのだけれど、簡単な方法として、プロファイルの背景色を変えるのがおすすめ。
変えたい方のターミナルを一度起動して、メニューの iTerm > Preferences から、Profiles > Colors > Background で変更できる。(ターミナル.appだと、この設定を変えると両方に反映されてしまう。)
追記: プロファイルが一つだと、iTermでも設定が共有されてしまうことがあるようなので、ちゃんと切り替える方が良いみたい。2つプロファイル、例えば「ARM」と「Intel」を作成して、以下のように.bash_profileに追記することで自動切り替えを実現する。
alias change_profile='(){echo -e "\033]1337;SetProfile=$1\a"}'
if [ "$(uname -m)" = "arm64" ]; then
# arm64
change_profile ARM
else
# x86_64
change_profile Intel
fi
新規プロファイルを作る際は、Defaultのプロファイルを「Other Actions...」から複製して、Generalタブから名称変更するのが楽。
ちなみに上記設定を追記すると、最初の実行時に警告が出るので、Always Allowを選択する。
後でこの設定を変更したい場合は、Preferences > Advanced から、 Show only non-default valuesにチェックを入れ、Prevent control sequences from changing the current profile? の値を変更する (デフォルトはUnspecified)。
ちゃんとx64になっていることを確認
それぞれのターミナルを起動して、uname -m
を実行すると、ARM版ではarm64
、Intel版ではx86_64
と表示される。
Homebrewをそれぞれでインストールする
次に、各ターミナルを起動して、Homebrewをそれぞれでインストールする。(ARM版のHomebrewを既にインストール済みの場合は、Intel版のターミナルでのみインストールする。)
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
Homebrew 3.0.0以降でARM版に正式対応したので、ARM版ターミナルでは /opt/homebrew
、Intel版では /usr/local/
以下にそれぞれ自動判別してインストールされる。
最後にパスの設定を促されるのだが、今回は共存させる必要があるので、bashの場合は~/.profile
、zshの場合は ~/.zprofile
に以下のように設定する。(既にeval "$(/opt/homebrew/bin/brew shellenv)"
が記載されている場合は置き換える。)
if [ "$(uname -m)" = "arm64" ]; then
eval "$(/opt/homebrew/bin/brew shellenv)"
export PATH="/opt/homebrew/bin:$PATH"
else
eval "$(/usr/local/bin/brew shellenv)"
fi
これでそれぞれのターミナルを再起動すると、きちんとbrewがそれぞれのアーキテクチャで使えるようになっている。which brew
でbrewのパスを確認できる。
あとは、それぞれのbrewはまるで別々のパソコンで使っているかのように別のフォルダに棲み分けされるので、必要なツールやライブラリはそれぞれでインストールする。
注意として、パスが通っているとARMかIntelかに関係なく実行できるのが、便利だが紛らわしい。ARMとIntelが混ざっていても問題ない場合もあるが、きちんとフォルダと設定を分けておいたほうが無難。
if [ "$(uname -m)" = "arm64" ]; then
# arm用の設定...
else
# intel用の設定...
fi
*envを棲み分けする
ARMとIntelで設定を分けたほうがいい具体例として、pyenv、nodenv、rbenvなどの*envがある。
anyenvの場合
自分の場合はこれらをまとめて設定できるanyenvを使っているが、何も考えずに使ってしまうと~/.anyenv
に両方混ざっておかしなことになるので、例えば以下のようにする。
git clone https://github.com/anyenv/anyenv ~/.anyenv_arm64
cp -r ~/.anyenv_arm64 ~/.anyenv_x64
if [ "$(uname -m)" = "arm64" ]; then
# arm64
export ANYENV_ROOT="$HOME/.anyenv_arm64"
export PATH="$HOME/.anyenv_arm64/bin:$PATH"
eval "$(anyenv init -)"
else
# x86_64
export ANYENV_ROOT="$HOME/.anyenv_x64"
export PATH="$HOME/.anyenv_x64/bin:$PATH"
eval "$(anyenv init -)"
fi
pyenvの場合
(anyenvを使っている場合は前述の設定だけで問題ないので、追加設定は不要。)
git clone https://github.com/pyenv/pyenv ~/.pyenv_arm64
cp -r ~/.pyenv_arm64 ~/.pyenv_x64
if [ "$(uname -m)" = "arm64" ]; then
# arm64
export PYENV_ROOT="$HOME/.pyenv_arm64"
export PATH="$HOME/.pyenv_arm64/bin:$PATH"
eval "$(pyenv init -)"
else
# x86_64
export PYENV_ROOT="$HOME/.pyenv_x64"
export PATH="$HOME/.pyenv_x64/bin:$PATH"
eval "$(pyenv init -)"
fi
nodenvやrbenvなどの場合も、node-buildやruby-buildのcloneが追加で必要なくらいで、設定は同様。
ARMとIntelが混ざってしまった場合
前述の通り、パスが通っているとARMかIntelか関係なく呼び出せてしまうので、アーキテクチャが混ざっていると困まったことがよく起こる。
コマンドの場合は、which
やwhere
などでどれが呼ばれているかを特定して、場合によってlipo -info
やotool
を使ってアーキテクチャを判断してケースバイケースで対処。
ライブラリの場合も同様で、brew doctor
やlocate
などのコマンドで判別しつつ、C_INCLUDE_PATH
やLD_LIBRARY_PATH
などの環境変数を切り分けたり、brewなどで調整したりする。
いずれにしても、片方のアーキテクチャのみでインストールされているツールやライブラリが問題になることが多いので、きちんと切り分けていれば大丈夫。
arch -x86_64 をうまく使うと便利
ちなみに一度Rosettaがインストールされていれば、
$ arch -x86_64 uname -m
のように arch -x86_64
をつけてコマンドを実行すればRosettaで実行でき、ちょっとしたことはこれで十分。(逆にARM64で実行したい場合は arch -arm64e
。 )
この方法を使えば、ターミナルごと分けなくても、
$ arch -x86_64 zsh
とか
$ arch -x86_64 bash
でRosettaのシェルに切り替えることもできる。これでも今までの設定はちゃんと読み込まれるので、どちらがいいかは好み。(ARCHでシェルを切り替える方が、ターミナルのGUI自体はネイティブに実行される分、電池消費はいいかもしれない。)