Ruby拡張ライブラリを含むGemのつくりかた

Ruby

今回は拡張ライブラリを含むGemを開発するための流れを学習するために、簡単なgemをつくることにします。
簡単なC言語で記述された拡張をGemで配布できる最低限の状態まで構築します。

スポンサーリンク

Gemの雛形を生成する。

$ bundle gem ext_example
      create  ext_example/Gemfile
      create  ext_example/Rakefile
      create  ext_example/LICENSE.txt
      create  ext_example/README.md
      create  ext_example/.gitignore
      create  ext_example/ext_example.gemspec
      create  ext_example/lib/ext_example.rb
      create  ext_example/lib/ext_example/version.rb
Initializing git repo in /Users/hirocaster/src/github.com/hirocaster/ext_example

rake-compiler を導入する

ext_example.gemspecを記述する
spec.summaryspec.descriptionは適当に記述する。
spec.extensionsには後程つくるファイルを記述しておく。

@@ -8,8 +8,8 @@ Gem::Specification.new do |spec|
   spec.version       = ExtExample::VERSION
   spec.authors       = ["hirocaster"]
   spec.email         = ["hohtsuka@gmail.com"]
-  spec.summary       = %q{TODO: Write a short summary. Required.}
-  spec.description   = %q{TODO: Write a longer description. Optional.}
+  spec.summary       = %q{C extensions example}
+  spec.description   = %q{C extensions example}
   spec.homepage      = ""
   spec.license       = "MIT"
@@ -18,6 +18,9 @@ Gem::Specification.new do |spec|
   spec.test_files    = spec.files.grep(%r{^(test|spec|features)/})
   spec.require_paths = ["lib"]
+  spec.extensions = %w{ext/ext_example/extconf.rb}
+
   spec.add_development_dependency "bundler", "~> 1.5"
   spec.add_development_dependency "rake"
+  spec.add_development_dependency "rake-compiler"
 end

bundle installを実行してrake-compilerを導入する。

$ bundle install
Fetching gem metadata from https://rubygems.org/..
Resolving dependencies...
Using bundler (1.5.3)
Using ext_example (0.0.1) from source at .
Installing rake (11.2.2)
Installing rake-compiler (0.9.9)
Your bundle is complete!
Gems in the group production were not installed.
I was installed into ./vendor/bundle

rake-compilerを稼動させる環境をつくる

拡張ライブラリはextディレクトリ以下に記述していく。

$ mkdir -p ext/ext_example/
$ touch ext/ext_example/extconf.rb

extconf.rb にはMakfileを作成するおまじないを記述。

require 'mkmf'
create_makefile('ext_example/ext_example')

Rakefileに先程のextconf.rbを利用してTaskを定義できるように記述

require "rake/extensiontask"
Rake::ExtensionTask.new("ext_example") do |ext|
  ext.lib_dir = "lib/ext_example"
end

これでC言語で記述したライブラリをコンパイルするためのTaskが定義されるはずなので、確認する。

$ rake -T
rake build                # Build ext_example-0.0.1.gem into the pkg directory
rake clean                # Remove any temporary products
rake clobber              # Remove any generated files
rake compile              # Compile all the extensions
rake compile:ext_example  # Compile ext_example
rake install              # Build and install ext_example-0.0.1.gem into system gems
rake release              # Create tag v0.0.1 and build and push ext_example-0.0.1.gem to Rubygems

ここまでの準備でrake compile:ext_exampleなどのタスクが表示されるのを確認できる。

拡張ライブラリを書く

拡張ライブラリの中心となるext/ext_example/ext_example.cを記述する。

Rubyは拡張ライブラリをロードする時に「Init_ライブラリ名」と いう関数を自動的に実行します.
https://github.com/ruby/ruby/blob/trunk/doc/extension.ja.rdoc#cコードを書く

ということなのでInit_ext_example()を定義。

#include <stdio.h>
void Init_ext_example()
{
  puts("Hello World!");
}

この拡張をロードするようにlib/ext_example.rbに記述する。

@@ -1,4 +1,5 @@
 require "ext_example/version"
+require "ext_example/ext_example"
 module ExtExample
   # Your code goes here...

コンパイルする。

$ rake compile:ext_example
mkdir -p tmp/universal.x86_64-darwin14/ext_example/2.0.0
cd tmp/universal.x86_64-darwin14/ext_example/2.0.0
/System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/bin/ruby -I. ../../../../ext/ext_example/extconf.rb
creating Makefile
cd -
cd tmp/universal.x86_64-darwin14/ext_example/2.0.0
/usr/bin/make
compiling ../../../../ext/ext_example/ext_example.c
linking shared-object ext_example/ext_example.bundle
cd -
mkdir -p tmp/universal.x86_64-darwin14/stage/lib/ext_example
install -c tmp/universal.x86_64-darwin14/ext_example/2.0.0/ext_example.bundle lib/ext_example/ext_example.bundle
cp tmp/universal.x86_64-darwin14/ext_example/2.0.0/ext_example.bundle tmp/universal.x86_64-darwin14/stage/lib/ext_example/ext_example.bundle

実際に動作させてみる。

$ irb
irb(main):001:0> require "ext_example"
Hello World!
=> true
irb(main):002:0>

Hello World!が出力されたので、無事に動作することが確認でききました。
ここまででC言語で記述した拡張を含む、最低限のGemを作成することができました。

拡張ライブラリの処理を実用的にしていくためには

Rubyの拡張ライブラリを開発するためのドキュメント が日本語であります。こちらを参考にしていくと、やりたいことをC言語でどのように記述してRubyと連携をとっていけばいいのか理解できるようになると思います。

タイトルとURLをコピーしました