How to add Mac-specific gems to bundle on Mac but not on Linux?
Asked Answered
I

5

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?

Impetuous answered 7/12, 2011 at 18:20 Comment(4)
But what about platform independency? I hope you still keep in mind this aspect of Rails.Blackjack
@MichałCzapko Platform independency is still there for this particular case because there are equivalents for rb-fseventHeadmistress
:darwin is no longer valid as a platform name (if it ever was).Paperboard
Btw when it comes to file system events, why not using listen which is more generic?Paperboard
P
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
Primitivism answered 7/12, 2011 at 18:24 Comment(3)
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...Intelligencer
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.Cheapskate
This is okay for gems since you do not check Gemfile.lock in to a gem's repo.Izaguirre
E
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
Epifocal answered 10/5, 2013 at 5:11 Comment(5)
This solved my problem with capistrano 3 which relies on Gemfile.lockLucrative
The bundler wiki doesn't seem to exist anymore, has it (or that page) moved anywhere else?Unsophisticated
Hmm, I can't find it in the new website structure. The most relevant page I could find was bundler.io/v1.5/man/gemfile.5.htmlEpifocal
I don't think you understand how require works. It only controls whether a gem is automatically required 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. bundler.io/gemfile.html install_if coming in Bundler 2 will do what you want. github.com/bundler/bundler/pull/3611 It might be helpful if there were install_if: :darwin, install_if: :windows, install_if: :linux syntax.Tibetoburman
Plus, require: true will require code of the name of the gem (default behavior). (Fixed answer.)Tibetoburman
P
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
Primitivism answered 7/12, 2011 at 18:24 Comment(3)
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...Intelligencer
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.Cheapskate
This is okay for gems since you do not check Gemfile.lock in to a gem's repo.Izaguirre
I
7

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

if RUBY_PLATFORM =~ /darwin/
  gem 'foo'
end
Impetuous answered 7/12, 2011 at 18:32 Comment(1)
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.Paperboard
P
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.

Paperboard answered 14/12, 2015 at 20:13 Comment(1)
This is actually the only correct answer which creates a proper Gemfile.lock. Ruby conditionals confuse bundler in some operations (bundle package, etc.).Tibetoburman
O
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

Of answered 16/10, 2016 at 11:31 Comment(2)
That only works if you have different gems. If you want different platform versions of the same gem, Bundler will tank your deployment.Passover
Is there a way to know this platform in your rspecs?Macromolecule

© 2022 - 2024 — McMap. All rights reserved.