CodeGnome Consulting, LTD

Programming - DevOps - Project Management - Information Security

Bootstrapping RVM on Mountain Lion

| Comments

The Dangers of a “Walled Garden”

No one seriously argues against the viewpoint that Apple’s OS X provides developers with a walled garden. While OS X runs Darwin under the hood, it’s still very much a closed ecosystem. Even if you install some third-party tools such as MacPorts or Homebrew, you’re still fundamentally stuck with some of the options that Apple makes on your behalf, very often in a “take it or leave it” way.

In the case of RVM, Ruby, and RubyGems, the walled garden has been systematically weeding out standard compilers in favor of LLVM-based compilers as provided by the 4.x series of Xcode. In practice, that means many rubies and gems will not compile without jumping through hoops—and sometimes not even then. Even versions of Ruby that will compile with LLVM may require gems that won’t, so this is a non-starter for serious Ruby or Ruby on Rails development.

Luckily, even if you use a Mac on a regular basis, there’s hope. With a little help from MacPorts, VirtualBox, and some exported compiler settings, you can get a minimal Ruby environment running well enough to bootstrap a Linux virtual machine that “just works” for Ruby development.

Install Xcode

You can buy the latest version of Xcode for free in the Mac App Store. Make sure you install the optional command-line tools from within the Xcode application, too. It’s an essential step, but it’s unfortunately a manual one.

Install a Non-LLVM Compiler

There may be other compilers that will work, but I personally had success with the gcc-apple-4.2 compiler. Using MacPorts, you can install this with sudo port install apple-gcc42. If you’re using Fink, Homebrew, or rolling your own—well, then you’re on your own.

Having the compiler installed isn’t enough, though. Before you attempt to install RVM, you need to update the .rvmrc file in your home directory with the correct compiler settings.

~/.rvmrc
1
2
3
4
5
CC='gcc-apple-4.2'
CFLAGS='-O2 -arch x86_64'
LDFLAGS='-L/opt/local/lib'
CPPFLAGS='-I/opt/local/include'
export CC CFLAGS LDFLAGS CPPFLAGS

While it may not actually be necessary, I recommend sourcing this file with source ~/.rvmrc before continuing with the RVM installation. This will ensure that your compiler settings are present in the current environment, but I’m not sure they’re needed until RVM sources the file later when it’s compiling rubies and gems. YMMV.

Libyaml, curl, and curl-ca-bundle—oh my!

For some unknown reason, RVM and the version of cURL installed by MacPorts don’t get along. If you try to install RVM with the MacPorts version of cURL, even if you’ve specifically run sudo port install curl-ca-bundle beforehand, you will get errors about compiling libyaml because the SSL verification fails. You’ll get an error message like this one:

curl: (60) SSL certificate problem: self signed certificate in certificate chain
More details here: http://curl.haxx.se/docs/sslcerts.html

curl performs SSL certificate verification by default, using a "bundle"
 of Certificate Authority (CA) public keys (CA certs). If the default
 bundle file isn't adequate, you can specify an alternate file
 using the --cacert option.
If this HTTPS server uses a certificate signed by a CA represented in
 the bundle, the certificate verification probably failed due to a
 problem with the certificate (it might be expired, or the name might
 not match the domain name in the URL).
If you'd like to turn off curl's verification of the certificate, use
 the -k (or --insecure) option.

Even attempting to install libyaml directly via MacPorts doesn’t work. Oh, MacPorts will install it, but RVM won’t use it. For our purposes, this doesn’t really matter, but you should be aware that there is an unresolved problem here—which is one reason we’re leaving the walled garden behind in the first place.

Install RVM

The best way to install RVM is by specifying the full path to the native version of curl. Even so, you may or may not have problems with libyaml during installation. If you encounter errors specific to libyaml, just ignore them; it won’t impact the endgame.

Install Latest Version of RVM
1
/usr/bin/curl -L https://get.rvm.io | bash -s latest

Install a Recent Ruby Version

The system Ruby for Mountain Lion is Ruby 1.8.7, which may not support all the gems you’ll need to build Vagrant, VeeWee, and other essential dependencies for building your virtualized development box. To prevent problems, we’re going to install Ruby 1.9.3 to make sure that the remaining steps go smoothly.

Assuming that you’ve correctly set the necessary flags in your ~/.rvmrc file, you can now install your rubies and gems using the gcc-apple-4.2 compiler. For example:

Install Ruby
1
2
rvm install 1.9.3
rvm use --default 1.9.3

Even if you get more errors about libyaml, you should now have a Ruby environment sufficient to bootstrap the ruby-dev box builder. You can get the ball rolling quickly with the following invocation.

Ruby-Dev Box Builder
1
2
3
git clone https://github.com/CodeGnome/ruby-dev.git
cd ruby-dev
bin/runme.sh

Reboot the New Box and Start Coding

On some occasions, Puppet needs a second, post-reboot run to complete the provisioning process. You will generally need that reboot anyway, as it’s the easiest way to start the virtual machine with X Windows running by default.

Once initial provisioning is completed, you should issue a vagrant reload on your host system. This will restart your virtual machine, log you directly into the X Windows environment using the lightweight awesome window manager, and complete any remaining provisioning steps that failed on the first pass.

You should be ready to go at this point. However, you may still want to run install_rvm.sh or install_rbenv.sh inside the virtual machine before you start coding if you want to use anything other than the virtual machine’s default system Ruby.

Don’t forget to consult the ruby-dev readme and wiki if you run into trouble. If all is still not right with the world, feel free to file issues or submit GitHub pull-requests.

Caveats

RVM seems to source ~/.rvmrc on its startup, so you will want to make sure you unexport those compiler flags whenever need a different compiler or want different compiler options. The following snippet will clear the compiler variables from the current shell and its environment.

Unexport Compiler Flags
1
2
3
flags=(CC CFLAGS LDFLAGS CPPFLAGS)
export -n "${flags[@]}"
unset "${flags[@]}" flags

Comments