Building Ruby

Lots of good resources into how we can build Ruby in Koichi’s Ruby Hack challenge.

A previous version of this page advocated for splitting the Ruby working tree into several different directories, one for source code, one for build artifacts and one for installed binaries in the same way as the Ruby hack challenge does. Personally, I found this too complex to work with on a day to day basis, preferring instead to just push everything into the same directory.

Here are some pros and cons I’ve found of using a unified build directory:



Realistically there isn’t much in it. I don’t like configuring editors all that much so the pros of a single tree outweigh the cons for me. And I’ve found that the main con can be easily worked around with the liberal use of make distclean or git clean -fdx

Getting the build structure ready

Simples: for me this looks like this…

cd src
git clone git@github.com/ruby/ruby.git
cd ruby

Then build:

Alternative configure

Aaron recommended doing:

  set -lx debugflags '-g'
  set -lx optflags '-O0'
  set -lx RUBY_DEVEL 'yes'
  ./configure --prefix=~../install --disable-install-doc --with-openssl-dir=(brew --prefix)/opt/openssl

This is Fish shell syntax, which is what I use. YMMV for other shells but you may be able to get away with swapping the set -lx commands for export

This will make sure that every time Ruby builds either itself or any of the included gems it always builds with:

It’s also possible to compile without this and then edit the generated Makefile, but this doesn’t apply these to any gems that are built - just the main miniruby and ruby binaries. Which can be a pain.

The above configure commands are for building on a Mac where we have to workaround the Apple/Homebrew OpenSSL situation. These days I do almost all of my dev on a Linux box where you can omit the --with-openssl-dir flag.

Configuring multiple targets

Generally I alias the snippet above to a function in fish shell so I can have different combinations of compile flags and install targets for different branches.

Here is the function I use to configure a default development version, when I want as near to standard settings as possible (but making debugging easier).

function configure-main
  set -lx debugflags '-ggdb3'
  set -lx optflags '-O0'
  set -lx RUBY_DEVEL 'yes'
  ./configure --prefix=$HOME/.rbenv/versions/main --disable-install-doc

I am working on a feature that requires some extra build flags, so I use a seperate command to build for that case

function configure-rvargc
  set -lx debugflags '-ggdb3'
  set -lx optflags '-O0'
  set -lx RUBY_DEVEL 'yes'
  set -lx cflags '-DUSE_RVARGC=1'
  ./configure --prefix=$HOME/.rbenv/versions/rvargc --disable-install-doc

Note the different prefix locations, so I can make install and switch between two rubies that are built identically other than the presence of my feature flag.

Building and installing

First find out how many working cpu’s you’ve got

On Linux that’s done with nproc:

~/src/ruby master ≡ mattvh@deunan
❯ nproc --all

on a Mac you can use sysctl

~/src/ruby master ≡ mattvh@kyouko
❯ sysctl -n hw.activecpu

A good general rule of thumb is to tell make to use cpu_count + 1 threads using the -j flag. You can run make -j without a specific number but I don’t because it can make things slower. Here’s a quote from Managing Projects with GNU Make 3rd Edition (O’Reilly)

The --jobs option can be used without a number. If so, make will spawn as many jobs as there are targets to be updated. This is usually a bad idea, because a large number of jobs will usually swamp a processor and can run much slower than even a single job.

Then build Ruby and install it to your prefix:

This will build and install ruby, irb, gem, bundler et al and dump them in your configured install directory. This takes time (rougly 5 minutes on my work MacBook Pro, longer on my personal Surface Pro).

Alternative make targets that are useful

Once Ruby/miniruby is built you should be good to start development. See