Writing Ruby C Extensions
This post is inspired by Writing Ruby C Extensions Part 1 and Part 2 by Tenderlove Making, and uses ore as a project generator.
Aim
create a ruby gem called stree wrapping the C library libstree used to compute Suffix
Prerequisites
Install ore.
gem isntall ZenTest ore
Create Project
mine stree \
--bundler \
--gemspec \
--mini-test \
--yard \
--markdown \
--name=stree \
--version=0.0.0
cd stree
Write First Test
Before writing the first test we need to fix the generated files to work with the latest gems. We can now run
bundle exec autotest
Since I like reading spec style tests (describe
and it
blocks),
let's convert the existing test.
Now, let's add a first real test.
# test/test_stree.rb
describe Stree do
it 'should return "hello world"' do
_(Stree.hello_world).must_equal 'hello world'
end
end
The test fails with a NoMethodError
since hello_world
is not defined
for Stree
. We will now implement this method in C.
C Extension Project Layout
It is customary to put C code in ext/<project-name>/
. Hence
mkdir -p ext/$(basename $(pwd))
extconf.rb
Inside the above directory we put an extconf.rb
which will generate a
Makefile
which in turn can be used to build the C code.
# ext/stree/extconf.rb
require 'mkmf'
create_makefile 'stree'
You can test extconf.rb
by running ruby ext/stree/extconf.rb
which
should create a Makefile
. Running make
gives "make: Nothing to be
done for `all'." since we don't have a C source file yet.
stree.c
#include <ruby.h>
static VALUE hello_world(VALUE self) {
return rb_str_new2("hello world");
}
void Init_stree() {
VALUE mStree = rb_define_module("Stree");
rb_define_singleton_method(mStree, "hello_world", hello_world, 0);
}
Now re-running ruby ext/stree/extconf.rb
and make
creates stree.o
and stree.bundle
(on OSX).
Automating the build
We obviously want to automate this task. For this we can use the
rake-compiler
gem.
# Rakefile
require 'rake/extensiontask'
Rake::ExtensionTask.new 'stree'
Now we can run
bundle exec rake compile
which produces lib/stree.bundle
.