Installing ruby extensions like openssl with rbenv
The ruby community has adopted quite a "for dummies" mindset: everything is made to work out of the box with zero configuration needed, on all environments. This, of course, is not without cost, and given that both features and development effort can be assumed to be infinite, the cost is performance. Ruby application bootstrap times are outrageous, memory consumption is off the charts as well, rubygems and bundler are one slower than the other, etc.
Today though I want to consider a case when the software plain does not work, specifically when rbenv installs ruby with extensions missing.
Suppose you did something like this:
% 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.
Now I could, like a good dummy, 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") but I like knowing what is going on on my system and I don't give root shells to any program that asks for them. So, we are going to toss this recommendation into /dev/null.
I don't know if there is a way to pry the actual reason why openssl was not installed out of rbenv, but it does not matter for the following discussion anyway.
Installing ruby extensions
The first thing you 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
stringio) most programs won't run. Other extensions (like
are required by some programs but ignored by others.
Still other extensions (
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 you 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
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
xfj in place of
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:
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.