How to solve the problem you weren’t planning to solve February 3, 2015

This post is all about installing therubyracer gem v0.10.2 on OSX Mavericks with Ruby 1.8.7 in RVM.

Well no, not really. It’s a metaphor.

This is every problem that ever blocked you from the thing you wanted to be doing. It’s every case where something should just work and doesn’t. It’s every problem that you didn’t want to have to solve but ended up having to.

This very specific problem has also been a fairly common one. Google for just ‘therubyracer’ and some variation of this issue is still 3-4 of the first page results. However, the point is, problems like this come up all the time in engineering.

Astute readers will also note that, at time of writing, every piece of software in this attempted install is woefully out of date. Valid ways to solve this problem will include upgrading all of these to their latest versions. If that is any kind of an option it’s definitely the best one.

So how do you install therubyracer gem v0.10.2 on OSX Mavericks with Ruby 1.8.7 in RVM? For me, it should have been like this:

$ bundle install

Because of course I don’t really care about therubyracer. Who even knows what it does? It’s just one part of a project that someone else wrote that I want to use. More importantly This isn’t the problem that I was planning to solve today, so – how to solve it asap before it derails everything?

First, obviously, paste the exact error message into Google and Stackoverflow and see if anyone has had the exact problem problem that you just had. Choose the best answer and try some of the commands that they suggest. There’s an art to this; choosing the right answers from the internet. The internet is full of people answering questions who:

  1. Have no idea what the answer is, but will just post an untested guess.
  2. Have no idea but tried something which happened to work for them, often with no idea why it worked.
  3. Know what they are talking about but post only commands with no context. Tiny variations from the exact situation of the original posted question can make their responses invalid.
  4. Will suggest that instead of the thing you wanted to do, that you simply do another thing.
  5. Have no apparent grasp of how answers, or indeed language, work. (…Yahoo Answers).

Choose wisely! In this particular case there were tons of people with similar issues and I judged that most of the answers were of type 2 and 3. A lot of answers along the lines of “I just ended up uninstalling X (rvm, ruby, everything…) and reinstalling” is a bit of a red flag, even if that is close to what you end up having to do. I tried a few things and none of them worked and I started to feel dirty about doing things blindly.

OK. This is the point that this post is all about, sorry it took a while getting there:

After a certain level of mental investment in a problem, you will always feel happier if you properly understand the eventual solution. If you can get away with quickly and blindly solving a problem: great. But once you’ve spent some time on it, if it suddenly just works for no reason you can see, that’s almost more irksome than it ever failing.

You could (almost) view an annoying problem like this as a Great Opportunity to learn some new things. A chance to look under the abstractions that we use every day.

Alright, let’s dig in. First our bundler abstraction layer has failed; it should have just worked, but it didn’t.

What does bundler actually do?

  1. It reads the project Gemfile and Gemfile.lock and works out which versions of the listed rubygems will work together with each other and each of their dependencies (and their dependencies).
  2. It takes that list of gem versions and either newly installs or uses the existing version using rubygems.

Either of these can fail. This post takes a looks at the dependency resolution part if that’s what is going wrong. In my case  bundler has failed to install one of the gems; therubyracer. The output of bundle install tells me:

An error occurred while installing therubyracer (0.10.2), and Bundler cannot continue.
Make sure that `gem install therubyracer -v '0.10.2'` succeeds before bundling.

And sure enough

$ gem install therubyracer -v '0.10.2'

does indeed fail building native extensions. Our rubygems abstraction layer has failed.

What does rubygems actually do?

Rubygems is a package manager. It has a repository of gems – ruby code packaged in a certain way – and a command line tool to download and install them. The command:

$ gem install <gemname> -v ‘version’

either:

  1. Finds the version it needs already installed
  2. Downloads a ruby gem package from a gem server, copies it to the correct location*, gem installs any dependencies (and their dependencies) in the same way
  3. Sometimes ruby gems include extensions written in C code that needs to be compiled to work on your machine. That’s what all this stuff is about:
Building native extensions.  This could take a while...
ERROR:  Error installing therubyracer:
ERROR: Failed to build gem native extension.
…
tons of warnings which I mostly choose to ignore
….
clang: warning: argument unused during compilation: '-rdynamic'
linking shared-object v8.bundle
clang: error: no such file or directory: '/Users/ali/.rvm/gems/ruby-1.8.7-p374/gems/libv8-3.3.10.4/lib/libv8/build/v8/libv8.a'
make: *** [v8.bundle] Error 1

The rabbit hole gets deeper. Are we about to ask…

What does a C compiler actually do?

No. First, I want to come back to that asterisk above (did you spot that and look for a footnote?) and ask:

What does RVM actually do?

Too much, some would say. Lets just say it lets you switch between multiple versions of Ruby. An installation of Ruby is basically going to consist of a directory structure of files and some environment variables so that ruby and other tools know where the files are. Rvm chooses to install all rubies under an rvm directory (usually ~/.rvm) and for each ruby will tell rubygems what is the correct place(/* (backreference asterisk) ) to install gems.

This line from the gem install error:

clang: error: no such file or directory: '/Users/ali/.rvm/gems/ruby-1.8.7-p374/gems/libv8-3.3.10.4/lib/libv8/build/v8/libv8.a'

Translates like this:

<my_rvm_gems_directory>/<gem_directory_for_this_ruby>/libv8-3.3.10.4/lib/libv8/build/v8/libv8.a

So compiling failed because it couldn’t find a static library that it expected for libv8 version 3.3.10.4. Wait, what? What’s libv8? When did I get that gem?

Going and checking on https://rubygems.org/gems/therubyracer/versions/0.10.2 tells me that therubyracer depends on libv8 ~> 3.3.10. It looks like therubyracer would have installed that as a dependency, but it found it installed already so didn’t bother. For some reason the installation didn’t have that static library file where therubyracer expected it. This is why so much of my internet searching is turning up suggestions to remove the libv8 gem and then let therubyracer install it as a dependency. Surely if that happens, therubyracer will know where that libv8.a file is and be able to use it?

Hack hack hack, uninstall and reinstall. Oh, unfortunately, libv8 3.3.10 won’t install. At this this gives me something new to take to the internet and more context to judge which answers are nonsense. Turns out libv8’s own native extensions won’t build with newer versions of xcode gcc. Apparently last time it was installed I had an earlier version of xcode installed that worked fine.

At this point I could go nuts and get into:

How does xcode actually work?..

How does brew actually work?..

How do compilers actually work?..

How does ….. buuuuut I’m not going to. I’m going to restrict this already long and weirdly specific post to:

What the heck can I do with the info that libv8 will compile with an earlier version of gcc?

xcode, specifically xcode command-line tools, gives you versions of c and c++ compiler commands. gem installing with native extensions is going to call some of these to compile things. You can install other versions of these compilers in parallel from homebrew like this:

$ brew tap homebrew/dupes
$ brew install apple-gcc42

That’s the gcc from xcode 4.2, which the internet tells me will work. These are installed in a non-invasive manner, i.e. it provides commands for using this compiler, but does not affect the existing gcc, g++ etc. commands.

When rubygems builds natively it may use any of these commands: gcc, g++, cpp, c++ so as the dumbest and most foolproof way to switch what it uses I did this:

$ alias gcc=/usr/local/Cellar/apple-gcc42/4.2.1-5666.3/bin/gcc-4.2
$ alias g++=/usr/local/Cellar/apple-gcc42/4.2.1-5666.3/bin/g++-4.2
$ alias cpp=/usr/local/Cellar/apple-gcc42/4.2.1-5666.3/bin/cpp-4.2
$ alias c++=/usr/local/Cellar/apple-gcc42/4.2.1-5666.3/bin/c++-4.2

which will only alias those commands to the older version for the lifetime of this bash session.

So…how do you solve the problem you weren’t planning to solve?

Ideally, asap without thinking too much. You don’t have to solve every problem from first principles.

However, if a problem is really kicking your ass, maybe allow yourself the licence to learn something from it. Dig in, understand something new, peek under an abstraction. An afternoon banging your head against something isn’t wasted if you can learn from it and especially if you can help other people avoid the same problems.

p.s.

If you really came here because you’re having problems installing therubyracer gem v0.10.2 on OSX Mavericks with Ruby 1.8.7 in RVM, I’ll attempt to answer in the style of the 5 types of internet response

  1. Untested guess:

Maybe just try

$ brew install therubyracer -v=0.10.2 -o=mavericks -r=1.8.7 -f=RVM
  1. Random thing that might have worked:

Uninstall xcode

Install xcode version 4.0

$ bundle install
  1. Just commands without context:

Do this:

$ brew tap homebrew/dupes
$ brew install apple-gcc42
$ alias gcc=/usr/local/Cellar/apple-gcc42/4.2.1-5666.3/bin/gcc-4.2
$ alias g++=/usr/local/Cellar/apple-gcc42/4.2.1-5666.3/bin/g++-4.2
$ alias cpp=/usr/local/Cellar/apple-gcc42/4.2.1-5666.3/bin/cpp-4.2
$ alias c++=/usr/local/Cellar/apple-gcc42/4.2.1-5666.3/bin/c++-4.2
$ brew uninstall v8
$ gem uninstall libv8
$ RUBYOPT=-rrubygems gem install therubyracer -v '0.10.2' -- --with-opt-lib=/usr/local/opt/openssl/lib
  1. Alternate suggestion:

Why not just upgrade to the latest versions of therubyracer, libv8, Ruby and OSX?

  1. Yahoo Answers:

lmao i dunno but can a human get preggant of a dog

ali
Author
Ali King