I’m writing a gem that can be used from both MRI Ruby and JRuby to talk
to
a Java library in a jar file. In JRuby talking to the Java library
requires no special configuration, but from MRI Ruby I need to use the
rjb
(Ruby Java Bridge) gem.
So, I want to install the rjb gem if in MRI Ruby, bot want not to
install
it if in JRuby, since it requires some native building.
I have this in my gemspec, which works fine:
unless /java/ === RUBY_PLATFORM spec.add_dependency ‘rjb’ end
However, when I run bundle in MRI, the resulting Gemfile.lock contains
the
rjb dependency without any condition not to use it in JRuby:
… rake (10.0.4) rjb (1.4.6) rspec (2.13.0) … So if I build
the
gem, distribute it, and then gem install it in JRuby, the gem install
tries
to build rjb, and fails because of the native extensions.
What’s the best way to deal with this? Should I duplicate the gem
dependencies, putting them in the Gemfile instead of the ‘gemspec’
that’s
there now? Or build two different gems (I hope not)?
Thanks,Keith
As you mentioned, you have the following in your gemspec:
unless /java/ === RUBY_PLATFORM spec.add_dependency ‘rjb’ end
The gemspec file is the description of how to package your gem, so the
RUBY_PLATFORM here will be the one of the ruby you use to build/package
your gem, not the one you
Maybe I’m overthinking this…my code doesn’t contain any extensions at
all, I just have a dependency on a gem on one platform but not the
other.
I don’t think I need rake-compile.
If I have to create one gem per platform, then I guess all I need to do
is
something like this:
I’ve seen other gems use an underscore instead (_java) (yours included,
Rhett), but when I try specifying that in the gemspec I get an error
from
the gem build:
Malformed version number string 0.1.0_java
Rhett, how did you do it? Did you rename the file manually? I could
certainly do that, but I assume if the gem build forbids it there is a
good
reason…or no?
I am not familiar with rake-compiler and I’m not sure it will help you
in this case, but I can give you an overview of what you need to
achieve.
When you have a gem that has different dependencies on different rubies,
you need to package the gem separately for each platform. Adding a
condition on the dependency in the gemspec is not sufficient; you
actually need to build and publish variants of the gem with different
values in the gemspec’s “platform” attribute. (This is because the
gemspec that is packaged in the gem isn’t the literal code you author in
the gemspec file – it’s a serialized representation derived from
interpreting the gemspec in the interpreter on which the gem is being
packaged. Any conditions will be evaluated in the published gemspec.)
One of my gems, ladle1, has both JRuby and generic ruby versions. You
can take a look at its source[2] to see how this happens, but basically
I use RVM to package it separately on both MRI and JRuby and then
publish both.
I believe if you do handle the packaging like this, bundler will do the
right thing. I haven’t tried it though, so I don’t know for sure.
Thanks, I realized that shortly after writing you, and was composing
another message when you responded.
I’m now doing the same thing with another gem, and gem build appends
“.java” to the gem file name instead of “-java”. Any idea why? The
“.java” is interpreted by Gem in a Box as meaning it’s a prerelease
version
(a gem convention I think), but that was not my intention.
Also, given that the Gemfile.lock file will differ in the respective
platform, how do you deal with committing it to the version control
repo?
I’ve seen other gems use an underscore instead (_java) (yours included, Rhett),
but when I try specifying that in the gemspec I get an error from the gem build:
Malformed version number string 0.1.0_java
Rhett, how did you do it? Did you rename the file manually? I could certainly
do that, but I assume if the gem build forbids it there is a good reason…or no?
You don’t want to change the version number. You want to change spec.platform if it is anything other than “ruby”, rubygems will add
an appropriate platform suffix to the gem package name. The platform
attribute is also what rubygems (and hopefully bundler) will use to pick
the right gem to install on MRI vs. JRuby.
I’m still perplexed as to how to handle committing the Gemfile.lock, given that
it will differ for the two platforms.
I don’t have experience doing this with a real application, but I just
tried a trivial test app and it seemed to work. I created a Gemfile with
a gem that had both generic and java versions. Then I did bundle install on MRI, which created a Gemfile.lock. And then I did bundle install on JRuby, which updated Gemfile.lock so that it included the
java-platform dependencies also. Then I switched back to MRI and did bundle install a third time; the java platform information in
Gemfile.lock was preserved.
I think the recommendation when developing a gem is to not check your
Gemfile.lock into source control so it would just be generated for each
platform you create the gem for.
“When developing a gem, use the
gemspechttp://gembundler.com/rubygems.html method
in your Gemfile to avoid duplication. In general, a gem’s Gemfile should
contain the Rubygems source and a single gemspec line. Do not check
your
Gemfile.lock into version control, since it enforces precision that
does
not exist in the gem command, which is used to install gems in practice.
Even if the precision could be enforced, you wouldn’t want it, since it
would prevent people from using your library with versions of its
dependencies that are different from the ones you used to develop the
gem.”
This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.