SWIG

以下、自分用メモ
Unit Testing C and C++ ... with Ruby and RSpec! を見つつ。スデに chroot な環境に swigRSpec も導入済み。なので、試してみるか。まず資料に沿って c のソース用意。

cexample.h
        /* cexample.h */
        #ifndef CEXAMPLE_H
        #define CEXAMPLE_H
        #ifdef __cplusplus
         extern "C" {
        #endif
        char* returnString(char* input);
        double returnDouble(int input);
        void  doNothing();

        #ifdef __cplusplus
         }
        #endif
        #endif
cexample.c
        /* cexample.h */

        char* returnString(char* input) {
            return input;
        }

        double returnDouble(int input) {
            return (double) input;
        }

        void  doNothing() {}
example.i
        %module example
        %{
            #include "cexample.h"
        %}
        %include "cexample.h"

で、swig 実行らしい

$ swig -ruby -Wall -o example_wrap.c example.i

あと、RSpec なソレ (cexample_spec.rb) を書いて

        require File.dirname(__FILE__) + '/spec_helper'
        require 'example'

        describe "Example (C functions)" do
          it "should be a constant on Module" do
            Module.constants.should include('Example')
          end
          it "should have the methods defined in the C header file" do
            Example.methods.should include('returnString')
            Example.methods.should include('returnDouble')
            Example.methods.should include('doNothing')
          end
        end

        describe Example, ".returnString" do
          it "should return the input char * string as a Ruby string unchanged" do
            Example.returnString("bar!").should == "bar!"
          end 
        end

        describe Example, ".returnDouble" do
          it "should return the input integer as a double" do
            Example.returnDouble(10).should == 10.0
          end
        end

        describe Example, ".doNothing" do
          it "should exist, but do nothing" do
            lambda { Example.doNothing }.should_not raise_error
          end
        end

で、RSpec 実行。

$ spec cexample_spec.rb 
/usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require': no such file to load -- ./spec_helper (LoadError)
        from /usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:27:in `require'
        from ./cexample_spec.rb:1
        from /usr/lib/ruby/gems/1.8/gems/rspec-1.1.3/lib/spec/runner/example_group_runner.rb:14:in `load'
        from /usr/lib/ruby/gems/1.8/gems/rspec-1.1.3/lib/spec/runner/example_group_runner.rb:14:in `load_files'
        from /usr/lib/ruby/gems/1.8/gems/rspec-1.1.3/lib/spec/runner/example_group_runner.rb:13:in `each'
        from /usr/lib/ruby/gems/1.8/gems/rspec-1.1.3/lib/spec/runner/example_group_runner.rb:13:in `load_files'
        from /usr/lib/ruby/gems/1.8/gems/rspec-1.1.3/lib/spec/runner/options.rb:85:in `run_examples'
        from /usr/lib/ruby/gems/1.8/gems/rspec-1.1.3/lib/spec/runner/command_line.rb:19:in `run'
        from /usr/lib/ruby/gems/1.8/gems/rspec-1.1.3/bin/spec:4
        from /usr/bin/spec:19:in `load'
        from /usr/bin/spec:19

む、オチた。spec_helper は何処に。ええと load-path を確認した方がよさげ。load-path は $: で確認できるらしい。(以下のソレは整形しております

$ ruby -e 'p $:;'
["/usr/local/lib/site_ruby/1.8", 
 "/usr/local/lib/site_ruby/1.8/i486-linux", 
 "/usr/local/lib/site_ruby/1.8/i386-linux", 
 "/usr/local/lib/site_ruby", 
 "/usr/lib/ruby/1.8", 
 "/usr/lib/ruby/1.8/i486-linux", 
 "/usr/lib/ruby/1.8/i386-linux", "."]
$

ええと spec_helper で find したら以下

# find / -name 'spec_helper*'
/usr/lib/ruby/gems/1.8/gems/rspec-1.1.3/spec/spec_helper.rb
/usr/lib/ruby/gems/1.8/gems/rspec-1.1.3/examples/pure/spec_helper.rb
/usr/lib/ruby/gems/1.8/gems/rspec-1.1.3/failing_examples/spec_helper.rb
/usr/lib/ruby/gems/1.8/gems/rspec-1.1.3/pre_commit/spec/spec_helper.rb
#

うーん。/usr/local/lib/site_ruby/1.8 あたりにリンク貼ってみるか。cexample_spec.rb のてっぺんはこうなってるんですが

require File.dirname(__FILE__) + '/spec_helper'

これはカレントディレクトリに置け、とゆー事かなぁ。

$ ruby -e 'p File.dirname(__FILE__)'
"."
$

コピーしてリトライ。

$ cp /usr/lib/ruby/gems/1.8/gems/rspec-1.1.3/spec/spec_helper.rb .
$ spec cexample_spec.rb
/usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require': no such file to load -- /home/rms/1.programming/1.sicp/spec/spec/spec_classes (LoadError)
        from /usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:27:in `require'
        from ./spec_helper.rb:12
        from /usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require'
        from /usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:27:in `require'
        from ./cexample_spec.rb:1
        from /usr/lib/ruby/gems/1.8/gems/rspec-1.1.3/lib/spec/runner/example_group_runner.rb:14:in `load'
        from /usr/lib/ruby/gems/1.8/gems/rspec-1.1.3/lib/spec/runner/example_group_runner.rb:14:in `load_files'
        from /usr/lib/ruby/gems/1.8/gems/rspec-1.1.3/lib/spec/runner/example_group_runner.rb:13:in `each'
        from /usr/lib/ruby/gems/1.8/gems/rspec-1.1.3/lib/spec/runner/example_group_runner.rb:13:in `load_files'
        from /usr/lib/ruby/gems/1.8/gems/rspec-1.1.3/lib/spec/runner/options.rb:85:in `run_examples'
        from /usr/lib/ruby/gems/1.8/gems/rspec-1.1.3/lib/spec/runner/command_line.rb:19:in `run'
        from /usr/lib/ruby/gems/1.8/gems/rspec-1.1.3/bin/spec:4
        from /usr/bin/spec:19:in `load'
        from /usr/bin/spec:19
$

今度は spec_classes か。やっぱ load-path にも無理がありそう。find してみると

# find / -name 'spec_classes*'
/usr/lib/ruby/gems/1.8/gems/rspec-1.1.3/spec/spec/spec_classes.rb
#

にあるんですが /usr/lib/ruby/1.8 にリンク作ってしまえ

# ln /usr/lib/ruby/gems/1.8/gems/rspec-1.1.3/spec/spec/spec_classes.rb /usr/lib/ruby/1.8/spec_classes.rb
# ln /usr/lib/ruby/gems/1.8/gems/rspec-1.1.3/spec/spec/runner_spec.rb /usr/lib/ruby/1.8/runner_spec.rb 
# ln /usr/lib/ruby/gems/1.8/gems/rspec-1.1.3/spec/spec/translator_spec.rb /usr/lib/ruby/1.8/tancelator_spec.rb
#

駄目だ。load-path いぢった方が早そげ。てーか何故にカレントディレクトリなんだ、と。cexample_spec.rb の先頭は以下に修正。

#        require File.dirname(__FILE__) + '/spec_helper'
        require 'spec_helper'
        require 'example'

あとコピッてきたファイル達も削除して cexample_spec.rb の先頭に以下を挿入してみる

dir = File.dirname("/usr/lib/ruby/gems/1.8/gems/rspec-1.1.3/spec/spec")
lib_path = File.expand_path("#{dir}")
$LOAD_PATH.unshift lib_path unless $LOAD_PATH.include?(lib_path)

で、リトライ

$ spec cexample_spec.rb
/usr/lib/ruby/gems/1.8/gems/rspec-1.1.3/spec/../lib/spec/expectations/differs/default.rb:5: You must gem install diff-lcs to use diffing (RuntimeError)
        from /usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require'
        from /usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:27:in `require'
        from /usr/lib/ruby/gems/1.8/gems/rspec-1.1.3/spec/spec_helper.rb:13
        from /usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require'
        from /usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:27:in `require'
        from ./cexample_spec.rb:6
        from /usr/lib/ruby/gems/1.8/gems/rspec-1.1.3/lib/spec/runner/example_group_runner.rb:14:in `load'
        from /usr/lib/ruby/gems/1.8/gems/rspec-1.1.3/lib/spec/runner/example_group_runner.rb:14:in `load_files'
        from /usr/lib/ruby/gems/1.8/gems/rspec-1.1.3/lib/spec/runner/example_group_runner.rb:13:in `each'
        from /usr/lib/ruby/gems/1.8/gems/rspec-1.1.3/lib/spec/runner/example_group_runner.rb:13:in `load_files'
        from /usr/lib/ruby/gems/1.8/gems/rspec-1.1.3/lib/spec/runner/options.rb:85:in `run_examples'
        from /usr/lib/ruby/gems/1.8/gems/rspec-1.1.3/lib/spec/runner/command_line.rb:19:in `run'
        from /usr/lib/ruby/gems/1.8/gems/rspec-1.1.3/bin/spec:4
        from /usr/bin/spec:19:in `load'
        from /usr/bin/spec:19

ヤり方的に微妙ですが、次の段階にイケたみたい。diff-lcs というソレを gem で入れれば良いのでしょうか。しかし gem 重い。で、リトライしてみましたが NG

$ spec cexample_spec.rb
/usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require': no such file to load -- example (LoadError)
        from /usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:27:in `require'
        from ./cexample_spec.rb:7
        from /usr/lib/ruby/gems/1.8/gems/rspec-1.1.3/lib/spec/runner/example_group_runner.rb:14:in `load'
        from /usr/lib/ruby/gems/1.8/gems/rspec-1.1.3/lib/spec/runner/example_group_runner.rb:14:in `load_files'
        from /usr/lib/ruby/gems/1.8/gems/rspec-1.1.3/lib/spec/runner/example_group_runner.rb:13:in `each'
        from /usr/lib/ruby/gems/1.8/gems/rspec-1.1.3/lib/spec/runner/example_group_runner.rb:13:in `load_files'
        from /usr/lib/ruby/gems/1.8/gems/rspec-1.1.3/lib/spec/runner/options.rb:85:in `run_examples'
        from /usr/lib/ruby/gems/1.8/gems/rspec-1.1.3/lib/spec/runner/command_line.rb:19:in `run'
        from /usr/lib/ruby/gems/1.8/gems/rspec-1.1.3/bin/spec:4
        from /usr/bin/spec:19:in `load'
        from /usr/bin/spec:19

こりゃアレだな。参照元が微妙なの??
先に RSpec の勉強した方が良さげだな。ruby 復習してみる必要もありそげ。しかし脱線してるのにもホドがある感満点。

てーか、よく考えたら C のコードをコンパイルとかしてないし。swig って何か、という部分からおさらいした方が良さげ。で、google 先生に聞いてみたらswigの使い方のメモ書きというコンテンツを発見。感謝しつつ。
まず extconf.rg という名前で以下

require "mkmf"
$CFLAGS += " -Wall "
$LOCAL_LIBS += " -llocallib " 
create_makefile("sample")

で、以下を実行、とある。

$ ruby extconf.rb; make; make install

とりあえず出力される makefile の中を見とく必要あり。で実行したら mkmf が load できねぇ、と叱られる。むむ、とウナりつつ現実トウヒ的昼寝。
起きてからいくつか google 先生にご教示頂く

なるほど、ruby1.8-dev か。Makefile もデキている模様。とりあえず make してみる

$ make
gcc -I. -I/usr/lib/ruby/1.8/i486-linux -I/usr/lib/ruby/1.8/i486-linux -I.  -fPIC -Wall -g -fno-strict-aliasing -O2  -fPIC -Wall   -c cexample.c
<built-in>:0: internal compiler error: Segmentation fault
Please submit a full bug report,
with preprocessed source if appropriate.
See <URL:http://gcc.gnu.org/bugs.html> for instructions.
For Debian GNU/Linux specific bug reporting instructions,
see <URL:file:///usr/share/doc/gcc-4.1/README.Bugs>.
make: *** [cexample.o] Error 1
$

げ。なんだそれは

$ gcc -v
Using built-in specs.
Target: i486-linux-gnu
Configured with: ../src/configure -v --enable-languages=c,c++,fortran,objc,obj-c++,treelang --prefix=/usr --enable-shared --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --enable-nls --program-suffix=-4.1 --enable-__cxa_atexit --enable-clocale=gnu --enable-libstdcxx-debug --enable-mpfr --with-tune=i686 --enable-checking=release i486-linux-gnu
Thread model: posix
gcc version 4.1.2 20061115 (prerelease) (Debian 4.1.1-21)

gcc-3.4 にしてみるか。

続々

まず、gcc-3.4 を導入。gcc-4.1 は削除したら微妙みたいなので Makefile を修正。で、make して example.so がカレントディレクトリに出力されたのを確認後

$ ruby cexample_spec.rb
.....

Finished in 0.058914 seconds

5 examples, 0 failures
$

を。動いたぞ。あとは RSpec をおさらいしとけばなんとかなりそげ。