6

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.

phs
  • 10,687
  • 4
  • 58
  • 84
jshen
  • 11,507
  • 7
  • 37
  • 59
  • See also http://stackoverflow.com/questions/3642085/make-bundler-use-different-gems-for-different-platforms – knut Nov 23 '12 at 22:55
  • That's not what I'm talking about. 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 – jshen Nov 24 '12 at 02:51
  • The simplest way would be to write the gem in ruby, that way you run it on `jRuby`, `MRI` or any implementation. It'll run fine. You haven't specified what are the external dependencies for your gem. – nikhil Nov 24 '12 at 04:32
  • 3
    Please answer my question or not, I asked it for a reason. Writing in pure ruby is not what I am trying to do. – jshen Nov 24 '12 at 06:12

1 Answers1

6

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).

Sébastien Le Callonnec
  • 26,254
  • 8
  • 67
  • 80