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

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<rb-inotify>, ["~> 0.8.8"])     else       s.add_dependency(%q<rb-inotify>, ["~> 0.8.8"])     end   else     s.add_dependency(%q<rb-inotify>, ["~> 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!!

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.

ruby - Rubygems: How do I add platform-specific dependency? - Stack Overflow

# this is a long threadhttp://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

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.

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.

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