30

I'm developing a Rails app on a Mac, and I'm new to testing, so I just added these gems to my Gemfile:

group :test, :development do
  gem 'rspec-rails'     
  gem 'rb-fsevent'
  gem 'growl'
end

But my production server runs Linux, so even if they aren't grouped in :production, bundler (v1.0.21) still attempts to install them. ...and fails, obviously!
extconf.rb:19:in '<main>': Only Darwin (Mac OS X) systems are supported (RuntimeError)

Setting RAILS_ENV to production before running bundle install doesn't work.

It worked by running bundle install --without development test, but how can these gems be taken into consideration by bundler only based on your OS?


Edit: The bundler wiki provides details on how to use platform as a parameter.
The same solution is given in bundler issue #663, so I tried:

group :test, :development do
  gem 'rspec-rails'     
  platforms :darwin do
    gem 'rb-fsevent'
    gem 'growl'
  end 
end

bundle install does not work, but even if we go back to square one and do
bundle install --without darwin, the result is 'darwin' is not a valid platform.
The available options are: [:ruby, :ruby_18, :ruby_19, :mri, :mri_18, :mri_19, :rbx, :jruby, :mswin, :mingw, :mingw_18, :mingw_19]


Any other (elegant) approaches?

Marius Butuc
  • 17,781
  • 22
  • 77
  • 111

5 Answers5

26

The Bundler wiki has a method that adds all gems to Gemfile.lock regardless of platform. It sets require => false depending on the system so you don't need to be able to actually run the gems:

gem 'rb-inotify', :require => RUBY_PLATFORM.include?('linux') && 'rb-inotify'

And they provide helper methods to make this clean:

def os_is(re)
  RbConfig::CONFIG['host_os'] =~ re
end

gem 'rb-fsevent', "~> 0.9.3", platforms: :ruby, install_if: os_is(/darwin/)
gem 'rb-inotify', "~> 0.8.8", platforms: :ruby, install_if: os_is(/linux/)
gem 'wdm',        "~> 0.1.0", platforms: [:mswin, :mingw. :x64_mingw], install_if: os_is(/mingw|mswin/i)

On my Windows 7 x64 system running Ubuntu 12.04 in a Vagrant VM, this worked fine, but the :platforms setting was required - the Linux bundler choked on the 'win32console' gem:

Console.c:1:21: fatal error: windows.h: No such file or directory
iono
  • 2,575
  • 1
  • 28
  • 36
  • This solved my problem with capistrano 3 which relies on Gemfile.lock – Daniël W. Crompton Jan 12 '14 at 01:08
  • 2
    The bundler wiki doesn't seem to exist anymore, has it (or that page) moved anywhere else? – Jason Axelson Jan 13 '14 at 19:56
  • Hmm, I can't find it in the new website structure. The most relevant page I could find was http://bundler.io/v1.5/man/gemfile.5.html – iono Jan 14 '14 at 10:04
  • I don't think you understand how `require` works. It only controls whether a gem is automatically `require`d or which file to `require`, not whether bundler is installed or not. Gems with native extensions for the wrong `host_os` will still be installed, and may fail. https://bundler.io/gemfile.html `install_if` coming in Bundler 2 will do what you want. https://github.com/bundler/bundler/pull/3611 It might be helpful if there were `install_if: :darwin`, `install_if: :windows`, `install_if: :linux` syntax. –  Nov 02 '16 at 02:00
  • Plus, `require: true` will require code of the name of the gem (default behavior). (Fixed answer.) –  Nov 02 '16 at 02:04
17

Gemfile actually is a regular ruby file, so you can use something like

case RUBY_PLATFORM
when /darwin/
  gem 'foo'
when /win32/
  gem 'bar'
end
zed_0xff
  • 32,417
  • 7
  • 53
  • 72
  • 17
    The problem I have with this approach is that the contents of your Gemfile.lock will change based on which platform you run the bundle command in. Normally one checks in Gemfile.lock, so this could be a problem... – RyanWilcox Dec 07 '11 at 21:57
  • 1
    Unfortunately this is the only solution available if you HAVE to run cross platform. We had to do this for some windows / linux incompatibilities. A simple case or if statement. – Amala Dec 08 '11 at 00:52
  • 1
    This is okay for gems since you do not check Gemfile.lock in to a gem's repo. – Kris May 15 '13 at 21:47
7

You can use :install_if method which accepts arbitrary lambda.

Following example comes directly from Gemfile's man pages:

install_if -> { RUBY_PLATFORM =~ /darwin/ } do
  gem "pasteboard"
end

It is much better than control flow constructs (e.g. if) as it maintains dependencies correctly and keeps Gemfile.lock uniform on all machines.

skalee
  • 12,331
  • 6
  • 55
  • 57
  • This is actually the only correct answer which creates a proper Gemfile.lock. Ruby conditionals confuse bundler in some operations (`bundle package`, etc.). –  Nov 02 '16 at 02:11
7

@zed_0xff: found a similar approach in an older rspec-core commit:

if RUBY_PLATFORM =~ /darwin/
  gem 'foo'
end
Marius Butuc
  • 17,781
  • 22
  • 77
  • 111
  • It's totally unsuitable for typical applications. That said, it's OK when you don't check Gemfile.lock into repository, as it happens in gem development. – skalee Dec 14 '15 at 20:07
1

According to the Bundler docs, you need to use the platforms directive:

#Gemfile
platforms :mswin do
  gem "x"
end

gem "weakling",   :platforms => :jruby
gem "ruby-debug", :platforms => :mri_18
gem "nokogiri",   :platforms => [:mri_18, :jruby]

There are a number of Gemfile platforms:

ruby C Ruby (MRI) or Rubinius, but NOT Windows

ruby_18 ruby AND version 1.8

ruby_19 ruby AND version 1.9

ruby_20 ruby AND version 2.0

mri Same as ruby, but not Rubinius

mri_18 mri AND version 1.8

mri_19 mri AND version 1.9

mri_20 mri AND version 2.0 rbx Same as ruby, but only Rubinius (not MRI

jruby JRuby

mswin Windows

mingw Windows 'mingw32' platform (aka RubyInstaller)

mingw_18 mingw AND version 1.8

mingw_19 mingw AND version 1.9 mingw_20 mingw AND version 2.0

Richard Peck
  • 76,116
  • 9
  • 93
  • 147