Bounding Unicorns

How To Install Ruby Extensions Like openssl With rbenv

Today we are going to look at installing Ruby extensions when they do not get built as part of Ruby itself.

rbenv, for example, can install ruby which lacks openssl module which is then useless for any practical work as rubygems use HTTPS, as the following example demonstrates:

% rbenv install 1.9.3-p448
Downloading yaml-0.1.4.tar.gz...
-> http://dqw8nmjcqpjn7.cloudfront.net/36c852831d02cf90508c29852361d01b
Installing yaml-0.1.4...
Installed yaml-0.1.4 to /home/me/.rbenv/versions/1.9.3-p448

Downloading ruby-1.9.3-p448.tar.gz...
-> http://dqw8nmjcqpjn7.cloudfront.net/a893cff26bcf351b8975ebf2a63b1023
Installing ruby-1.9.3-p448...
Installed ruby-1.9.3-p448 to /home/me/.rbenv/versions/1.9.3-p448

Ruby installed! Let's try using it:

% bundle install

Could not load OpenSSL.
You must recompile Ruby with OpenSSL support or change the sources in your Gemfile from 'https' to
'http'. Instructions for compiling with OpenSSL using RVM are available at rvm.io/packages/openssl.

Uh-oh. My ruby is useless for my project.

One way of fixing this situation is to follow the instructions at rvm.io/packages/openssl which more or less say "give rvm a root shell and let it take care of everything, you don't need to know the details". This though is a non-starter for those of us with systems background. Instead we are going to install the needed extensions manually.

Installing ruby extensions

The first thing we need to know is that ruby installation is performed in two parts. The first part builds and installs the ruby interpreter itself, the second part builds and installs extensions that are included with the interpreter. Without some extensions (like date, io or stringio) most programs won't run. Other extensions (like openssl) are required by some programs but ignored by others. Still other extensions (gdbm, win32ole) are so obscure that you may have never heard of them, and they may well not exist on your machine. Ruby-the-interpreter can be installed without many of the extensions it ships with, which explains how rbenv reports installation success yet produces a ruby installation that is useless for a particular application.

The second thing we need to know is that ruby extensions can be installed at any time into an existing ruby installation. There is no requirement that the interpreter and extensions are built together. This means to install the missing openssl extension we just need to build that one extension, not rebuild the entire ruby installation.

Now, for how to actually do it. First, we need to obtain the ruby source code. rbenv deleted it after "successful" installation, but told us where the source came from: http://dqw8nmjcqpjn7.cloudfront.net/a893cff26bcf351b8975ebf2a63b1023 above. Therefore:

% wget http://dqw8nmjcqpjn7.cloudfront.net/a893cff26bcf351b8975ebf2a63b1023
...
2014-01-27 22:14:01 (6.34 MB/s) - a893cff26bcf351b8975ebf2a63b1023 saved [12559260/12559260]

This is really a tarball, which we can unpack:

% tar xf a893cff26bcf351b8975ebf2a63b1023

If your tar is smart, it should detect gzip and bzip2 compressed tarballs. If it does not, try xfz or xfj in place of xf. Now we have a ruby-1.9.3-p448 directory with ruby source in it. Navigate to the extension we are trying to install:

% cd ruby-1.9.3-p448/ext/openssl

If you have not done so already, activate the ruby you want to install the extension to which should be the same version:

% rbenv shell 1.9.3-p448

Now configure the extension:

% ruby extconf.rb

You will get a bunch of output. If all goes well, you'll see something like this at the end:

=== Checking done. ===
creating extconf.h
creating Makefile
Done.

Chances are, all does not go well, as otherwise openssl would have been installed automatically. If so you might see an error like this:

=== Checking for required stuff... ===
checking for openssl/ssl.h... no
=== Checking for required stuff failed. ===
Makefile wasn't created. Fix the errors above.

This system is missing openssl headers.

Resolve the issue if any, patch the source if necessary, then rerun ruby extconf.rb. Repeat until it succeeds.

Once the extension is configured you can build it:

% make

And once it's built, you can install it:

% make install

Go back to your application and see if it now works!

This recipe works for readline and any other extension shipped with ruby.