Rake::Builder

2010/08/29

Use Rake::Builder as a Make Replacement:

$ rake build

I've put together a rake-based system for building C and C++ projects.

I've called it Rake::Builder, it's available on GitHub here.

autoconf

Build systems are complicated by external dependencies: finding third-party headers and libraries. Often there is a specific configuration for each operating system and distribution.

The standard system is GNU's autoconf system, but it is a nightmare. At the most simple level, you distribute source code with a Makefile.

The problem is that Makefiles are just pure dependency managers - if they don't find what they need, they just fail.

The solution to that problem is the use of configure. Configure runs a series of compatibility tests, sniffs the host system and spits out a Makefile.

So, with configure, the user then does the following:

$ ./configure
$ make
$ sudo make install

But, configure itself is a long file, and it's is a program: about 6000 lines of shell code.

A few lines:

ac_ext=cpp
ac_cpp='$CXXCPP $CPPFLAGS'
ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'

The solution to having to maintain configure? Generate it!

With autotools, there is a seemingly eternal regress of configuration files and scripts generated by other scripts.

This was my project's root directory listing before I gave up on autoconf:

AUTHORS
COPYING
ChangeLog
EXAMPLE
INSTALL
Makefile
Makefile.am
Makefile.in
NEWS
README
aclocal.m4
autom4te.cache
config.h
config.h.in
config.log
config.status
configure
configure.ac
depcomp
include
install-sh
missing
objects
src
stamp-h1
test

Fifteen of those files are involved in the mechanics of autoconf.

autoconf dependencies

Autoconf doesn't really address the issue of building on various systems and adapting semi-automatically. You still have to add all the settings, but you have to learn where in a myriad of files these settings are to be placed.

Rake

Of the autoconf alternatives, rake seems to offer the cleanest solution. Rake itself was apparently created as a build system for C, but out of the box it's too generic: it's simply a dependency handling system.

There are already a number of specializations of rake. In Rails projects, and Gem building, Rakefiles handle maintenance, testing, documentation building and packaging.

Rake::Builder is my attempt to specialise rake to the task of building C and C++ projects.

I've got to version 0.0.11 and it now builds the following types of projects:

  • C++,

  • QT C++,

  • C,

  • Objective-C.

This is an example with two build targets: a static library and a test executable, with the test project depending on the library:

require 'rake/builder'

Rake::Builder.new do |builder|
  builder.target               = 'libactive_record_sqlite.a'
  builder.source_search_paths  = [ 'src' ]
  builder.header_search_paths  = [ 'include/**/*.h' ]
  builder.objects_path         = 'objects'
  builder.include_paths        = [ 'include' ]
  builder.library_dependencies = [ 'sqlite3' ]
end

Rake::Builder.new do |builder|
  builder.task_namespace       = :test
  builder.target               = 'active_record_test'
  builder.source_search_paths  = [ 'test' ]
  builder.header_search_paths  = [ 'test' ]
  builder.objects_path         = 'test/lib_objects'
  builder.include_paths        = [ 'include', 'test' ]
  builder.library_dependencies = [ 'sqlite3', 'gtest', 'gtest_main', 'active_record_sqlite' ]
  builder.library_paths        = [ 'objects' ]
  builder.target_prerequisites = [ :'rake:build' ]
  builder.default_task         = [ :run ]
end

I'm about to start using Rake::Builder on a sizeable C++ project using QT, so I'll have a chance to see if it handles real world stuff without turning into autoconf.