How to create a native gem for ruby using C
I’ve been building a native gem, read this blog post to check out how this works nowadays.
It’s not that complicated albeit a bit unclear in the beginning. It all boils down to the following folder structure.
├── Gemfile
├── Guardfile
├── Rakefile
├── ext
│ └── my_native_gem
│ ├── extconf.rb
│ └── my_native_gem.c
├── lib
├── my_native_gem.gemspec
└── test
└── test_my_native_gem.rb
- The
./ext/
directory hosts your c files andextconf.rb
for makefile generation. - The lib directory will be used for placing your compiled gem in
./test/
for your tests
Create a .gemspec with native support
Begin by setting up your .gemspec
. Here you specify the extensions
in the s.extensions
. It’s your regular gemspec except for the s.extensions
.
Gem::Specification.new do |s|
s.name = "my_native_gem"
s.version = "0.0.0"
s.summary = "Does native stuff"
s.author = "Emile Bosch"
s.extensions << "ext/my_native_gem/extconf.rb" # <= Native extension
s.add_development_dependency "rake-compiler"
end
Setup the source and make sure we can compile
Now its time to add the source files. Start by creating an ./ext/my_native_gem
directory.
Create a ./ext/my_native_gem/extconf.rb
to tell ruby how to generate a Makefile
require 'mkmf'
dir_config('my_native_gem')
create_makefile('my_native_gem')
Create a a ./ext/my_native_gem/my_native_gem.c
this holds your native gem code.
#include "ruby.h"
static VALUE hello_world(VALUE mod)
{
return rb_str_new2("hello world");
}
void Init_my_native_gem()
{
VALUE mHello = rb_define_module("Hello");
rb_define_singleton_method(mHello, "SayHello", hello_world, 0);
}
Create a Rakefile
with an extension task, this introduces the rake command rake compile
to compile a native gem.
require 'rake/extensiontask'
Rake::ExtensionTask.new('my_native_gem')
You should now be able to run rake compile
on the command line.
Time to add tests
Start of by creating a ./test
directory.
require 'minitest/autorun'
require 'my_native_gem'
class My_Native_Gem_Test < MiniTest::Unit::TestCase
def test_ok
puts Hello.SayHello
end
end
Extend your Rakefile to support test task.
require 'rake/testtask'
Rake::TestTask.new do |t|
t.libs << "test"
t.test_files = FileList['test/test*.rb']
t.verbose = true
end
You should now be able to run rake test
on the command line.
Watch for changes
- Add
guard
andguard-shell
as development dependency
Now add a Guardfile
to watch for changes. Whenever something changes in the test
directory we run the tests.
And if something changes in the ext
directory we compile the c code.
guard :shell do
watch /test/ do |m|
system('rake test')
end
watch /ext/ do |m|
system('rake compile')
end
end
You should now be able to run Guard
on the command line. On see it compile and run tests on changes.
Ship and be happy
Just ship it. OMG shipitsquirrel will be so happy!