"Amigos y nadie más. El resto, la selva"
-- Jorge Guillén

Compiling and deploying binary Ruby gems

If, like me, you need to deploy a set of Ruby gems to a number of servers that do not have a way to compile on its own, you will need to figure out a way to include native extensions into the gem itself. The question is, how does one properly do this? The process is a very simple if you already know how Gem works. For the rest of us, here is a simple guide to follow step by step.

Say that you attempt to create a binary gem using gem-compiler and it produces the right file:

gem fetch ffi --platform ruby
gem install gem-compiler
gem compile ffi*.gem
ls ffi-1.9.3-x86_64-linux.gem

Ideally, you should be able to transfer ffi-1.9.3-x86_64-linux.gem to a remote location and install it right? Wrong! gem-compiler does not handle well this and many other gems so you would have to hack the gems slightly to make them work.

gem unpack ffi-1.9.3-x86_64-linux.gem
cd ffi-1.9.3-x86_64-linux

Then make changes to the ffi.gemspec file so that you comment out the extconf.rb entry that's added to the s.extensions, and you also need to change the s.files so that .so$ is not ignored. The working gemspec file should look like the following:

s.files = %w(ffi.gemspec LICENSE COPYING README.md Rakefile) + Dir.glob("{ext,gen,lib,spec,libtest}/**/*").reject { |f| f =~ /(lib\/[12]\.[089]|\.bundle$)/ }
#s.extensions << 'ext/ffi_c/extconf.rb'

Now you can repack your gem and you're good to go!

gem build ffi.gemspec
mv -f ffi-1.9.3.gem ../ffi-1.9.3-x86_64-linux.gem

Note that if you have both ffi-1.9.3.gem and ffi-1.9.3-x86_64-linux.gem in a single directory, Rubygem will choose the proper native gem for your system when you try to install a gem that depends on it. This is a very nice feature!

Hope that helps somebody out there...


New Comment

* optional