Build a ruby gem and conditionally specify dependencies

I am working on a gem that has needs to set dependencies conditionally
when the gem is installed. I’ve done some digging around

and it looks like i’m not alone in this need.

this is a long thread

http://www.ruby-forum.com/topic/957999

The only way I can see to add dependencies to a gem is to use
add_dependency method within a Gem::Specifiction block in a .gemspec
file

Gem::Specification.new do |s|

… standard setup stuff

conditionally set dependencies

s.add_dependency “rb-inotify”, “~> 0.8.8” if RUBY_PLATFORM =~ /linux/
i
s.add_dependency “rb-fsevent”, “~> 0.4.3.1” if RUBY_PLATFORM =~ /
darwin/i
s.add_dependency “rb-fchange”, “~> 0.0.5” if RUBY_PLATFORM =~ /mswin|
mingw/i

end

Based on all of the docs and threads I found on the net, I would have
expected that if you install the gem on

  • Linux, then, rb-inotify would be a dependency and auto-installed
  • Mac - rb-fsevent would be installed
  • Windows - rb-fchange would be installed

However, it seems that is not the case. The “if” statements within
the block are evaluated at the time the gem is built and packaged.
Therefore,
if you build and package the gem on Linux, then, rb-inotify is added
as a dependency, Mac, then, rb-fsevent, Windows - rb-fchange.

Still needing a solution, I dug around in the rubygems code and it
seems the following is a broad stoke of what happens.

  • build all of your code for your gem: foo.gem
  • create a foo.gemspec file
  • build, package, and release the gem to a gem server such as
    rubygems.org
  • let everyone know
  • developers install it locally via: gem install foo
  • the foo.gem file is downloaded, unpacked, and installed. all
    dependencies are installed as well.
  • everything should be set and we can beging using the gem.

It seems that when the gem is built and released the foo.gemspec file
is loaded and the Gem::Specification block is evaluated and converted
to YAML, compressed as
metadata.gz, and included in foo.gem. The ruby code is compressed
into data.tar.gz and included as well. When the gem is installed on
the local developer machine,
the YAML is extracted from metadata.gz and converted back into a
Gem::Specification block, however, it is not converted back to the
original block.

instead, you will see something like the following:

Gem::Specification.new do |s|

if s.respond_to? :specification_version then
s.specification_version = 3

if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0')

then
s.add_runtime_dependency(%q, [“~> 0.8.8”])
else
s.add_dependency(%q, [“~> 0.8.8”])
end
else
s.add_dependency(%q, [“~> 0.8.8”])
end

end

Ok. So, I have a bird’s eye view of the process, however, that does
not change my desire to build a single gem and conditionally specify
dependencies for a range of OS targets.

If anyone has a solution other than building multiple .gemspec files
for each target OS… I’m all ears!!

On Jan 20, 7:56am, “rails.impaired” [email protected] wrote:

add_dependency method within a Gem::Specifiction block in a .gemspec
file

You can’t use conditionals on gemspec because gemspec is serialized
into YAML, which doesn’t contain executable code.

The recommendation is to generate the gemspec and change both platform
and dependencies for each platform:

gemspec = Gem::Specification.new do |s|
s.platform = Gem::Platform::RUBY
end

here build the normal gem

Now for linux:

gemspec.platform = “linux”
gemspec.add_dependency …

build the newer gemspec

Ok. So, I have a bird’s eye view of the process, however, that does
not change my desire to build a single gem and conditionally specify
dependencies for a range of OS targets.

Well, is not possible, dependencies are defined for the gemspec, can’t
be defined for different platforms.

Probably you can introduce a newer version of the gemspec for RubyGems
2.0…

If anyone has a solution other than building multiple .gemspec files
for each target OS… I’m all ears!!

Above example has been used in different projects, most of the time by
#dup the gemspec and building it for each platform.


Luis L.

On Fri, Jan 20, 2012 at 3:10 PM, Luis L. [email protected]
wrote:

On Jan 20, 7:56 am, “rails.impaired” [email protected] wrote:>

If anyone has a solution other than building multiple .gemspec files
for each target OS… I’m all ears!!

Above example has been used in different projects, most of the time by
#dup the gemspec and building it for each platform.

We face a related problem in the Gemfile of a local Rails project (not a
gem).

Currently, the Gemfile contains:

group :test do

on Mac os X

gem ‘rb-fsevent’ if RUBY_PLATFORM.include?(“x86_64-darwin”)
gem ‘ruby_gntp’ if RUBY_PLATFORM.include?(“x86_64-darwin”)

on Linux

gem ‘rb-inotify’ unless RUBY_PLATFORM.include?(“x86_64-darwin”)
gem ‘libnotify’ unless RUBY_PLATFORM.include?(“x86_64-darwin”)
end

This works (although it is ugly) for developing on Mac and Linux
systems.

But, we stopped checking in the Gemfile.lock since it changes every time
a developer with a different platform checks in the code.

So, a solution for multi-platform Gemfiles should also solve the
problem for Gemfile.lock.

Or is there a solution that we overlooked?

Thanks,

Peter