Skip to content

Instantly share code, notes, and snippets.

Last active May 10, 2023 20:55
Show Gist options
  • Save foca/82fc8ba2d762dbbf30be to your computer and use it in GitHub Desktop.
Save foca/82fc8ba2d762dbbf30be to your computer and use it in GitHub Desktop.
A Makefile for developing a ruby gem

A Makefile for building ruby gems

This Makefile aids in developing a ruby gem. It's pretty opinionated to my workflow, but it might be useful to you, so here it goes.

  1. Assumes you use of gs (or gst) for managing gemsets.
  2. Assumes you use dep for installing dependencies. Because fuck Bundler.
  3. Assumes the directory in which the source code is located is named after the gem. So basename $(dirname $(pwd)) would return foo if you would gem install foo.
  4. Assumes the existance of a lib/*/version.rb that has a line that defines a VERSION constant.

If All those assumptions / prerequisites are met, then you can:

  • make test: run your tests. By default this Makefile assumes the use of [cutest][cutest], but you can change that to whatever strikes your fancy.
  • make pkg/foo-1.0.0.gem: Build the gem. This will properly track any changes to the gemspec and to the lib/foo/version.rb file.
  • make all (or just make): Run tests and then build all the gems.
  • make release: Build gems and then push them to
  • make clean: Delete any built gems.

The tests will dep install before running, but only if the .gems file changed. In order to avoid polluting the global namespace and installing gems in your root GEM_HOME, all the tasks in this Makefile will fail if you're not inside a gemset (i.e. GS_NAME isn't set).

The Makefile will also run tasks with ./lib and ./test added to your ruby include path (via expanding the RUBYLIB env var). This should allow you to work without needing to touch the $LOAD_PATH inside your project, which is a horrible practice.


In case your directory doesn't exactly match the gem name, you can just redefine the PACKAGES variable to list the names of the package(s) to be built.

You can also change the VERSION_FILE variable in order to change the pattern to find the version.rb file. This defaults to lib/*/version.rb.

ifndef GS_NAME
$(error GS_NAME not set. Have you `gs in` yet?)
PACKAGES := $(shell basename `pwd`)
VERSION_FILE := lib/*/version.rb
DEPS := ${GEM_HOME}/installed
VERSION := $(shell grep VERSION $(VERSION_FILE) | sed -e 's/VERSION =//' -e 's/[ "]//g')
GEMS := $(addprefix pkg/, $(addsuffix -$(VERSION).gem, $(PACKAGES)))
export RUBYLIB=$RUBYLIB:lib:test
all: test $(GEMS)
test: $(DEPS)
cutest -r ./test/helper.rb ./test/**/*_test.rb
rm pkg/*.gem
release: $(GEMS)
for gem in $^; do gem push $$gem; done
pkg/%-$(VERSION).gem: %.gemspec $(VERSION_FILE)
gem build $<
mv $(@F) pkg/
$(DEPS): $(GEM_HOME) .gems
which dep &>/dev/null || gem install dep
dep install
touch $(GEM_HOME)/installed
.PHONY: all test release clean
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment