Goodbye fat gem¶ ↑
: author
Sutou Kouhei
: institution
ClearCode Inc.
: content-source
RubyKaigi Takeout 2020
: date
2020-09-04
: start-time
2020-09-04T10:00:00+09:00
: end-time
2020-09-04T10:25:00+09:00
: theme
.
Sutou Kouhei¶ ↑
The president of ClearCode Inc.n (('note:クリアコードの社長'))
# img # src = images/clear-code-rubykaigi-2020-silver-sponsor.png # relative_height = 100 # reflect_ratio = 0.1
Sutou Kouhei¶ ↑
* The maintainer of (({rake-compiler}))\n (('note:(({rake-compiler}))のメンテナー')) * A gem for generating fat gem\n (('note:fat gemを作るためのgem')) * The maintainer of (({Ruby-GNOME}))\n (('note:(({Ruby-GNOME}))のメンテナー')) * A project that used fat gem\n (('note:fat gemを使っていたプロジェクト'))
Fat gem¶ ↑
Gem that includes pre-built binariesn (('note:ビルド済みバイナリー入りのgem'))
Why is fat gem needed?n(('note:どうしてfat gemが必要なのか'))¶ ↑
* Difficult to build extension library\n (('note:拡張ライブラリーのビルドが難しい')) * Extension library:\n A Ruby library implemented with C API\n (('note:拡張ライブラリー:C APIを使って実装されたRubyライブラリー')) * Fat gem just copies pre-built binaries\n (('note:fat gemは単にビルド済みバイナリーをコピーするだけ(ビルドしない)'))
Why is building it difficult?n(('note:どうして拡張ライブラリーのビルドは難しいのか'))¶ ↑
* Users need build environment\n (('note:ユーザーはビルド環境を用意しないといけない')) * e.g.: C compiler, (({make})) and so on\n (('note:例:Cコンパイラーや(({make}))など')) * Users need build dependencies\n (('note:ユーザーが依存するライブラリーを用意しないといけない')) * e.g.: GTK+ 3 for gtk3 gem\n (('note:例:gtk3 gemはGTK+ 3が必要'))
With fat gemn(('note:fat gemを使うと'))¶ ↑
* Users don't need build environment!\n (('note:ユーザーはビルド環境を用意しなくてもよい')) * No C compiler\n (('note:ユーザーはビルド環境がなくてもよい')) * Users don't fail installation!\n (('note:ユーザーはインストールに失敗しない')) * Fat gem just copies pre-built binaries\n (('note:fat gemは単にビルド済みバイナリーをコピーするだけ'))
With fat gemn(('note:fat gemを使うと'))¶ ↑
(('tag:center')) (('tag:xx-large')) Happy!n 🙌
(('tag:right')) (('wait'))…Really?n (('note:…ほんとに?'))
A fat gem maintainer says…n(('note:あるfat gemメンテナー曰く…'))¶ ↑
Thanks fat gem!n Goodbye fat gem!n (('note:ありがとうfat gem!'))n (('note:さよならfat gem!'))
Fat gem problem 1n(('note:fat gemの問題1'))¶ ↑
Can't usen the latest Rubyn immediatelyn (('note:いち早く最新のRubyを使えない'))
Detailsn(('note:詳細'))¶ ↑
* Ruby is released every Christmas\n (('note:Rubyは毎年クリスマスにリリースされる')) * To use fat gems with the latest Ruby:\n (('note:最新のRubyでfat gemを使うには')) * Need to release fat gems for it\n (('note:最新のRuby用のfat gemがリリースされていないといけない')) * Users can't use the latest Ruby while:\n (('note:ユーザーは↓の間は最新のRubyを使えないまま')) * Any of fat gems doesn't support it\n (('note:使っているfat gemのどれか1つでも最新のRubyをサポートしていない'))
From fat gem maintainer viewn(('note:fat gemメンテナー視点'))¶ ↑
Not all fat gem maintainersn can do itn ((immediately))n (('note:すべてのfat gemメンテナーが((*すぐに*))対応できるわけじゃない'))
Fat gem problem 2n(('note:fat gemの問題2'))¶ ↑
Vulnerability responsen may get delayedn (('note:脆弱性対応が遅れがち'))
Detailsn(('note:詳細'))¶ ↑
* Only bindings fat gem case\n (('note:バインディングのfat gemだけのケース')) * (('note:Bindings: A extension library to use an external library'))\n (('note:バインディング:外部のライブラリーを使う拡張ライブラリー')) * Includes the external library binary\n (('note:fat gem内に外部ライブラリーのバイナリーも含む')) * When a vulnerability of the external library is found:\n (('note:外部ライブラリーに脆弱性が見つかった場合:')) * Should be released immediately with fix\n (('note:すぐに修正を含んだバージョンをリリースするべき'))
From fat gem maintainer viewn(('note:fat gemメンテナー視点'))¶ ↑
Not all fat gem maintainersn can do itn ((immediately))n (('note:すべてのfat gemメンテナーが((*すぐに*))対応できるわけじゃない'))
Fat gem problem 3n(('note:fat gemの問題3'))¶ ↑
Can't controln the external library versionn (('note:外部ライブラリーのバージョンをコントロールできない'))
Detailsn(('note:詳細'))¶ ↑
* Only bindings fat gem case\n (('note:バインディングのfat gemだけのケース')) * Maintainers choose one external library version\n (('note:メンテナーが外部ライブラリーのどのバージョンを使うかを選ぶ')) * If old external library is chosen, users can't use the latest version\n (('note:古い外部ライブラリーを選んだら、ユーザーは最新バージョンを使えない'))
Fat gem problem 4n(('note:fat gemの問題4'))¶ ↑
(({require})) is slowern (('note:(({require}))が遅くなる'))
(({require})) for fat gemn(('note:fat gem用の(({require}))'))¶ ↑
Fat gem needs the following code:n (('note:fat gemでは次のようなコードが必要:'))
# rouge ruby begin # Try the bundled binary in fat gem require "#{RUBY_VERSION[/\d+\.\d+/]}/io/console.so" rescue LoadError # Use the local built binary require 'io/console.so' end
Why slower?n(('note:なぜ遅くなるか'))¶ ↑
* For not fat gem install:\n (('note:fat gemを使わない環境:')) * Such as on GNU/Linux and macOS\n (('note:たとえばGNU/LinuxやmacOS')) * (({require})) for fat gem is always failed\n (('note:fat gem用の(({require}))は必ず失敗')) * Can't ignore with large (({$LOAD_PATH})):\n (('note:(({$LOAD_PATH}))が大きい場合は無視できない')) * e.g.: +0.1s with Ruby on Rails application\n (('note:例:Ruby on Railsアプリケーションでは0.1秒遅くなる'))\n (('note:((<URL:https://github.com/ruby/io-console/pull/4>))'))
Fat gem problem 5n(('note:fat gemの問題5'))¶ ↑
Fat gem releasen may be forgottenn (('note:fat gemのリリースを忘れられる'))
Detailsn(('note:詳細'))¶ ↑
* Normal release is easy\n (('note:通常のリリースは簡単')) * (({rake release})) is done in a few seconds\n (('note:(({rake release}))は数秒で終わる')) * Fat gem release isn't easy\n (('note:fat gemのリリースは簡単じゃない')) * rake-compiler-dock help maintainers but...\n (('note:rake-compiler-dockを使えばマシになるけど…')) * It will take at least a few minutes...\n (('note:少なくとも数分かかる…'))
From fat gem maintainer viewn(('note:fat gemメンテナー視点'))¶ ↑
* Don't want to release a new version...\n (('note:新しいバージョンをリリースしたくないな…')) * Ruby-GNOME case: 10+ related gems\n (('note:Ruby-GNOMEの場合:10以上の関連gemがある')) * It takes at least 30 minutes\n (('note:順調にいった場合でも少なくとも30分はかかる')) * Forget to release a fat gem\n (('note:fat gemをリリースするのを忘れる')) * io-console 0.4.8 case\n (('note:((<URL:https://github.com/ruby/bigdecimal/pull/148#issuecomment-512075494>))'))
Fat gem problem 6n(('note:fat gemの問題6'))¶ ↑
Highn maintenancen costn (('note:メンテナンスが大変'))
Detailsn(('note:詳細'))¶ ↑
* Especially binding fat gem case\n (('note:特にバインディングのfat gemのケース')) * Normally, cross-compiling is used\n (('note:通常、クロスコンパイルしてfat gemを作る')) * Most external libraries don't do it\n (('note:多くの外部ライブラリーはクロスコンパイルなんてしない')) * There are some problems on upgrading\n (('note:外部ライブラリーのバージョンをあげるたびになにかしら問題が見つかる')) * Ruby-GNOME has 10+ related external libraries\n (('note:Ruby-GNOMEは10以上の関連外部ライブラリーがあって大変だった'))
How to solve these problems?n(('note:これらの問題を解決するには?'))¶ ↑
Thanks fat gem!n Goodbye fat gem!n (('note:ありがとうfat gem!'))n (('note:さよならfat gem!'))
Why is fat gem neededn(('note:fat gemが必要だった理由'))¶ ↑
* Users don't have build environment\n (('note:ユーザーがビルド環境を持っていない')) * Especially, Windows users\n (('note:特にWindowsユーザー'))
Windows users and build envn(('note:Windowsユーザーとビルド環境'))¶ ↑
* ((<RubyInstaller for Windows|URL:https://rubyinstaller.org/>)) provides build env by default since 2.4\n (('note:RubyInstaller for Windows 2.4以降はデフォルトでビルド環境を提供')) * Ruby 2.3 reached EOL\n (('note:Ruby 2.3はEOLになっている')) * Most Windows users must use 2.4 or later\n (('note:I know some Windows users don't use RubyInstaller for Windows'))\n (('note:ほとんどのWindowsユーザーは2.4以降を使っているはず')) * Most Ruby users have build env now!\n (('note:ほとんどのユーザーはビルド環境を持っている!'))
Ruby-GNOME said goodbye fat gem!n(('note:Ruby-GNOMEはfat gemにさようならをした!'))¶ ↑
* Since 2018-10-31\n (('note:2018-10-31から')) * Install related issues isn't increased\n (('note:インストール関連のissueは増えていない'))
Resolve fat gem problem 1n(('note:fat gem関連の問題の解決1'))¶ ↑
Can usen the latest Rubyn immediatelyn (('note:いち早く最新のRubyを使える'))
Detailsn(('note:詳細'))¶ ↑
* Don't need a new release for new Ruby\n (('note:新しいRuby用にgemをリリースする必要がない')) * Until new Ruby doesn't change C API\n (('note:新しいRubyでC APIが変わっていない限り')) * Can release a new version\n before new Ruby release\n (('note:新しいRubyがリリースされる前に新しいバージョンのgemをリリースできる')) * Can test with preview release Ruby on CI\n (('note:CIでpreviewリリースのRubyをテストできる'))
Resolve fat gem problem 2n(('note:fat gem関連の問題の解決2'))¶ ↑
Vulnerability responsen can be done byn each systemn (('note:脆弱性は各システムが対応してくれる'))
Detailsn(('note:詳細'))¶ ↑
* Especially, binding fat gem case\n (('note:特にバインディングfat gemの場合')) * Packaging system will update soon than binding fat gem maintainers\n (('note:バインディングfat gemのメンテナーよりもパッケージングシステムの方がすぐに更新することが多い')) * The # of packaging system maintainers\n is larger than\n the # of maintainers of each fat gem\n (('note:各fat gemのメンテナーの数よりもパッケージングシステムのメンテナーの数の方が多いから'))
Resolve fat gem problem 3n(('note:fat gem関連の問題の解決3'))¶ ↑
Can controln the external library versionn (('note:外部ライブラリーのバージョンをコントロールできる'))
Detailsn(('note:詳細'))¶ ↑
* Packaging system may provide the latest external library\n (('note:パッケージングシステムは最新の外部ライブラリーを提供してくれるかもしれない')) * Users can build suitable version of the external library\n (('note:ユーザーは適切なバージョンの外部ライブラリーをビルドできる'))
Resolve fat gem problem 4n(('note:fat gem関連の問題の解決4'))¶ ↑
(({require})) isn't slowern (('note:(({require}))が遅くならない'))
Detailsn(('note:詳細'))¶ ↑
No fallback (({require})):n (('note:フォールバック用の(({require}))がいらない'))
# rouge ruby # Always use the local built binary require 'io/console.so'
bigdecimal said goodbye fat gem!n(('note:bigedimalはfat gemにさようならをした!'))¶ ↑
* Because of this\n (('note:これが理由')) * ((<"ruby/bigdecimal#149"|URL:https://github.com/ruby/bigdecimal/pull/149>))
Resolve fat gem problem 5, 6n(('note:fat gem関連の問題の解決5,6'))¶ ↑
Easy to maintainn (('note:メンテナンスしやすくなる'))
Detailsn(('note:詳細'))¶ ↑
* No fat gem release process\n (('note:fat gemをリリースする作業がなくなる')) * Don't forget fat gem release😀\n (('note:fat gemのリリースも忘れない😀')) * No cross compiling\n (('note:クロスコンパイルしなくてもよい')) * No rake-compiler\n (('note:rake-compilerもいらない')) * Maintainers can use their time for others\n (('note:メンテナーは別のことに時間を使える'))
Stop fat gemn(('note:fat gemをやめると'))¶ ↑
* All problems can be solved!\n (('note:すべての問題を解決できる!')) * Additional good points\n (('note:さらにいいことも')) * Enable optimization for each user's env\n (('note:各ユーザーの環境ごとに最適化できる')) * e.g.: (({-O3 -march=native})) GCC options\n (('note:例:GCCの(({-O3 -march=native}))オプションを使う'))
Stop fat gemn(('note:fat gemをやめると'))¶ ↑
(('tag:center')) (('tag:xx-large')) Happy!n 🙌
(('tag:right')) (('wait'))…Really?n (('note:…ほんとに?'))
Stop fat gem problem 1n(('note:fat gemをやめたときの問題1'))¶ ↑
Long install timen (('note:インストール時間が長くなる'))
Detailsn(('note:詳細'))¶ ↑
* Fat gem: * Just copy pre-built binaries\n (('note:単にビルド済みバイナリーをコピー')) * Gem: * Build and install binaries\n (('note:バイナリーをビルドしてインストール'))
How to resolve this?n(('note:解決法は?'))¶ ↑
No idea…n (('note:なんかある?'))
Stop fat gem problem 2n(('note:fat gemをやめたときの問題2'))¶ ↑
Fail to installn (('note:インストールに失敗する'))
Detailsn(('note:詳細'))¶ ↑
* Only bindings gem case\n (('note:バインディングのgemだけのケース')) * Needs the external library to build\n (('note:ビルドするために外部ライブラリーが必要')) * If it doesn't exist, (({gem install})) is failed\n (('note:外部ライブラリーがないと(({gem install}))が失敗')) * e.g.: (({gem install rmagick})) is failed without ImageMagick\n (('note:例:ImageMagickがないと(({gem install rmagick}))が失敗'))
How to resolve this?n(('note:解決法は?'))¶ ↑
Installn the external libraryn automaticallyn (('note:自動で外部ライブラリーをインストール'))
RubyInstaller for Windows¶ ↑
Put the external library package name to the gem's metadata:n (('note:gemのメタデータに外部ライブラリーのパッケージ名を設定'))
# rouge ruby gemspec.metadata["msys2_mingw_dependencies"] = "cairo"
(('note:((<“MSYS2 library dependencies - For gem developers - onclick/rubyinstaller2 Wiki”|URL:github.com/oneclick/rubyinstaller2/wiki/For-gem-developers#-msys2-library-dependency>))'))
native-package-installer¶ ↑
rcairo case:
# rouge ruby require "pkg-config" require "native-package-installer" unless PKGConfig.have_package("cairo") # Install cairo from packaging system automatically unless NativePackageInstaller.install(:arch_linux => "cairo", :debian => "libcairo2-dev", :homebrew => "cairo", :macports => "cairo", :redhat => "cairo-devel") exit(false) end exit(false) unless PKGConfig.have_package("cairo") end
Stop fat gem problem 3n(('note:fat gemをやめたときの問題3'))¶ ↑
Bundler'sn dependency resolutionn may failn (('note:Bundlerの依存関係解決が失敗するかもしれない'))
Bundler may use wrong dependencyn(('note:Bundlerは間違った依存情報を使ってしまうかもしれない'))¶ ↑
# rouge bash git clone https://github.com/rails/rails.git cd rails bundle install bundle update # Error
(('note:((<“Bundle Update Fails for Rails 6.1 if Ruby >= 2.6 · Issue #37224 · rails/rails”|URL:github.com/rails/rails/issues/37224>))'))
Actual¶ ↑
Bundler could not find compatible versions for gem "ruby": In Gemfile: ruby mysql2 (~> 0.5) was resolved to 0.5.2, which depends on ruby (< 2.6) x64-mingw32 mysql2 (~> 0.5) was resolved to 0.5.2, which depends on ruby (< 2.6) x86-mingw32 nokogiri (>= 1.8.1) was resolved to 1.10.4, which depends on ruby (>= 2.3) x64-mingw32 nokogiri (>= 1.8.1) was resolved to 1.10.4, which depends on ruby (>= 2.3) x86-mingw32 rails was resolved to 6.1.0.alpha, which depends on ruby (>= 2.5.0)
Detailsn(('note:詳細'))¶ ↑
Omitn (('note:省略'))
How to resolve this?n(('note:解決法は?'))¶ ↑
* I've been fixed this\n (('note:直しておいた')) * ((<"rubygems/bundler#7522"|URL:https://github.com/rubygems/bundler/pull/7522>)) * If you're interested in fixing problems in upstream, ClearCode is a good corporation:\n (('note:問題をアップストリームで直すことが好きな人はクリアコードといういい会社があるよ!'))\n ((<URL:https://www.clear-code.com/recruitment/>)) * Use Bundler 2.2.0 or later(('note:(not released yet)'))\n (('note:未リリースだけどBundler 2.2.0以降を使えば解決する'))
Wrap upn(('note:まとめ'))¶ ↑
* Fat gem was useful until Ruby 2.3\n (('note:Ruby 2.3まではfat gemは有用だった')) * We can stop using fat gem now!\n (('note:今はfat gemをやめられる!'))
Wrap upn(('note:まとめ'))¶ ↑
Thanks fat gem!n Goodbye fat gem!n (('note:ありがとうfat gem!'))n (('note:さよならfat gem!'))