2021 年になっても Rails 関連で v8 ドライバを良い感じにビルドするのが未だに難しい件について。

追記 (2021/6/7)

どうやらそもそも無理にビルドしなくてもいいという話 を書いた。

あらすじ

32bit 環境が死滅して早数年、人類は x86_64 の楽園で楽しく過ごしていた。

しかし Apple Silicon M1 の台頭によってマルチアーキテクチャ (x86_64 と arm64 (と aarch64) と linux と darwin と…) に苦しめられた過去の記憶を思い出すことになる…。

よく libv8 のビルドで指定されているオプションが無くなっていた件について。

therubyracermini_racer の内部で使われている libv8 についての話。

ググると大体出てくるビルドオプションである --with-system-v8 だが、この コミット で落ちてたようだ。6.3.292.48.1 までは README にてオプションが確認できるが、 6.7.288.46.0 からはきちんと消えている。

そしてこれは 2018 年の中頃の話なのだが、ググってもこういう情報はあまり出てこない気がしている。調べ方が悪いだけなのか、もうみんな興味を失っているのか…。

とりあえず雰囲気で --with-system-v8 が指定され続けているような気もする。

therubyracer と mini_racer について。

therubyracer は Rails 本体からもとっくに (2017年の話) 見放されて おり、 mini_racer に移行している。この辺の話はいくつか見付かるのでまだ到達しやすい。

therubyracer のコミット自体は 2018 年の 11 月まで確認できるもののリリースはされていない。2017 年の 1 月に出ている 0.12.3 が最新だが、 gemspec で依存関係を見ると libv8 の ~> 3.16.14.15 の指定であり、ここから更に実装を辿ると この辺 で libv8 の 3.16 系をチェックアウトしてビルドしていることが分かるが、これは Chromium のソースの方の 3.16.0 の ChangeLog3.17.0 の ChangeLog を眺めるに 2012 年から 2013 年の v8 を使っていることになる。

流石にそれはあかんやろってことで Rails 本体の方も therubyracer を見限って、無理の少ないラッパーである mini_racer の方に移行したという話の流れらしい。

mini_racer の 0.3.1 と 0.4.0 で v8 ドライバが変わったっぽい。

mini_racer の 0.3.1 までは libv8 に依存していたが 0.4.0 からは libv8-node に依存が変わっている。

名前が大体同じで滅茶苦茶ややこしいが、歴史のある libv8 から鞍替えしたようだ。なお RubyGems.org にある こちら からの GitHub リンクはリポジトリの名前が変わっていて到達できないし、該当リリースのタグも打たれていないので、移行先の gem 製作者の仕事が適当というか甘い気もしており、若干の不安要素が残る。

libv8-node の不安要素について。

不安要素で言うと移行された libv8-node は gem 側で実装されているビルド時の設定が微妙におかしいのか、 x86_64 linux-musl 環境下で vendor/bundle 以下に何度 bundle install しても bundle check が通らないし bundle exec ... でコケ続けるという不具合がありそうだ。まだ詳細には調べてはいない。

/usr/local/bundle 以下に bundle install すれば問題はないのだが、 bundler を使ってグローバル領域に入れるような設定にすることは稀だろうから、常用するにあたってこの問題は厳しい。

この問題は Mac 環境では x86_64 でも arm64 でも再現はしなかったし、 CI に持っていって始めて確認された現象であって、この時点でダルさが極まった訳なのだが、一応マジかよと思って手元に似たような Docker 環境を作ったら再現した、みたいな感じだったので調査のモチベがただ単に低まっていてダルいという話もある。

気が向いたらちゃんと時間を取って調査でもします。

とりあえず不安なので libv8-node ではなく libv8 を使うように mini_racer のバージョンの指定を 0.4.0 ではなく 0.3.1 にしておけば良い。良いのだが…。

mini_racer のバージョンを下げて libv8-node ではなく libv8 の方を使うにしても…。

mini_racer では新しい libv8 を使えるのがウリな訳で、この記事を書いている時点では 8.4.255.0 なのだが、これを aarch64 環境の Linux でビルドしようとすると通らないという中々厳しい現実が待っている。

v8 のバイナリをビルドするのになんと Python の 2 系が求められるpython2 として PATH にあれば良いのだが、一部の環境では python2.7 として入るので python2pythonln -s でもしてやる必要がある。

そしてつらい気持ちで Python 2 系の環境を整えても aarch64 環境だと v8 のビルド中にコケる。ちょっとこの辺まで来ると調査するにも心が折れてくる。こうなってくるとわざわざ Python 2.7 を用意してまで libv8 を入れることはないような気もするので、 libv8-node の細かい不具合が解消されたら大人しくそちらに移行すれば良いだろう、という気持ちで放置することにした。

気が向いたらちゃんと時間を取って調査でもします。

ちなみに aarch64 の Linux でビルド出来ないが、arm64 の Mac では通る。そして x86_64 の Linux と Mac では問題ない。Windows 環境は知らん。つらいですね。

mini_racer に移行しても、マルチプロセスにすると (puma や unicorn など) 死ぬ。

内容的には この issue の話なのだが、 fork してマルチプロセスになると、プロセス終了時にセグフォするという問題がある。

この問題について、上の issue 報告者は

  • Ruby 2.4.6
  • mini_racer 0.2.8
  • libv8 7.3.492.27.1

の環境で Unicorn を使用しているらしい。また、イシュー報告欄にて

  • Ruby 2.5.5
  • mini_racer 0.2.8

でも再現してるよとか、

  • Ruby 2.5.3
  • mini_racer 0.2.14
  • libv8 7.3.492.27.1

で再現してるよという話もあるし、自分自身も

  • Ruby 2.5.5
  • mini_racer 0.3.1
  • libv8 8.4.255.0

で再現したことを確認している。

一応この問題について README に回避策 が書かれているようにも見えるが、イシュー報告者は「これやってもダメだったわ」って書いてるし、そんな気がしている。

libv8-node に移行すれば問題が解決するかはちょっと分からない、これも気が向いたら調査します。

締め

調査すればするほど調査することが増えていく問題 (この現象には yak shaving という名前が付いている) が深刻で、どこかで枝刈りしないといけないんだけど、心が折れつつあるので記事を書いて頭を整理している。

ネイティブエクステンションを様々な環境で動くようにビルドするのは本質的に面倒臭い。これはしょうがない。

それはそうと Asset Pipeline を捨てられるなら捨てた方がいいし、 Rails を使うなら API モード に限るな、という気持ちが強まりました。