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

In the last post, we looked at the basics of the Redo build system and did one simple example. Today I’d like to focus on the dependency tracking aspects. Things are a bit more flexible and powerful in Redo than the other build systems I’m used to, and this is one of the spots that got me pretty excited originally.

We have already seen that the program redo-ifchange is used to rebuild a target that is out of date. In a system like Make, dependency tracking is no big deal. If the target you’re currently working on has unbuilt dependencies, you go make those (and so on recursively) until you have everything you need to build the original target.

Of course, you can do a similar thing in Redo. But there’s more! Check out this example, from the docs themselves. We’ll keep the program foo.c, which depends on foo.o and bar.o. Here is the file foo.do:

 DEPS="foo.o bar.o"
 redo-ifchange $DEPS
 gcc -o $3 $DEPS

Remember, this is just a shellscript. It defines some files that the executable foo depends on (for convenience), tells the system to rebuild them if the files they depend on have changed recently, and uses gcc to link them together to make the foo executable.

The cool part is the .do file that compiles C files into object files. Let’s take a look at default.o.do:

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

So what’s going on here? First, the object file we’re building certainly depends on the source file; that’s the first line. The second line is very cool – I didn’t know about this stuff until a few years ago. The -MD switch tells the compiler to build the object file, but also create a list of headers that are then written to $2.d by the -MF flag. Thus, we get a list of all the headers that this object file depends on, for free, from the compiler! That’s pretty cool, but what happens next is one of the things that sets Redo apart from other build systems. First , the dependency file $2.d is read into a shell variable. Next, we instruct the build system to rebuild the target if any of those files have changed more recently. (Note: the strange expression ${DEPS#*:} just chops off the very beginning of the file, which has the name of the object file followed by a colon. See this Advanced Bash scripting guide for more info).

So here’s the first cool thing about Redo:

Dependency Specification

Redo lets you specify your dependencies beforehand, but it also lets you declare dependencies after the target has been built.

As we’ve seen (and you can imagine) this is very important for C/C++ as we often don’t have complete information about what depends on what until the compiler has at it.

What about the crazy shellscripting?

Yeah, I’m glad you asked. To be honest, I had to look up the last line of default.o.do above – I’m not much of a shellscript person myself. But fear not! Redo doesn’t depend on the shell or any other programming language for its .do files. We could have just as easily written foo.do and default.o.do in Python or Ruby, or whatever you favorite language is. For example, we could have munged the .d dependency file however we liked before sending it to redo-ifchange. This is a second important (cool) thing about Redo:

Language Agnostic

Redo lets you work in whatever language you’re most comfortable with.

Next we’ll look at an example from a project I worked on a few years ago. Redo performed very nicely for us, and I started using it more after that.

Approx. 617 words, plus code