GentooのRuby開発ガイド

rubyって日本でなかなか人気なんですが結構パッケージの人はあんまり足りてません。(gemでいいからかも?)まあ、でもdistroでパッケージがあると嬉しいですよね。

Gentooでもなかなか人手不足で特にruby1.9対応をやる気がある人があんまりいないみたいです。

ということで、Gentooを好きなみなさん、Rubyを好きなみなさん GentooRubyまわりの開発に参加してみませんか?

とはいえ、それってなにをしたらいいの?どうしたらいいの?と思われることでしょう。ここではそういった疑問にお答えします。

Bugを見つけるために

Gentooの開発はBugzilla https://bugzilla.gentoo.org/ を中心に行なわれています。このBugzillaではその名のとおりGentooに関する全ての「バグ」が扱われています。「バグ」と言ってもその名から想像できる「不具合」だけでなく「新しいパッケージがほしい」だとか「このパッケージをstable」にしてくれ、などの「要求」もバグにふくまれます。そういうバグはどうやって見つければいいでしょうか。

Bugzilla

まずはすでに報告されているバグを知りましょう。あれこれ探してバグを見つけてやった!と思ったら報告されていて解決間際だった…なんて泣きたくなりますからね。BugzillaでRubyのバグを探します。

とりあえず https://bugs.gentoo.org/ にアクセスして"New Account"でアカウントを作っておきましょう。(SSLの警告が出ますね…。 http://www.cacert.org から証明書とってください…。

さて早速 ruby とでもいれて検索してみましょう。 https://bugs.gentoo.org/buglist.cgi?quicksearch=ruby になるはずです。いっぱいありますね、うちでは184個見つかりました。

厳密に探すならばAdvanced Search https://bugs.gentoo.org/query.cgi?format=advanced から Search By People のところ "the Bug Assignee" "contains" "ruby@gentoo.org" にしてみましょう。 https://bugs.gentoo.org/buglist.cgi?resolution=---;emailtype1=substring;emailassigned_to1=1;query_format=advanced;email1=ruby@gentoo.org が開いたんじゃないかと思います。GentooRubyチームが担当となっているバグです。

また、Ruby19関連では https://bugs.gentoo.org/show_bug.cgi?id=ruby19 というバグがあります。これはRuby19でうまく動かないパッケージのバグを追跡している"TRACKER"と呼ばれるちょっと特殊なバグですね。Ruby19をGentooで早く動かしたければ、このバグの"Depends on"にリストされているバグをちぎっては投げして解決してやらねばなりません。

euscan

これで報告されているバグはわかりました。次はバグを探してみます。

euscan http://euscan.iksaif.net/ebuildからupstreamの情報をチェックして、パッケージの次のバージョンのURLを推測しスキャンしているサイトです。 まあ、難しいことはいいので見てみてください。

Ruby関連のものは http://euscan.iksaif.net/herds/ruby/view/ ですね。一番右の Unpackaged のものを見つけたらがんがん報告…と言いたいところですが、はっきり言って unpackaged 多すぎるのでとりあえずざっと見て自分が必要だと思うものを報告していきましょう。

FEATURES=test

今度は不具合を探してみましょう。まあ、ビルドできないパッケージやらは特になにもしなくてもめぐりあわせてしまうでしょうからあえては書きません。ここでは FEATURES=test というものに注目します。

Rubyは結構テストを書いているような気がします。ビルドの結果が正しいかどうかを確認するのに、これを使わない手はありませんね。 FEATURES=test をつけて emerge するとパッケージのテストが走ります。ここでテストが失敗すればバグを報告すればいいです。できたら、upstreamのほうにも報告したほうがいいですね。

また、一部のテストは実行に特殊な環境を必要とすることがあります。そういう時はebuildに RESTRICT=test としてテストを実行させないようにしておくべきです。環境依存のものを見つけたら、RESTRICT=testするべきなんじゃない?という報告をするといいわけですね。

Bugを直すために

もうここまででたくさんバグを見つけたでしょうか。 たくさん見つけたそのバグをがんがん直していきましょう。
バグを直すために、ebuildや開発ツールについて学びましょう。ここではRuby関連のバグを直すのにターゲットをしぼり、ebuildの基本については別のエントリにわけました。

rubyebuild

いろいろなebuildから抜粋しながら見てみましょう。

# dev-ruby/rails/rails-3.0.9.ebuild
USE_RUBY="ruby18 ree18"
# net-misc/mikutter/mikutter-0.0.3.14.ebuild
USE_RUBY="ruby18 ruby19"

USE_RUBYrubyのパッケージの中で一番大切な変数です。これはそのパッケージがどのRuby実装に対してインストール可能であるかを表しています。つまり、ここでは Rails 3.0.9 は Ruby (MRI) 1.8.x とRuby Enterprise Edition 1.8.x でインストール可能であり、mikutterは Ruby (MRI) 1.8.x と Ruby (MRI) 1.9.x でインストール可能であると言っています。この他にも jrubyJRuby を意味するようになっています。

# net-misc/mikutter/mikutter-0.0.3.14.ebuild
ruby_add_rdepend "dev-ruby/ruby-gtk2
	dev-ruby/rcairo
	dev-ruby/httpclient"

ここでは RDEPEND (実行時依存関係)を追加しています。このパッケージが正常に動作するために、他のどのパッケージが必要か?ということですね。

普通のRuby以外のパッケージではこれを

RDEPEND="dev-ruby/ruby-gtk2
	dev-ruby/rcairo
	dev-ruby/httpclient"

というふうに変数に代入する形で書きます。しかし、 Rubyのパッケージの場合 ruby1.8用、ruby1.9用などそれぞれのRuby実装ごとに依存パッケージがインストールされていなければいけません。この「有効な実装ごとにそれぞれ使用するRubyパッケージに依存する」というこを自分で書くこともできますが、面倒なのでそれを自動化する関数が ruby_add_rdepend です。まあ、難しいことは考えずに Rubyのパッケージは ruby_add_rdepend を使って書く、と覚えてください。

# dev-ruby/mysql-ruby/mysql-ruby-2.8.2-r1.ebuild
all_ruby_prepare() {
	epatch "${FILESDIR}/${P}-test2.patch"
}

all_ruby_prepare という名の関数を定義しています。 ebuildでのインストールは unpack (ソースコード展開) -> prepare (パッチあてたりする) -> configure (パッケージの設定する) -> compile (コンパイルする) -> install (インストールする) という流れで行なわれます。Rubyのパッケージは各実装ごとにインストールされるので、それぞれの項目が「全てのRuby実装で実行すること」「それぞれの実装ごとに実行すること」にわけられます。これを all_ruby_* とか each_ruby_* という関数で定義し、実行することを書いていきます。

ということで、ここでは全てのRuby実装でソースコードにパッチをあてています。 epatch はebuildの中で使える特殊なpatchです。いろいろ細かいことやってます。気にせずにpatchはこいつであてちゃってください。変数 FILESDIR はebuildのあるディレクトリのfilesディレクトリになります。ようするにここでは /usr/portage/dev-ruby/mysql-ruby/files/ になるので /usr/portage/dev-ruby/mysql-ruby/files/mysql-ruby-2.8.2-test2.patch がパッチとしてあてられます。

まあ、 all_ruby_prepare と epatch がわかればコードにパッチをあてられるようになるので、結構な数のバグは(パッチ書ければ)直せるようになるんじゃないかなーと思います。

# dev-ruby/mysql-ruby/mysql-ruby-2.8.2-r1.ebuild
each_ruby_configure() {
	${RUBY} extconf.rb --with-mysql-config "${EPREFIX}/usr/bin/mysqlconfig" || die
}

また、 each_ruby_* の中ではそれぞれの Ruby実装の実行バイナリへのパスが ${RUBY} 変数の中に入っています。

ということで、こう書くとそれぞれの実装で extconf.rb を実行できる、というわけですね。

最後に、 die 関数はその名の通りに、パッケージのインストールプロセスが「死にます」 致命的な失敗になった時に die してインストールを切り上げるわけですね。

repoman

さてはて、 ebuild を直せたと思ったら今度は本当に直ったのかどうかテストしてみなくてはいけませんね。しかし、ebuildを直してすぐemergeしてもうまくはいかないでしょう。パッケージのディレクトリに Manifest というファイルがあり、これにebuildチェックサムやファイルサイズが書いてありこれが一致しないと不正なebuildだと見なされるからです。なので、ebuildを書いたら、この Manifestを再生成しましょう。パッケージのディレクトリに移動して、 repoman manifest とすればいいです。

また、この repoman というコマンドは ebuild がちゃんと書かれているかどうかチェックするためにも使われます。 repoman fullとしてみてください。警告やエラーがでましたか? 直しましょう。中には気にすることがないものもあります。 notadded とか allmasked とか。

報告する

パッケージを直したら https://bugs.gentoo.org に報告しましょう。具体的にどう報告するかは…まあ英語読んでください…内容としては

  • emerge --info
  • Rubyのバージョン
  • 失敗した時の /var/tmp/portage/*/*/temp/build.log
  • どういう patch を ebuildにあてたか
  • どういうファイルが ebuild で使われたか (epatch で使ったものとか)

を書きましょう。

具体的には

ここでは具体例を見ていきます。

テストをする

add USE_RUBY="ruby19" to dev-ruby/sqlite3-ruby-1.3.3 https://bugs.gentoo.org/show_bug.cgi?id=358809 を題材にテストの方法について見ます。

"I added USE_RUBY="ruby19" to the ebuild and tested, and test succeeded." とありますが、その後反応がないようなので… build.log でもはって「おら、確かにテスト通ってるで!」と言ってやりましょう!

まずは Ruby19を使う準備をします。 /etc/make.conf に RUBY_TARGETS="ruby18 ruby19"のように ruby19 を入れておきます。これは自分のマシンで使いたいRuby実装のリストになります。RUBY_TARGETSの設定とパッケージのUSE_RUBYの設定でマッチした実装にパッケージがインストールされるようになっています。Ruby19はまだマスクされてますから、 /etc/portage/package.unmask に "dev-lang/ruby" と書いておきます。多分これで ruby19が入るはずです。

つぎに /usr/portage/dev-ruby/sqlite3-ruby/sqlite3-ruby-1.3.3.ebuild を開いて USE_RUBY に ruby19を追加します。 忘れずに repoman manifest をして… emerge を実行しましょう!

sudo FEATURES='test keeptemp' emerge -1 sqlite3-ruby

ということで、 emerge がうまく終わったら /var/tmp/portage/dev-ruby/sqlite3-ruby-1.3.3/temp/build.log を見てみます。普通は emerge がうまくいけばこのファイルは削除されていますが、 FEATURES=keeptemp しているので残っています。

 * Running test phase for ruby19 ...
(in /var/tmp/portage/dev-ruby/sqlite3-ruby-1.3.3/work/ruby19/sqlite3-ruby-1.3.3)
WARNING: rake-compiler found compiled files in 'ext/sqlite3' directory. Please remove them.
rake-compiler must be configured first to enable cross-compilation
rake-compiler must be configured first to enable cross-compilation
/usr/bin/ruby19 -w -I.:lib:bin:test:. -e 'require "rubygems"; require "test/unit"; require "test/test_backup.rb"; require "test/test_collation.rb"; require "test/test_database.rb"; require "test/test_database_readonly.rb"; require "test/test_deprecated.rb"; require "test/test_encoding.rb"; require "test/test_integration.rb"; require "test/test_integration_open_close.rb"; require "test/test_integration_pending.rb"; require "test/test_integration_resultset.rb"; require "test/test_integration_statement.rb"; require "test/test_sqlite3.rb"; require "test/test_statement.rb"; require "test/test_statement_execute.rb"' -- 
Loaded suite -e
Started
...................................................................................................................................................................S..................................................
Finished in 3.790359 seconds.

  1) Skipped:
test_busy_handler_outwait(TC_Integration_Pending) [/var/tmp/portage/dev-ruby/sqlite3-ruby-1.3.3/work/ruby19/sqlite3-ruby-1.3.3/test/test_integration_pending.rb:23]:
not working in 1.9

214 tests, 303 assertions, 0 failures, 0 errors, 1 skips

と、いうことでちゃんと ruby19 でのテストが実行されて失敗がありません! この build.log を添付して、上のような抜粋をコメントに書いて、うちでも動いたよー?まだー?とかコメントしてくればいいです。

新しいパッケージ

Ruby D-Bus https://trac.luon.net/ruby-dbus/ のパッケージを書いてみましょう。

まずは自分で書いた ebuild をおいておくディレクトリをきめます。 ~/portage にしておきましょうか。

% mkdir -p ~/portage/profiles
% echo myoverlay > ~/portage/profiles/repo_name

自分で書いた ebuild用のリポジトリにも名前をつける必要があります。 profiles/repo_name に適当に名前を書いておきます。

dbus-rubyディレクトリを作ります。

% mkdir -p ~/portage/dev-ruby/ruby-dbus
% cd ~/portage/dev-ruby/ruby-dbus

さあ dbus-ruby-0.7.0.ebuild を書いていきましょう。 基本のテンプレートはこれです。

# Copyright 1999-2011 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
# $Header: $

EAPI=3

DESCRIPTION=""
HOMEPAGE=""
SRC_URI=""

LICENSE=""
SLOT="0"
KEYWORDS="~amd64"
IUSE=""

DEPEND=""
RDEPEND="${DEPEND}"

まずは説明とかを適当にうめていきます。

DESCRIPTION="Ruby module for interaction with D-Bus"
HOMEPAGE="https://trac.luon.net/ruby-dbus/"
SRC_URI="https://rubygems.org/downloads/ruby-dbus-0.7.0.gem"

LICENSE="LGPL-2.1"
SLOT="0"
KEYWORDS="~amd64"
IUSE=""

DEPEND=""
RDEPEND="${DEPEND}"

Rubyのパッケージは、ほとんどの作業を ruby-fakegem という eclass (ライブラリみたいなもの)がやってくれます。なので、そいつを読みこみ、依存関係をうめます。

EAPI=3

USE_RUBY="ruby18 ruby19"

inherit ruby-fakegem

DESCRIPTION="Ruby module for interaction with D-Bus"
HOMEPAGE="https://trac.luon.net/ruby-dbus/"
SRC_URI="https://rubygems.org/downloads/ruby-dbus-0.7.0.gem"

LICENSE="LGPL-2.1"
SLOT="0"
KEYWORDS="~amd64"
IUSE=""

DEPEND="ruby_targets_ruby18? ( $(ruby_implementation_depend ruby18 '>=' -1.8.7) )"
RDEPEND=""

それではこれでemergeできるかどうか試して………ちゃんとインストールできてるかどうかを試してみましょう。

% repoman manufest
% sudo emerge -1 ruby-dbus
% qlist ruby-dbus
/usr/lib64/ruby/gems/1.8/specifications/ruby-dbus-0.7.0.gemspec
/usr/lib64/ruby/gems/1.8/gems/ruby-dbus-0.7.0/lib/dbus/core_ext/class/attribute.rb
/usr/lib64/ruby/gems/1.8/gems/ruby-dbus-0.7.0/lib/dbus/core_ext/kernel/singleton_class.rb
/usr/lib64/ruby/gems/1.8/gems/ruby-dbus-0.7.0/lib/dbus/core_ext/module/remove_method.rb
/usr/lib64/ruby/gems/1.8/gems/ruby-dbus-0.7.0/lib/dbus/auth.rb
/usr/lib64/ruby/gems/1.8/gems/ruby-dbus-0.7.0/lib/dbus/bus.rb
/usr/lib64/ruby/gems/1.8/gems/ruby-dbus-0.7.0/lib/dbus/error.rb
/usr/lib64/ruby/gems/1.8/gems/ruby-dbus-0.7.0/lib/dbus/export.rb
/usr/lib64/ruby/gems/1.8/gems/ruby-dbus-0.7.0/lib/dbus/introspect.rb
/usr/lib64/ruby/gems/1.8/gems/ruby-dbus-0.7.0/lib/dbus/marshall.rb
/usr/lib64/ruby/gems/1.8/gems/ruby-dbus-0.7.0/lib/dbus/matchrule.rb
/usr/lib64/ruby/gems/1.8/gems/ruby-dbus-0.7.0/lib/dbus/message.rb
/usr/lib64/ruby/gems/1.8/gems/ruby-dbus-0.7.0/lib/dbus/type.rb
/usr/lib64/ruby/gems/1.8/gems/ruby-dbus-0.7.0/lib/dbus.rb
/usr/lib64/ruby/gems/1.9.1/specifications/ruby-dbus-0.7.0.gemspec
/usr/lib64/ruby/gems/1.9.1/gems/ruby-dbus-0.7.0/lib/dbus/core_ext/class/attribute.rb
/usr/lib64/ruby/gems/1.9.1/gems/ruby-dbus-0.7.0/lib/dbus/core_ext/kernel/singleton_class.rb
/usr/lib64/ruby/gems/1.9.1/gems/ruby-dbus-0.7.0/lib/dbus/core_ext/module/remove_method.rb
/usr/lib64/ruby/gems/1.9.1/gems/ruby-dbus-0.7.0/lib/dbus/auth.rb
/usr/lib64/ruby/gems/1.9.1/gems/ruby-dbus-0.7.0/lib/dbus/bus.rb
/usr/lib64/ruby/gems/1.9.1/gems/ruby-dbus-0.7.0/lib/dbus/error.rb
/usr/lib64/ruby/gems/1.9.1/gems/ruby-dbus-0.7.0/lib/dbus/export.rb
/usr/lib64/ruby/gems/1.9.1/gems/ruby-dbus-0.7.0/lib/dbus/introspect.rb
/usr/lib64/ruby/gems/1.9.1/gems/ruby-dbus-0.7.0/lib/dbus/marshall.rb
/usr/lib64/ruby/gems/1.9.1/gems/ruby-dbus-0.7.0/lib/dbus/matchrule.rb
/usr/lib64/ruby/gems/1.9.1/gems/ruby-dbus-0.7.0/lib/dbus/message.rb
/usr/lib64/ruby/gems/1.9.1/gems/ruby-dbus-0.7.0/lib/dbus/type.rb
/usr/lib64/ruby/gems/1.9.1/gems/ruby-dbus-0.7.0/lib/dbus.rb

インストールされているようですね!

最後に

質問があれば @naota344 やコメントなどをください。