是非に及ばず

プログラミングに関する話題などを書いていきます(主にRailsやAndroidアプリ開発について)

ruby2.2でkyotocabinet-rubyがインストールできない問題の対処法

通常の流れとしては、
ruby extconf.rb
make
make install
で良いはずなのだが、ruby2.2で試したところエラーが発生した。

具体的なエラーはこのようなもの。

$ wget http://fallabs.com/kyotocabinet/rubypkg/kyotocabinet-ruby-1.32.tar.gz
$ tar xvzf kyotocabinet-ruby-1.32.tar.gz
$ cd  kyotocabinet-ruby-1.32
$ ruby extconf.rb

*** extconf.rb failed ***
Could not create Makefile due to some reason, probably lack of necessary
libraries and/or headers.  Check the mkmf.log file for more details.  You may
need configuration options.

Provided configuration options:
        --with-opt-dir
        --without-opt-dir
        --with-opt-include
        --without-opt-include=${opt-dir}/include
        --with-opt-lib
        --without-opt-lib=${opt-dir}/lib
        --with-make-prog
        --without-make-prog
        --srcdir=.
        --curdir
        --ruby=/home/watanabe/.rbenv/versions/2.2.2/bin/$(RUBY_BASE_NAME)
        --with-kyotocabinet-dir
        --without-kyotocabinet-dir
        --with-kyotocabinet-include
        --without-kyotocabinet-include=${kyotocabinet-dir}/include
        --with-kyotocabinet-lib
        --without-kyotocabinet-lib=${kyotocabinet-dir}/lib
extconf.rb:18:in `<main>': uninitialized constant Config (NameError)

exitconf.rbのConfigという定数というかクラスが未定義という事でエラーになっている。
Configが使われている場所を調べる。

$ grep Config extconf.rb
Config::CONFIG["CPP"] = "g++ -E"

ruby2.2ではConfigがRbConfigに変わっているので、ここを書き換えれば良いはず。

$ cp -a extconf.rb extconf.rb.bak
$ sed -i 's/Config/RbConfig/g' extconf.rb
$ diff extconf.rb extconf.rb.bak
18c18
< RbConfig::CONFIG["CPP"] = "g++ -E"
---
> Config::CONFIG["CPP"] = "g++ -E"

再度、extconf.rbを実行すると無事成功!

$ ruby extconf.rb
setting variables ...
  $CFLAGS = -I. -I/usr/local/include -Wall $(cflags) -O2
  $LDFLAGS = -L. -L/home/watanabe/.rbenv/versions/2.2.2/lib  -fstack-protector -rdynamic -Wl,-export-dynamic -L. -L/usr/local/lib
  $libs =  -lkyotocabinet -lz -lstdc++ -lrt -lpthread -lm -lc
checking for kccommon.h... yes
creating Makefile

次にmakeを実行すると、またしてもエラー・・・。

$ make
compiling kyotocabinet.cc
kyotocabinet.cc: In static member function ‘static void NativeFunction::execute(NativeFunction*)’:
kyotocabinet.cc:602: error: ‘rb_thread_blocking_region’ was not declared in this scope
make: *** [kyotocabinet.o] Error 1

ネットで調べたところ、rb_thread_blocking_regionという関数がなくなったから起きているらしい。

kyotocabinet.ccの該当行は以下のようになっている。

600   static void execute(NativeFunction* func) {
601 #if defined(_KC_YARV_)
602     rb_thread_blocking_region(execute_impl, func, RUBY_UBF_IO, NULL);
603 #else
604     func->operate();
605 #endif
606   }

_KC_YARV_が定義されていたらrb_thread_blocking_regionを使用するという事になっているが、
代替手段としてfunc->operate()が用意されている。
という事は、常にfunc->operate()を使うようにしてやれば良いはず。

調べたところ、_KC_YARV_ は601~605行目の処理のみで利用されているので影響範囲が小さい。

24 #if RUBY_VM >= 1
25 #define _KC_YARV_
26 #endif

とりあえず、25行目を空にしてやれば良いだろう。

$ cp -a kyotocabinet.cc kyotocabinet.cc.bak
$ sed -i 's/define _KC_YARV_//g' kyotocabinet.cc
$ diff kyotocabinet.cc kyotocabinet.cc.bak
25c25
< #
---
> #define _KC_YARV_

再度、makeを実行するとエラーが起きずにコンパイルが成功した。

$ make
compiling kyotocabinet.cc
linking shared-object kyotocabinet.so

とりあえず、インストールする。

$ make install

テストを実行して問題がないか確認するが、やっぱりエラー。

$ ruby test.rb
test.rb:33:in `<main>': uninitialized constant Config (NameError)

例によってConfigをRbConfigにしてやれば良い。

影響範囲を調べる。なぜかRbConfigに直ってるやつもある・・・
$ grep Config test.rb
rubycmd = Config::CONFIG["bindir"] + "/" + RbConfig::CONFIG['ruby_install_name']

$ cp -a test.rb test.rb.bak
$ sed -i 's/rubycmd = Config/rubycmd = RbConfig/' test.rb
$ diff test.rb test.rb.bak
33c33
< rubycmd = RbConfig::CONFIG["bindir"] + "/" + RbConfig::CONFIG['ruby_install_name']
---
> rubycmd = Config::CONFIG["bindir"] + "/" + RbConfig::CONFIG['ruby_install_name']

再度、テストを実行(少し時間かかる)すると、無事に全てのテストが成功。

$ ruby test.rb
001/105: kctest.rb order ':' '10000': ok
002/105: kctest.rb order -rnd ':' '10000': ok
003/105: kctest.rb order -etc ':' '10000': ok
...
105/105: kctest.rb misc 'casket.kcf': ok
105 tests were all ok

これで安心してruby2.2でもkyotocabinetを使う事が出来る。