Software! Math! Data! The blog of R. Sean Bowman
The blog of R. Sean Bowman
May 11 2015

We’ve been talking about the Redo build system, and I promised some more interesting examples. This one comes from a project my colleague Itamar Gal and I did a few years back, and it shows off how easy and flexible Redo is for small/medium C++ projects.

The program is called smash, and it computes the persistent homology of a point cloud. (I’d love to talk about persistent homology sometime, but for now let’s just thing of this as a small C++ project.) What needs to be done for a project of this size? First we need to build the executable. Second, we need to run tests. (Where I come from, we brush our teeth, clean up after ourselves, and test our software.) Finally, we need to be able to clean up all the junk we might make in the process of development. We didn’t do an install script, so I won’t cover that.

Cleaning

Let’s start easy: clean.do just removes some files. It has no dependencies.

rm -f main_vr tests *.log *.o *~ nohup.out\
  main_clique main_collapse gmon.out

Generating a compile script

The next part is a bit harder. We need to compile stuff, but we’d like some flexibility. In particular, we don’t want to have to rely on lots of environment variables, and we might need different settings on different machines. You can read more at the Redo github site, but here’s our script, called compile.do:

if [ -e "/usr/bin/g++-4.9" ]; then
    CC=g++-4.9
    STD=c++14
elif [ -e "/usr/bin/g++-4.6" ]; then
    CC=g++-4.6
    STD=c++0x
fi
DEBUG_FLAGS="-g"
LIBS="-lboost_program_options -lboost_timer"
OPTS="-O3 -march-native"
WS="-Wall -Werror -Wno-deprecated"
CFLAGS="$OPTS --std=$STD $WS -D_GNU_SOURCE $LIBS"
echo "$CC $DEBUG_FLAGS $CFLAGS $INC \$@" > $3
chmod +x $3

Okay, huh? Remember that the variable $3 will hold the name of the output file if the command is successful. IN this case, that’s compile (since the .do file is compile.do). First we find out what version of g++ our machine supports (we were developing on two machines when we wrote this), then we determine appropriate options, we write out a shellscript, and finally we make it executable using chmod. The result may look something like this:

g++-4.9 -g -O3 -march=native --std=c++14\
  -Wall -Werror -Wno-deprecated -D_GNU_SOURCE\
  -lboost_program_options -lboost_timer  $@

Compiling

This is a nice, self contained shellscript that takes a .cpp file and compiles it with the settings we’ve chosen. Awesome. Now we can do the actual building of our program. As in the last post, we need a way to build object files from C++ ones. We can do that generically with a default.o.do script.

redo-ifchange compile $2.cpp
./compile -MD -MF $2.d -c -o $3 $2.cpp
read DEPS <$2.d
redo-ifchange ${DEPS#*:}
rm $2.d

Have a look at the first line. It says that the .o file depends on the .cpp file, sure, but it also depends on the compile script. That’s crucial, because if we move machines or add new flags, we need to remake the software. Next we do the awesome trick we learned last time about letting the compiler tell us the dependencies and rebuilding if any have changed.

The next couple of files are now easy to understand. First, we can make the main program, here called main_collapse.

DEPS="main_collapse.o simplex.o input.o chain.o"
redo-ifchange compile $DEPS
LIBS="-lboost_program_options -lboost_timer -lboost_system"
./compile -o $3 $DEPS $LIBS

Again, we use the compile script with some extra flags to compile our executable, which depends on some object files as well as the compile script.

Testing

Finally, to test, we have the following two scripts. The first one tests.do, builds the tests.

DEPS="tests.o simplex.o input.o chain.o"
redo-ifchange $DEPS
./compile -L/home/rsbowman/src/boost_1_48_0/stage/lib -o $3\
  $DEPS -lboost_unit_test_framework -lboost_system

The second actually runs the tests. Note its dependence on tests.

redo-ifchange tests
./tests 1>&2

And that’s it! This build system served us very well. Of course, this is a small and simple enough project that we could have easily used a make. But I really like the ideas of Redo: I like it’s simplicity, I like that I don’t have to remember weird make syntax (which I never can), and I like that it’s easy to change and add new tasks.

Approx. 682 words, plus code