Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I make a gem that targets both MRI and JRuby?

Tags:

ruby

gem

jruby

I want to make a gem, and when someone else tries to use it with MRI it will use C code, and when they use it from JRuby it will use Java code.

The nokogiri and puma gems do this, and I've glanced at their code, but didn't see how they were making it happen.

like image 559
jshen Avatar asked Nov 23 '12 21:11

jshen


1 Answers

This is done by cross-compiling the gem for the different platforms you are targeting, using rvm (or other similar tools to switch between rubies) and rake-compiler.

The gemspec file must specify the files needed for each platform; this is done by checking the platform the gem is being compiled with:

Gem::Specification.new do |gem|
# . . .

  if RUBY_PLATFORM =~ /java/
    # package jars
    gem.files += ['lib/*.jar']
    # . . . 
  else
    # package C stuff
    gem.files += Dir['ext/**/*.c']
    # . . . 
    gem.extensions = Dir['ext/**/extconf.rb']
  end
end

In the Rakefile, after installing rake-compiler, the pattern is usually the following:

spec = Gem::Specification.load('hello_world.gemspec')

if RUBY_PLATFORM =~ /java/
  require 'rake/javaextensiontask'
  Rake::JavaExtensionTask.new('hello_world', spec)
else
  require 'rake/extensiontask'
  Rake::ExtensionTask.new('hello_world', spec)
end

But you may need to do specific tasks for the different platforms.

With MRI, you then compile with rake native gem; with JRuby, rake java gem – this is where a tool like rvm gets handy. You finally end up with different gem files for your gem, one per platform, that you can then release as your gem.

See rake-compiler documentation for more details, or check out other projects that do the same, such as redcloth or pg_array_parser (I find they are better examples than nokogiri for this).

like image 51
Sébastien Le Callonnec Avatar answered Nov 15 '22 09:11

Sébastien Le Callonnec