How to rebuild native Ruby gems after a lib/system upgrade?

Première publication : 2014-03-14
Tags : rubyrubygems

When you install Ruby gems that have native extensions (usually developed in C and compiled) sometimes that extension binds itself to a system library, for example Nokogiri with LibXML.

After a library upgrade (security minor update, or a full system upgrade), your gem has to be rebuilt to use the new library’s interface.

Most of the times, the gem will continue to work just fine, put will print a message like this :

WARNING: Nokogiri was built against LibXML version 2.7.8, but has dynamically loaded 2.8.0

So how can we rebuild the gem?

You could upgrade the gem’s version in your application’s dependency specifications (usually Bundler’s Gemfile), and redeploy your app. It’s ok, but what if you don’t want to change the version?

The best way would be to rebuild the existing gem and restart your app. There is a gem pristine --all command available. I’ve tried it but it didn’t solve my issue. Maybe the rebuild process doesn’t change the libraries paths, or something else. If you know about this, let me know too ;)

You could uninstall the gem and redeploy your app. If you’re using a deployment mechanism that checks for dependencies, it will reinstall the gem and build it against the latest library. But in the meantime, the gem is missing and your app might break.

You could finally briefly uninstall the gem and reinstall it. On a modern server, it’s a matter of tens of seconds.

The usual steps :

$ gem list | grep nokogiri
nokogiri (1.5.11)

$ gem uninstall --executables --ignore-dependencies nokogiri
Removing nokogiri
Successfully uninstalled nokogiri-1.5.11

$ gem install nokogiri -v 1.5.11
Fetching: nokogiri-1.5.11.gem (100%)
Building native extensions. This could take a while...
Successfully installed nokogiri-1.5.11
1 gem installed

The --executables --ignore-dependencies flags on uninstall disable confirmation for removing the gem executable(s) and dependency warnings.

[Edit] The --ignore-dependencies flag is better than --force. It is also compatible with Rubygems 1.8.

If you’re using something Bundler and Capistrano, there is an additional issue ; the gems are not installed in your traditional Ruby paths, they are in your application’s shared directory.

Here is the gem environment of one of my users :

$ gem env
RubyGems Environment:
- RUBYGEMS VERSION: 2.2.2
- RUBY VERSION: 2.1.1 (2014-02-24 patchlevel 76) [x86_64-linux]
- INSTALLATION DIRECTORY: /home/jlecour/.rbenv/versions/2.1.1/lib/ruby/gems/2.1.0
- RUBY EXECUTABLE: /home/jlecour/.rbenv/versions/2.1.1/bin/ruby
- EXECUTABLE DIRECTORY: /home/jlecour/.rbenv/versions/2.1.1/bin
- SPEC CACHE DIRECTORY: /home/jlecour/.gem/specs
- RUBYGEMS PLATFORMS:
- ruby
- x86_64-linux
- GEM PATHS:
- /home/jlecour/.rbenv/versions/2.1.1/lib/ruby/gems/2.1.0
- /home/jlecour/.gem/ruby/2.1.0
- GEM CONFIGURATION:
- :update_sources => true
- :verbose => true
- :backtrace => false
- :bulk_threshold => 1000
- "gem" => "--no-rdoc --no-ri"
- :sources => ["http://rubygems.org"]
- REMOTE SOURCES:
- http://rubygems.org
- http://gems.wishbed.net
- SHELL PATH:
- /home/jlecour/.rbenv/versions/2.1.1/bin
- /home/jlecour/.rbenv/libexec
- /home/jlecour/.rbenv/plugins/rbenv-vars/bin
- /home/jlecour/.rbenv/plugins/ruby-build/bin
- /home/jlecour/.rbenv/shims
- /home/jlecour/.rbenv/bin
- /home/jlecour/bin
- /usr/local/bin
- /usr/bin
- /bin
- /usr/local/games
- /usr/games

But my application’s gems are in /home/jlecour/apps/example/shared/bundle/ruby/2.1.0/

To tell the gem command to use this location, you have to set the GEM_HOME environment variable for each call (or exporting it at the beginning, but remember to set it back after)

The sequence becomes this :

$ export GEM_HOME=/home/jlecour/apps/example/shared/bundle/ruby/2.1.0/

$ gem list | grep nokogiri
nokogiri (1.5.11)

$ gem uninstall --executables --ignore-dependencies nokogiri
Removing nokogiri
Successfully uninstalled nokogiri-1.5.11

$ gem install nokogiri -v 1.5.11
Fetching: nokogiri-1.5.11.gem (100%)
Building native extensions. This could take a while…
Successfully installed nokogiri-1.5.11
1 gem installed

$ unset GEM_HOME

Then you just have to restart your application and voilà.