rbenv + ruby-build はどうやって動いているのか

rbenv といえば 複数バージョンの Ruby を切り替えて使うための環境を提供してくれる便利なツールとして欠かすことのできないものですが、機会があってそれがどうやって動いているか調べたので、ここに書いておきます。また ruby-build は ruby をインストールするためによく使われている(というか使ってない人いないのでは)rbenv のプラグインです。rbenv install というコマンドは ruby-build が提供しています。これについても一緒に書きます。

はじめに断っておきますが、間違ったことを書いているかもしれないのでその辺はご了承ください。また、将来 rbenv の変更によって変わってしまう部分もあるかもしれません。

また rbenv のドキュメントはとても充実しているので、それを読めばここに書いてあることはだいたいわかると思います。

もくじ

  1. rbenv のインストール方法による違い
  2. RBENV_ROOT とはなにか?
  3. rbenv init はなにを行うのか
  4. rbenv は ruby-build をどのように使うか?
  5. 新しい ruby を入れたい。それぞれのインストール方法でどうやるか?
  6. 情報源

1. rbenv のインストール方法による違い

Mac で rbenv を使う場合、2通りの方法があります。

  1. Github から checkout してくる
  2. homebrew を使う

この2つ、何が違うのでしょうか?

1つ目の違いはインストール場所です。

Github から checkout

以下の場所にインストール(git clone)します。

  • rbenv は $HOME/.rbenv
  • ruby-build は $HOME/.rbenv/plugins/ruby-build

homebrew

以下の場所にインストールされます。

  • rbenv は /usr/local/Cellar/rbenv/バージョン番号
  • ruby-build は /usr/local/Cellar/ruby-build/バージョン番号(年月日)

また、homebrew の仕組みにしたがって、適切なバージョンの実行可能ファイルに /usr/local/bin からのシンボリックリンクが作成されます。

こんな感じ。

$ ll /usr/local/bin/{rbenv,ruby-build}
lrwxr-xr-x 1 takatoshi admin 31 6 19 2013 /usr/local/bin/rbenv@ -> ../Cellar/rbenv/0.4.0/bin/rbenv
lrwxr-xr-x 1 takatoshi admin 44 1 8 22:33 /usr/local/bin/ruby-build@ -> ../Cellar/ruby-build/20141225/bin/ruby-build

2つめの違いはインストールされるファイルです。

Github から checkout する場合は(当たり前ですが)リポジトリの内容そのままになります。

homebrew の場合はリリースパッケージに含まれるファイルがインストールされます。具体的には homebrew の Formula

を見ればわかりますが、Releases · sstephenson/rbenv · GitHub にある Source code (tar.gz) を解凍したものになっています。

2. RBENV_ROOT とはなにか?

RBENV_ROOT というのは rbenv の shims (後述) と versions (Ruby のインストール先) がある場所(パス)を指し示すための環境変数です。 rbenv コマンドを叩いた時に未設定なら $HOME/.rbenv になります(ここで設定している)。

RBENV_ROOT の値を見るには rbenv root コマンドを使います。RBENV_ROOT 環境変数が空っぽの時に $HOME/.rbenv になっているのがわかります。

$ echo $RBENV_ROOT

$ rbenv root
/Users/takatoshi/.rbenv

ちなみに RBENV_ROOT は特に理由がなければデフォルトのままにしておくのがオススメらしいです。 そして mislav (Mislav Marohnić) · GitHub さんによると変更する理由は思いつかないとのこと。

参考URL

Keeps adding ~/.rbenv dir even though RBENV_ROOT has been set to other dir · Issue #508 · sstephenson/rbenv · GitHub

そして RBENV_ROOT を変更する場合は rbenv init する前にやる必要があります。 理由はデフォルトが$HOME/.rbenvになるということと rbenv init が何を行うかを知ればわかります。

3. rbenv init はなにを行うのか

rbenv のインストール手順を見ると .bash_profile の中で eval "$(rbenv init -)" を実行するように書いてあります。

3. Add rbenv init to your shell to enable shims and autocompletion.

$ echo 'eval "$(rbenv init -)"' >> ~/.bash_profile

これは何を行うのでしょうか? 答えは書いてあるとおり、shims と autocompletion を有効化します。 ・・・って、それなに?

sstephenson/rbenv · GitHub を見るともっと詳細に書いてあります。

これを見ると shims を有効化するというのは $RBENV_ROOT/shims を PATH に入れることだというのがわかります。 実際に shims を見てみると以下のファイルが入っていました。

$ ls `rbenv root`/shims
bundle bundler erb gem irb rake rdoc ri ruby testrb

rbenv init コマンドの実体は rbenv/rbenv-init at master · sstephenson/rbenv · GitHub です。これを見ると shims の有効化をする前に $RBENV_ROOT の下に shims と versions というディレクトリを作っているのがわかります。またその後で rbenv rehash しています

rbenv rehash が何をするか?それはここに書いてあります。

これは Ruby の実行可能ファイルの shims を作成する(し直す)コマンドです。shims とは実行可能ファイル(ruby, rake など)のラッパースクリプトで、その中では rbenv exec コマンドを使って ruby や rake などを実行しています。だから、新しいバージョンの Ruby を入れたときや、実行ファイルを提供する gem を入れたあとに実行するとよいのです。

rbenv/rbenv-rehash at master · sstephenson/rbenv · GitHub

4. rbenv は ruby-build をどのように使うか?

ruby-build を入れると rbenv install コマンドが使えるようになります。 rbenv init もそうですが rbenv のコマンドはすべてこのようなプラグイン方式になっています。

では、まず init や install というコマンドはどのように実行されるのでしょうか?

それを知るためには rbenv/rbenv at master · sstephenson/rbenv · GitHub を見ればよいです。

がその前に、ドキュメントにも書いてあるので先にそっちを見てみましょう。

https://github.com/sstephenson/rbenv/wiki/Plugins

A plugin can be installed by dropping it in as a sub-directory of $RBENV_ROOT/plugins, or it can be located elsewhere on the system as long as rbenv-* executables are placed in the $PATH and hooks are installed accordingly somewhere in $RBENV_HOOK_PATH.

  • プラグイン$RBENV_ROOT/plugins に配置する
  • または rbenv-** は init とか install とか)という実行可能ファイルが PATH のどこかにあればよい

前者は git clone でプラグインを入れた場合、後者は homebrew で入れた場合に対応すると考えられます。

また、プラグインの作り方というドキュメントもあります。

https://github.com/sstephenson/rbenv/wiki/Authoring-plugins

rbenv がコマンドを実行する前にやっていることは、ほとんどこのドキュメントに書かれている気がします。

簡単に言うと、

  1. コマンドを実行するための環境を準備
  2. コマンドを実行

これだけ。環境を準備というのは、RBENV_ROOT、PATH、RBENV_HOOK_PATH などの環境変数を設定したりすることです。

5. 新しい ruby を入れたい。それぞれのインストール方法でどうやるか?

ここまでくれば、これも容易にわかるでしょう。 Github checkout と homebrew の違いはインストール場所です。

つまり rbenv install バージョン番号 したときの挙動はこうなります。

Github checkout のとき

  • $RBENV_ROOT/plugins/ruby-build/bin/ruby-build が実行される
  • したがって、ここにある ruby-build を 最新にしておけばよい(通常は git pull すればいいだろう)

homebrew のとき

  • PATH のどこかにある ruby-build が実行される
  • それを最新にするのは homebrew の役割だ(通常は brew update && brew upgrade ruby-build すればいいだろう)

6. 情報源

この2つを見ましょう。