エムスリーテックブログ

エムスリー(m3)のエンジニア・開発メンバーによる技術ブログです

M1 Mac に古い ruby をインストールしてみたけど業務利用を諦めた話

こんにちは、エムスリーエンジニアリンググループの園田です。本記事は エムスリー Advent Calendar 2021 の 18 日目の記事です。

最近、業務 PC の Mac が壊れたので、M1 (Apple Silicon) Mac に乗り換えました。
自分が担当している一部システムでは EOL の古い ruby を利用しているシステムがまだ残っており、この記事はその際に M1 Mac に ruby 2.3.8 をインストールしたときの備忘録です。

先に結論

ruby のインストール自体は比較的簡単にできました。
でも native ビルドが必要な gem は gem ごとに対応が必要でしんどすぎるので、業務利用は諦めました。
ローカル環境では docker 使うようにしてます。*1

ざっくり手順

  • openssl 1.0.2 のソースに darwin-arm64 のパッチを当ててインストール
  • ruby-2.3.8 の tool/config.sub に arm64 のパッチを当てる
  • configure オプションに色々追加で指定してビルドする

プレーンなソースからインストールする場合も、 rbenv を使ってる場合も、 asdf を使っている場合も configure--prefix が異なるだけで、手順はほぼ同じです。
なお、筆者は asdf でインストールしました。

openssl 1.0.2 インストール

ruby 2.3.8 は homebrew で入れられる openssl 1.1 系には対応していないので 1.0.2 をソースからインストールする必要があります。
ソースは OpenSSL の HP からダウンロードしました。

openssl を darwin-arm64 に対応させるパッチは Gist にありました。

wget https://www.openssl.org/source/old/1.0.2/openssl-1.0.2u.tar.gz
tar zxf openssl-1.0.2u.tar.gz
curl -s https://gist.githubusercontent.com/felixbuenemann/5f4dcb30ebb3b86e1302e2ec305bac89/raw/b339a33ff072c9747df21e2558c36634dd62c195/openssl-1.0.2u-darwin-arm64.patch | patch -p0

パッチを当てたら Configure コマンドに darwin64-arm64-cc を指定してビルド & インストールしました。インストール先のディレクトリは任意です(ここでは $HOME/libs/openssl-1.0.2u にしています)。

cd openssl-1.0.2u
./Configure "darwin64-arm64-cc" shared zlib --prefix=$HOME/libs/openssl-1.0.2u
make && make install

ruby 2.3.8 インストール

こちらもそのままだと aarch64-apple-darwin-arm64 に対応していないので、 tool/config.sub にパッチを当てます。こちらはパッチ当てなくてもファイルをそのままダウンロードできるのでそれを使いました。

wget https://cache.ruby-lang.org/pub/ruby/2.3/ruby-2.3.8.tar.gz
tar zxf ruby-2.3.8.tar.gz
cd ruby-2.3.8/tool
mv config.sub{,.bak}
wget https://raw.githubusercontent.com/gcc-mirror/gcc/master/config.sub
cd ../

インストールした openssl 1.0.2 のパスをオプションで指定して configure しました。

./configure \
  --prefix=$HOME/.asdf/installs/ruby/2.3.8 \
  --disable-install-doc \
  --disable-install-rdoc \
  --enable-shared \
  --with-openssl-dir=$HOME/libs/openssl-1.0.2u

rbenv の場合は --prefix=$(rbenv root)/versions/2.3.8 など

このオプションで make したら ffi でエラーが出ました。

closure.c:263:14: error: implicit declaration of function 'ffi_prep_closure' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
    result = ffi_prep_closure(pcl, cif, callback, (void *)self);

RUBY_CFLAGS=-DUSE_FFI_CLOSURE_ALLOC を指定すればいいらしい(ruby-build を利用する場合で、直接インストールする場合は RUBY_ を消して CFLAGS にする)。

github.com

環境変数を追加して再度 configuremake 実行。

make clean
export CFLAGS=-DUSE_FFI_CLOSURE_ALLOC
./configure \
  --prefix=$HOME/.asdf/installs/ruby/2.3.8 \
  --disable-install-doc \
  --disable-install-rdoc \
  --enable-shared \
  --with-openssl-dir=$HOME/libs/openssl-1.0.2u
make && make install

asdfreshim しないと認識されませんでした(rbenv なら rehash かな?)。

asdf reshim
asdf local ruby 2.3.8
ruby -v
> ruby 2.3.8p459 (2018-10-18 revision 65136) [-darwin20]

ここまで来たら後一歩ですね。

gem install bundler

OMG ...

/Users/ryohei-sonoda/.asdf/installs/ruby/2.3.8/lib/ruby/2.3.0/-darwin20/enc/utf_16le.bundle: warning: failed to load encoding (UTF-16LE); use ASCII-8BIT instead
/Users/ryohei-sonoda/.asdf/installs/ruby/2.3.8/lib/ruby/2.3.0/-darwin20/enc/utf_16be.bundle: warning: failed to load encoding (UTF-16BE); use ASCII-8BIT instead
ERROR:  Loading command: install (ArgumentError)
        unknown encoding name: binary
ERROR:  While executing gem ... (NoMethodError)
    undefined method `invoke_with_build_args' for nil:NilClass

Encoding 関連なので readline かな・・・?と勘を頼りに --with-readline-dir をつけて再ビルド。

brew install readline # 未インストールの場合
make clean; rm -rf $HOME/.asdf/installs/ruby/2.3.8 # 消さないとダメ
export CFLAGS=-DUSE_FFI_CLOSURE_ALLOC
./configure \
  --prefix=$HOME/.asdf/installs/ruby/2.3.8 \
  --disable-install-doc \
  --disable-install-rdoc \
  --enable-shared \
  --with-openssl-dir=$HOME/libs/openssl-1.0.2u \
  --with-readline-dir=$(brew --prefix readline)
make && make install

asdf reshim 
gem install bundler

> Fetching: bundler-2.2.33.gem (100%)
> Successfully installed bundler-2.2.33
> 1 gem installed

いけた。

ruby インストール後のトラブル

bundler で SSL エラー

bundle install すると SSL のエラーが出る。

Fetching source index from https://rubygems.org/

Retrying fetcher due to error (2/4): Bundler::Fetcher::CertificateFailureError Could not verify the SSL certificate for https://rubygems.org/.
There is a chance you are experiencing a man-in-the-middle attack, but most likely your system doesn't have the CA certificates needed for verification. For information about OpenSSL certificates, see https://railsapps.github.io/openssl-certificate-verify-failed.html. To connect without using SSL, edit your Gemfile sources and change 'https' to 'http'.

最近よく見かけたエラーなのですぐに Let's Encrypt の古い CA を見ているのかなと推測。

ruby -ropenssl -e "p OpenSSL::X509::DEFAULT_CERT_FILE"
> /Users/ryohei-sonoda/libs/openssl-1.0.2u/ssl/cert.pem
ls /Users/ryohei-sonoda/libs/openssl-1.0.2u/ssl/cert.pem
> ls: cannot access '/Users/ryohei-sonoda/libs/openssl-1.0.2u/ssl/cert.pem': No such file or directory

そもそも cert.pem がなかった。以下で解決。

ln -s /etc/ssl/cert.pem /Users/ryohei-sonoda/libs/openssl-1.0.2u/ssl/cert.pem

nokogiri ビルドエラー

当然のように出てくるビルドエラー。依存の libxml2 のビルドに失敗しているらしい。

ld: symbol(s) not found for architecture arm64

以下でいけた。

brew install libxml2
bundle config --local build.nokogiri --use-system-libraries
bundle install

ffi ビルドエラー

Invalid configuration `arm64-apple-darwin20.6.0': machine `arm64-apple' not recognized
configure: error: /bin/sh /Users/ryohei-sonoda/git/askdoctors/v2/vendor/bundle/ruby/2.3.0/gems/ffi-1.9.17/ext/ffi_c/libffi/config.sub arm64-apple-darwin20.6.0 failed
make: *** ["/Users/ryohei-sonoda/git/askdoctors/v2/vendor/bundle/ruby/2.3.0/gems/ffi-1.9.17/ext/ffi_c/libffi--darwin20"/.libs/libffi_convenience.a] Error 1

こちらは以下でいけました。

brew install libffi # 未インストールの場合
export LDFLAGS=-L$(brew --prefix libffi)/lib
export CPPFLAGS=-I$(brew --prefix libffi)/include
export PKG_CONFIG_PATH=$(brew --prefix libffi)/lib/pkgconfig
bundle install

pg ビルドエラー

古い pg (0.18.2) でビルドエラー。
エラー内容は紛失したので書けませんが、以下でいけました。

bundle config --local \
  build.pg \
  --with-cflags=-Wno-error=implicit-function-declaration

byebug ビルドエラー

以下略・・・

まとめ

古い gem はほとんどが aarch64-apple-darwin-arm64 に対応していないので、諦めて docker 版の ruby 2.3 を使うことにしました。

docker イメージだとアーキテクチャが darwin/arm64 じゃなくて linux/arm64 になるので、だいたいの gem はビルドできるようだ

古い ruby を M1 Mac にインストールすることは「できる」けど、「できる」ことと「実用に足る」かどうかは別物であることを改めて思い知りました(それはそう)。

頑張って古い ruby をインストールしようとしている人が、この記事を見て思いとどまってくれたら幸いです。

参考にしたサイト

We are hiring!

古いアーキテクチャを刷新したいエネルギッシュなエンジニアを募集しています!もちろん、新しいアーキテクチャも扱ってます!(他のポストを見てればわかりますよね?)

jobs.m3.com

*1:余談ですが、M1 Mac の docker volume は少し速くなった気がします。気のせいかな?