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:
Pros:
./configure
in the wrong
directory by accident!Cons
make clean
doesn’t
clean up everything you expect it to and subsequent compilations don’t give
the results you expect.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
Simples: for me this looks like this…
cd src
git clone git@github.com/ruby/ruby.git
cd ruby
Then build:
autoconf
autoconf
it’s too low level and may not always do what you expect. Instead either use ./autogen.sh
or call autoreconf --install
(which is basically what autogen.sh
does anyway). autoreconf
wraps autogen
and some of the other auto*
tools./configure --enable-shared --with-openssl-dir="$(brew --prefix openssl)" --with-readline-dir="$(brew --prefix readline)" --disable-libedit
configure
Aaron recommended doing:
begin
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
end
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:
-g
include debug symbols - needed for C function labelling and decent
debugger context (otherwise lldb
just shows you assembly). You can-O0
minimal optimisations - this is needed to make sure that cmake
doesn’t
optimise out any local variables or useful information that you can use while
debugging.RUBY_DEVEL
enabled - I don’t actually know what this does yet.--enable-shared
has been removed. Omitting this flag will mean that Ruby
gets built as one big statically linked binary instead of a small binary and a
larger shared library. This makes debugging easier because it will make
following code through the stacktrace easier. Make sure to enable shared
libraries when doing benchmarking, as this is how Ruby is compiled for release
builds.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.
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
end
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
end
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.
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
12
on a Mac you can use sysctl
~/src/ruby master ≡ mattvh@kyouko
❯ sysctl -n hw.activecpu
12
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:
make -j13 && make install
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).
make
targets that are usefulmake miniruby
- Useful when you just need core ruby, doesn’t build gems or stdlib, you can’t use require
but it’s really fast so useful for a quick feedback loop. I use this all the time when working on GC stuff.make
- the default target, builds ruby
binarymake prepare-gems
- installs the default set of gems that are built with ruby into the current build path (running the full test suite requires this)make clean
- cleans up all the build artifacts. Use this when making changes to configure flags that affect the core binaries. This doesn’t clean up the ./configure
script itself, or any artifacts created by configure
, so you can just make
again to build from scratchmake distclean
- cleans up all artifacts created by configure and by make. if you get into an odd situation with build flags or weird make
behaviour, generally this is a good way out. You’ll need to ./configure
again because this will clean up the generated Makefile
Once Ruby/miniruby is built you should be good to start development. See