Sunday, August 19, 2012

Ruby on OS X

A few weeks ago I decided to try (once again) to learn Ruby and Rails.  In the past, I never got far in this endeavor, on account of getting confused and/or bored. This time, I haven't gotten far because I haven't been able to get the damn language working the way I need it to.

Allow me to elaborate. I am using several pieces of dated but still useful reference material.  The good thing is that they were all published around the same time, meaning they all use the same (or similar) version of Ruby, Rails, and Rubygems.  The bad thing is that all of these tools have received major upgrades in the years since.  Version mismatches usually lead to incompatibilities, and sure enough, the commands listed in my Rails book are flat out broken when used with Ruby 1.9 and Rails 3.  I needed the old stuff, and I needed it configured properly.

My quest to to achieve this perfect development environment was a monumental pain in the ass, redeemed only by the fact that it taught me a ton about Ruby, the Ruby community, and the innards of OS X.

My first major hurdle came while trying to compile Ruby 1.8.5 from scratch.  It turns out that as of Mountain Lion (or maybe Lion, I forget), OS X has no pure version of GCC.  It's all LLVM, with either a Clang or GCC front end.  Suffice to say that only the most bleeding edge version of Ruby compiles nicely with these tools.  Everything else simply craps out at one point or another.   At this point, I decided to look for some alternatives.  RVM is a popular tool for managing different versions of Ruby, so I tried to give it a shot.  It failed spectacularly.  Unbeknownst to me,  RVM installs different versions of Ruby by compiling them.  Obviously it failed just as hard, no matter which version I tried to install.

I went back to the Internet to look for solutions.  The most common suggestion was to install Apple's pre-Lion version of GCC4.2 using Homebrew.  I don't like Homebrew (or Macports or Fink), and I wasn't excited about using it just for the compiler I needed.  So instead I found a wonderful guide to building GCC from scratch on OS X.  The instructions worked perfectly, and they left me with a working, up to date compiler that was installed to a sane location.

I didn't know it yet, but at this point, I had all the tools I needed to get Ruby up and running.  Yet I still struggled when building from source.  One issue is that the Ruby source has some glaring issues (at least in version 1.8.5).  For example, I found one file that had #define where it needed #defined,and another which tried to pass a const char* to free() (which, at least on my system, requires a void* argument.  Then there was the file which defined two constants which already existed in OS X's version of stdlib.h.

The second issue was getting make to use my hand build version of GCC.  It was easy enough to put it on my PATH, but that wasn't enough.  Some parts of Ruby's build process use the generic
cc command, and on OS X this is a symbolic link to /usr/bin/clang.  I only discovered this last part today.  A week ago, I had what looked to be a working version of Ruby which failed to install gems.  When I googled the error messages, all of the hits were concerning the aforementioned issues with compiling Ruby using LLVM and Clang.  I kept thinking "but I'm not using them.  I have GCC4.7.  I can see that it's being used by make. "  Only it wasn't being used by make. At least, not during the entire build process.  When I finally changed the cc symlink, the build finally succeeded.

Just kidding.  It failed again.  For whatever reason, several of the modules in the ext/ folder of the source tree had empty Makefiles.  I'm still unsure as to how this happened, but the issue went away after running make distclean, and then another make.  This time, I had a fully working copy of Ruby 1.8.5.  After that, everything fell into place.  Rubygems. Rails 1.2.  It's all working fine, as do the book examples.

At first, my takeaway from this ordeal was that Apple was doing a fine job fucking up OS X as a legitimate environment for programmers. By the end of it, my tune changed.  I still don't like the loss of GCC (and now I won't have to worry about it either), but if FreeBSD can get their system running with Clang and LLVM, I hesitate to say that either tool is a problem.  Rather, the issues seemed to stem  from the way Ruby is/was written.  I'm not saying it isn't worthy of being used in production environments, but I don't expect (and don't encounter) such sloppiness in any other major programming language.

I also learned a lot about the Ruby community.  Specifically, I decided that the concept of "convention over configuration" espoused by Rails developers extends into the community at large.  Did you know that RVM will change the way the cd command behaves on your system?  Even if the change isn't problematic for 99.9999999% of users, it is still something I'd like to be told about at installation.  I also grew frustrated at how so many of the proposed solutions to my problems boiled down to using a tool that would automagically download and configure the tools I needed in order to build and install the tools I wanted to use.  In other words, they wanted you to paste a few commands into a shell and hit enter, regardless of whether or not you knew what it would do to your system. To be fair, this isn't exclusive to the Ruby community, but I grow tired of it in general.

Don't worry though; I still have a Ruby specific gripe.  Before I installed Rubygems, I had a sneaking suspicion that the latest version would not be compatible with old versions of the language.  This is in fact true, but good luck finding this information out on the Rubygems homepage.  It may very well exist, but the only place I found the answer was an old question on Stack Exchange. Without that hunch, who knows how much more time I would have spent struggling.

Ruby'ers - I get it.  I really do.  Configuration sucks.  It's boring, error prone, and compared to writing actual code, is no fun at all.  But dammit, sometimes it's necessary.  If you ignore configuration, you will have no idea where to look when something goes wrong.  And despite the fact that these languages and frameworks ask us to trust them, they can and will make mistakes.  The only way to deal with them is to know what the heck is going on in the first place.

No comments: