h3pei.tech
お仕事のご依頼
2020-12-30

ruby-buildとrbenvのプラグイン機構

前回の記事 に続いて rbenv ネタ。 今回は、ruby-build と rbenv の関係について。

ruby-build とは何か

ruby-build とは、あらゆるバージョンの ruby を簡単にインストールするためのコマンドラインユーティリティ。本家の README には次のように書かれている。

ruby-build is a command-line utility that makes it easy to install virtually any version of Ruby, from source.

ruby-build は rbenv のプラグインとして使うこともできるし、スタンドアロンなコマンドとして使うこともできる。

多くの ruby ユーザは前者の rbenv のプラグインとして使っていることと思う。自分もそうなので、今回はこちらについて少し深堀っていく。

rbenv install コマンドは ruby-build が提供している

rbenv --help コマンドを実行すると次のような結果が出力される。

 rbenv --help
Usage: rbenv <command> [<args>]

Some useful rbenv commands are:
   commands    List all available rbenv commands
   local       Set or show the local application-specific Ruby version
   global      Set or show the global Ruby version
   shell       Set or show the shell-specific Ruby version
   install     Install a Ruby version using ruby-build
   uninstall   Uninstall a specific Ruby version
   rehash      Rehash rbenv shims (run this after installing executables)
   version     Show the current Ruby version and its origin
   versions    List installed Ruby versions
   which       Display the full path to an executable
   whence      List all Ruby versions that contain the given executable

See `rbenv help <command>' for information on a specific command.
For full documentation, see: https://github.com/rbenv/rbenv#readme

ここにある install というサブコマンドは、実は rbenv 本体には組み込まれておらず、rbenv プラグインとしての ruby-build が提供しているコマンドになっている。

その証拠に、rbenv core のコマンドが含まれる libexec ディレクトリ以下 を見ても install 機能を提供する実行ファイルは含まれていない。

実際に自分の環境の rbenv/libexec 以下を見ても該当するファイルが存在しないことを確認した (※ rbenv 自体は homebrew で install している)。

 ls -1 /usr/local/Cellar/rbenv/1.1.2/libexec/ | grep install
# => 結果なし

rbenv はどのようにしてプラグインを読み込むか

では rbenv 本体はどのようにしてプラグインとしての ruby-build を読み込み、install コマンドをはやしているか?

これを理解するには、rbenv のプラグイン機構について知る必要がある。 rbenv 公式の Wiki を見ると、パスが通っているところに rbenv-COMMAND という形式で配置すれば良い といった旨の記述が見つかる。

つまり、install サブコマンドを生やす ruby-build の場合は rbenv-install という実行ファイルが PATH の通っているどこかに存在するということになる。

実際に自分の環境で調べてみたところ、/usr/local/bin/ 以下に rbenv-install ファイルを見つけることができた。/usr/local/bin にはもちろんパスが通っている。

 file /usr/local/bin/rbenv-install
/usr/local/bin/rbenv-install: Bourne-Again shell script text executable, ASCII text

# パスが通っていることを確認
 echo $PATH | grep --only-matching '/usr/local/bin'
/usr/local/bin

なお、自分の場合は ruby-build も homebrew でインストールしているので、実際には /usr/local/Cellar/ruby-build/VERSION/bin/rbenv-install へのシンボリックリンクとなっている。

おまけ:さらに深ぼってみる

ここまで来たので、ソースコードレベルでもう少し深ぼってみる。 rbenv-COMMAND という形式で PATH が通っているところに配置すれば実行してくれるのはわかったが、具体的にどうやって探索しているか?を見てみたい。

以下は、rbenv コマンドの実行ファイルの抜粋

command="$1"
case "$command" in
"" )
  { rbenv---version
    rbenv-help
  } | abort
  ;;
-v | --version )
  exec rbenv---version
  ;;
-h | --help )
  exec rbenv-help
  ;;
* )
  command_path="$(command -v "rbenv-$command" || true)"
  if [ -z "$command_path" ]; then
    if [ "$command" == "shell" ]; then
      abort "shell integration not enabled. Run \`rbenv init' for instructions."
    else
      abort "no such command \`$command'"
    fi
  fi

  shift 1
  if [ "$1" = --help ]; then
    if [[ "$command" == "sh-"* ]]; then
      echo "rbenv help \"$command\""
    else
      exec rbenv-help "$command"
    fi
  else
    exec "$command_path" "$@"
  fi
  ;;
esac

今回の ruby-build を例に考えると rbenv install .. とコマンドを実行したときのことを考える。以下、箇条書きでざっくりまとめる。

  • command="$1" の部分は、rbenv コマンドの次に渡される引数がくるので install という文字列が代入される
  • case の最初の3つの条件には当てはまらず、* ) の分岐に入る
  • この次が肝。command_path="$(command -v "rbenv-$command" || true)" を実行して、rbenv-install コマンドのパスを取得している。
    • 実際に自分の環境で command -v rbenv-install を実行すると /usr/local/bin/rbenv-install という結果が得られた
  • if [ -z .. ]; then の部分は、引数の文字列長が0のときに真となるので、該当しないのでスキップ
  • shift 1 する。これにより次の $1 に入る値が rbenv install xxx コマンドを例にすると xxx に当たる。
  • $1--help では無いので else 節へ
  • execrbenv-install が実行される

結論、command コマンドで rbenv-install のパスを取得したあとは exec に引き渡して実行するだけだった。

まとめ

  • ruby-build とは、あらゆるバージョンの ruby を簡単にインストールするためのコマンドラインユーティリティ
  • rbenv の install コマンドは、rbenv プラグインとしての ruby-build が提供している
  • rbenv は rbenv-COMMAND という形式の実行可能ファイルを PATH が通っているところに配置しておけば読み込んでくれる

参考

h3pei's icon

h3pei

フリーランスのソフトウェアエンジニア。Ruby / Rails アプリケーションの開発が得意領域。設計・実装・運用まで含めてプロダクト開発が好きです。

Questalという目標達成コミュニティサービスを開発しました。仲間と一緒に目標達成に取り組みたい方はぜひご利用ください。

お仕事のご依頼
GitHubnoteX

関連記事