GNU coreutils を入れてる M1 Mac で Python 3.x のビルドが通らないのをなんとかする。

環境

  • M1 MacBook Air
  • macOS Big Sur 11.5.2
# GNU 版での表示
$ uname -a
Darwin MBA101.local 20.6.0 Darwin Kernel Version 20.6.0: Wed Jun 23 00:26:27 PDT 2021; root:xnu-7195.141.2~5/RELEASE_ARM64_T8101 arm64 arm64 MacBookAir10,1 Darwin

結論

記事タイトルで半分くらい言っているのだが、 M1 搭載機では GNU 版 uname の出力が BSD 版 uname と異なっており、 config.guess にてその出力を利用しているのが原因だった。

わざわざ GNU 版 coreutils なんかを入れて普段使いしてる人間は少ない模様。ググっても困ってる人がそもそも少ないような感じだった。

pyenv のイシュー欄での このコメント を見付けられなかったらもっと時間かかったと思う。

解決方法 1 (多分こっちの方がいい)

  • 一時的に GNU 版 coreutils を使わないようにしてビルドする。

ワンライナーで PATH を弄るならこんな感じ。

$ export PATH=$(printenv PATH | tr ':' '\n' | grep -v gnubin | tr '\n' ':' | rev | cut -d ':' -f2- | rev)

解決方法 2 (こういうのもある)

  • pyenv なら PYTHON_CONFIGURE_OPTS 環境変数の指定で ./configure--build を渡せるようだ。
    • python-build で使える特別な環境変数は こちら に詳しい。
    • PYTHON_CONFIGURE_OPTS="--build=arm-apple-darwin$(/usr/bin/uname -r)" pyenv install 3.9.6
    • config.guess をざっと読んだ限り、多分渡すのは arm だと思うが aarch64 を渡しても手元ではビルド出来たし、どちらでも Mach-O 64-bit executable arm64 のバイナリが出てくる。
    • 記事執筆時点で /usr/bin/uname -r で取れるカーネルバージョンの指定は 20.6.0

BSD 版と GNU 版で uname の出力は実際どう違うの?

M1 MacBook Air ではこんな感じ

# uname コマンドが複数入ってることを確認する
$ which -a uname
/opt/homebrew/opt/coreutils/libexec/gnubin/uname
/usr/bin/uname

# GNU 版
$ /opt/homebrew/opt/coreutils/libexec/gnubin/uname -p
arm64

# BSD 版
$ /usr/bin/uname -p
arm

なお手元の Intel 入ってる Mac mini 2018 では -p オプションについての出力に差異はない

# uname コマンドが複数入ってることを確認する
$ which -a uname
/usr/local/opt/coreutils/libexec/gnubin/uname
/usr/bin/uname

# GNU 版
$ /usr/local/opt/coreutils/libexec/gnubin/uname -p
i386

# BSD 版
$ /usr/bin/uname -p
i386

-a オプションで全部出すと出力に若干の違いが見られる。

折角なので pyenv で M1 Mac に入れられるバージョンを手元でビルドしつつ試してみた

  • 2.x 系
    • なし
  • 3.6 系以前
    • なし
  • 3.7 系
    • 3.7.9 以降ならビルド出来る
  • 3.8 系
    • 3.8.10 以降ならビルド出来る
  • 3.9 系
    • 3.9.1 以降ならビルド出来る

古いやつを Rosetta 2 経由で使いたい

興味がなくて調べてないので他をあたってくれ。

LDFLAGS とか CPPFLAGS とか CFLAGS とか

このあたりの環境依存なので homebrew とかで入れたものがインストール後に表示してくるメッセージをサボらずに読んでちゃんと設定しましょう。

こういうのがめんどいから Docker コンテナに寄せるのがいいのでは?

とは言え手元で書き捨てのスクリプトを実行したいくらいカジュアルな用途では docker volume の指定をしたりする方がめんどい気もするし、Activity Monitor.app で見る限り Docker を常時起動させていると結構バッテリ消費が激しいことが分かりつつあるので最近は必要なときだけ立ち上げているから、なんというか、めんどい。

締め

ちょっと動かしたいサンプルコードを動かそうと思ったら動作環境が手元にないことに気付いて、まぁどうせ pyenv install ... で終わりだろと軽く思ってたんだけど全然そんなことなくて、色々調べたら誰も困ってないし、なんでやねんと思ってビルド作業ディレクトリまで行って ./configure --help とかしつつ調査して色々やった結果それで半日終わった。

肝心の動かしたかったサンプルコードはまだ動かしてすらない。