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!!