Packaging a Ruby C Extension as a Gem
Posted 1-30-2011 by
Matthew Downey
Recently, I created my first ruby gem. It also happened to be a C extension. While there is a fairly good deal of information available on the web on how to package a basic gem, there isn't very much information about how to package a gem that includes a C extension.
Bundler has a nice feature that creates a project skeleton for a gem, so it is easiest to start there:
bundle gem hello_world
create hello_world/Gemfile
create hello_world/Rakefile
create hello_world/.gitignore
create hello_world/hello_world.gemspec
create hello_world/lib/hello_world.rb
create hello_world/lib/hello_world/version.rb
Initializating git repo in /home/matt/hello_worldmkdir -p hello_world/ext/hello_world
#include "ruby.h" #include <stdio.h> static VALUE method_hello_world(VALUE self) { printf("Hello World!\n"); return Qnil; } VALUE HelloWorldModule; VALUE HelloWorldClass; void Init_hello_world() { HelloWorldModule = rb_define_module("HelloWorld"); HelloWorldClass = rb_define_class_under(HelloWorldModule, "HelloWorld", rb_cObject); rb_define_method(HelloWorldClass, "hello_world", method_hello_world, 0); }
require 'mkmf' create_makefile("hello_world")
require 'hello_world/hello_world' module HelloWorld # Your code goes here... end
require 'rubygems' require 'rake' require 'rake/extensiontask' require 'bundler' Rake::ExtensionTask.new("hello_world") do |extension| extension.lib_dir = "lib/hello_world" end task :chmod do File.chmod(0775, 'lib/hello_world/hello_world.so') end task :build => [:clean, :compile, :chmod] Bundler::GemHelper.install_tasks
# -*- encoding: utf-8 -*- $:.push File.expand_path("../lib", __FILE__) require "hello_world/version" Gem::Specification.new do |s| s.name = "hello_world" s.version = HelloWorld::VERSION s.platform = Gem::Platform::RUBY s.authors = ["Matthew Downey"] s.email = ["mattddowney@gmail.NOSPAM.com"] s.homepage = "http://www.writehack.com" s.summary = %q{Hello World!} s.description = %q{Gem that prints Hello World!} s.rubyforge_project = "hello_world" s.files = `git ls-files`.split("\n") s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } s.require_paths = ["lib"] s.extensions = ["ext/hello_world/extconf.rb"] end
rake build
git init git add . git commit -m 'initial commit'
rake install
ruby-1.9.2-p136 :001 > require 'rubygems' => true ruby-1.9.2-p136 :002 > require 'hello_world' => true ruby-1.9.2-p136 :003 > hello = HelloWorld::HelloWorld.new => #<HelloWorld::HelloWorld:0x000000027d7c40> ruby-1.9.2-p136 :004 > hello.hello_world Hello World! => nil ruby-1.9.2-p136 :005 >