わさっきhb

大学(教育研究)とか ,親馬鹿とか,和歌山とか,とか,とか.

Rubyの二つのスナップショットをコマンド一つでインストール

はじめに

何台かのLinuxCygwinの環境で,Rubyの最新版スナップショット(1.9系列)と安定版スナップショット(1.8系列)をビルドし,インストールしています.
一つの実行環境で,1.9系列と1.8系列の両方で動作を確認したい,ということがときどきあります.そういうとき,別々にダウンロードしてconfigureのオプションを指定してmakeして,make installのときにsudoをどうしようとか悩むのはよろしくありませんし,これまで1.8系列を置いていたパスに1.9系列のバイナリを入れてしまっては,トラブルの元です.
そこで,

  • Linuxでは,
    • 安定版*1を ~/Lib/ruby/bin/ruby に入れてこちらをメインに使い,
    • 最新版を ~/Lib/.ruby19/bin/ruby に入れてサブとして使う.
  • Cygwinでは,
    • 最新版を ~/Lib/ruby/bin/ruby に入れてこちらをメインに使い,
    • 安定版を ~/Lib/.ruby18/bin/ruby に入れてサブとして使う.

という方針で,最新のものをビルドできるよう,zshのfunctionなどを書きました.

前提

ビルドに必要なコマンドなどは一通り揃えているものとします.
ディレクトリ ~/src と,~/Lib/dummy を作っておきます.それぞれ,中にファイルはあってもなくてもかまいません.

~/.zshrc の記述

1. アーキテクチャ判定
if [ -x /usr/bin/uname ] || [ -x /bin/uname ]; then
    case "`uname -sr`" in 
	Linux*);   export ARCHI="linux"   ;;
	CYGWIN*);  export ARCHI="cygwin"  ;;
	*);	   export ARCHI="unknown" ;;
    esac
else
    export ARCHI="unknown"
fi

環境変数ARCHIを設置し,"linux","cygwin","unknown"のいずれかを代入します.

2. コマンドサーチパス
if [ -d $HOME/Lib ]
then
    for d in $HOME/Lib/*
    do
	if [ -d $d/bin ]
	then
	    PATH=$d/bin:$PATH
	fi
    done
fi

rubyをインストールするまでは,何もしません.
インストール後,~/Lib/ruby/bin があれば,それをコマンドサーチパス(環境変数PATH)の先頭に挿入します.rubyに限らず,例えばソースから構築したsubversionやgitなんてのでもかまいません.こうすることで,rubyなどのコマンドをホームディレクトリの下に置くことができ,root権限なしでもビルド・インストール・実行できるわけです.
ところで,「setopt glob_dots」という指定をすでにしていたら,「for d in $HOME/Lib/*」において,~/Lib/.ruby18/bin あるいは ~/Lib/.ruby19/bin にもマッチすることになります.しかし,「.」と「r」のASCIIコードに注意すると,~/Lib/ドットファイルディレクトリ/bin が先,~/Lib/ruby/bin が後でマッチし,このforループを終えると,コマンドサーチパスの優先順位は ~/Lib/ruby/bin のほうが高くなるので,問題になりません.

3. ビルド・インストールの基礎関数

いよいよビルドとインストールのfunctionです.

function ruby-snapshot-install() {
    local ECHO=""
    local today=$(date '+%Y%m%d')
    local ssflag=1
    local instdir="ruby"
    local uri dir1 dir2 tgz

    if [[ $# != 0 ]]
    then
	instdir=$1
	shift
	if [[ $# != 0 ]]
	then
	    ssflag=$1
	    shift
	fi
    fi
    if [[ $ssflag = "1" ]]
    then
	uri=ftp://ftp.ruby-lang.org/pub/ruby/stable-snapshot.tar.gz
	dir1=ruby
	dir2=ruby-ss-$today
	tgz=$dir2.tgz
    else
	uri=ftp://ftp.ruby-lang.org/pub/ruby/snapshot.tar.gz
	dir1=snapshot
	dir2=ruby-sn-$today
	tgz=$dir2.tgz
    fi
    $ECHO pushd $HOME/src
    $ECHO wget -O $tgz $uri
    $ECHO tar xzf $tgz
    $ECHO mv $dir1 $dir2
    $ECHO popd
    $ECHO pushd $HOME/src/$dir2
    if [[ $ARCHI = "linux" ]]
    then
	$ECHO ./configure --prefix=$HOME/Lib/$instdir --enable-pthread
    else
	$ECHO ./configure --prefix=$HOME/Lib/$instdir
    fi
    $ECHO make
    $ECHO make install
    $ECHO popd
}

このfunctionは,引数を2つとることができます(なくてもかまいません).1番目は,rubyなどのバイナリを ~/Lib/ディレクトリ/bin/ruby にインストールする際の「ディレクトリ」です.2番目は,1なら安定版,それ以外なら最新版のスナップショットをインストールします.
上の「local ECHO=""」のところを「local ECHO="echo"」にすれば,ビルド・インストールのために実行するコマンドが表示されるだけとなります.
ビルド後のtarballファイルとソースディレクトリは,以下のとおりです.

  • 最新版
    • tarballは ~/src/ruby-sn-YYYYMMDD.tgz
    • ソースは ~/src/ruby-sn-YYYYMMDD
  • 安定版
    • tarballは ~/src/ruby-ss-YYYYMMDD.tgz
    • ソースは ~/src/ruby-ss-YYYYMMDD

なお,最新版と安定版とで,tarballを伸張したときにできるディレクトリ名が違います.具体的には,前者はsnapshot,後者はrubyです.この違いを吸収しているのが,ローカル変数dir1です.
記述について,もう少し細かく.if文のブラケットが2重になっていますが,気の迷いです*2.あと,popdして直後にpushdしているところがありますが,当初,pushdしてcdしてpopdしたらこのfunctionの実行前に戻るかなと思ったのですが,「setopt auto_pushd」を指定しているときにまずいと気づきまして,指定の有無にかかわらず元に戻れるようにしたのでした.
このfunctionを直接呼び出すのでは,最新版か安定版,どちらか1種類しかビルドできません.もう一つ,functionを定義して,ワンコマンド化します.

4. 1コマンドで2つをビルド・インストール
if [[ $ARCHI = "cygwin" ]]
then
    function ruby-build() {
	ruby-snapshot-install ruby 0
	ruby-snapshot-install .ruby18
    }
else
    function ruby-build() {
	ruby-snapshot-install
	ruby-snapshot-install .ruby19 0
    }
fi

ここまでの記述を ~/.zshrc に入れ,zshを起動し(直し)てから,

$ ruby-build

と実行し,一通りのインストール作業を終えるのを待てば*3,冒頭に書いたとおりになります.
もう一度zshを起動し直して,

$ which ruby
$ ruby -v
$ ~/Lib/.ruby(ここでTabキーを押して補完)/bin/ruby -v

を実行することで,バージョンを確認できます.

irbとWirble

今すぐ1.8系列と1.9系列とで比較したいプログラム,というのはないのですが,代わりに,Wirbleをどちらのirbからでも使えるようにします.Wirbleについてはhttp://journal.mycom.co.jp/articles/2006/12/22/wirble/に解説があります.先に違いを書いておくと,1.8系列ではgem install wirbleの前にひと苦労,1.9系列では後にひと苦労です.

~/.irbrc

Wirbleのインストールの前に,~/.irbrcを設定しておきます.

require 'rubygems'
require 'wirble'
Wirble.init(:skip_prompt => :DEFAULT, :history_path => File.expand_path('~/.irb_history'))
Wirble.colorize

1.9系列では「require 'rubygems'」は不要ですが,書いてあっても不具合ありませんし,1.8系列では必須ですので,記述しておきます.
Wirble.initの:skip_promptは,wirbleは便利だけどプロンプトがシンプルすぎる。でも色付けや補完はしたい - 橋本幸樹の無愛想な日記に,:history_pathは,WindowsだとWirbleを使ったirbのシンタックスハイライトは使えない? - のほほん徒然に書かれています.
ではWirble本体を.

安定版でWirble

安定版(1.8系列)については,gemコマンドがありませんので,ソースからインストールします.なのですが,RubyForge:Rubygemsから最新のtgzファイルをダウンロードして伸張し,ruby setup.rb*4を実行するのでは,

Malformed version number string 1.8.8.-1 (ArgumentError)

というエラーが出ることがあります.http://www.bluequartz.us/phpBB2/viewtopic.php?t=82557&sid=576693b760d0f0bcc7ebd6916ec0547d(英語)で議論がなされていて,「version.hで定義されている『#define RUBY_PATCHLEVEL -1』を『#define RUBY_PATCHLEVEL 1』に書き換えればいいようです.ただ,これは先ほどビルドしたばかりのRubyのソースにあるversion.hを言っていますので,

$ cd (安定版のソース)
$ vim version.h
(次のとおりタイプ: / スペース - 1 Enter l x Z Z)
$ make
$ make install

の順に実行します.なお,configureは実行しません.
あとは,Rubygemsをインストールして,gem install wirbleとします.irbを実行し,「x=(1..10).to_a」とか「x.to_s」とかすれば,結果がきれいに色づけられます.

最新版でWirble

最新版(1.9系列)では,gemコマンドも合わせてインストールされますので,ただちにgem install wirbleを実行します.しかし,続いてirbを実行するのでは,

(略)/wirble.rb:141: syntax error, unexpected ':', expecting keyword_then or ',' or ';' or '\n' (SyntaxError)
when ':': state << :symbol
(略)

といったエラーが出ます.コマンド一つで解決を試みると,

$ ruby -p -i.bak -e '$_.sub!(": ","; ")if/when/=~$_' (略)/wirble.rb

とすればよさそうです.「(略)」には,エラーが出たwirble.rbのパスを書きます.これで,中にある「when 式: 」をすべて「when 式;」に置き換えます.
irbと動作確認の方法は,安定版と同じです.

*1:以下,「スナップショット」を省略します.

*2:それでも弁解しておくと,http://foobar-tomo.blogspot.com/2008/08/z-shell-user-friendly-user-guide.htmlに『zshでは条件判定時にブラケットを2重にする』こととそのメリットが書かれていたので,これからはこう書こうと思った次第です.

*3:CPUがAtom 1.6GHzのWindows XP + Cygwin環境では,最新版のビルドに1時間,安定版も30分ほどかかりました.

*4:環境によっては,「ruby」はフルパスを書かないといけませんが,省略します.「gem」「irb」も同様です.