Spiking Makefiles with BASH

The thing with complex projects is that they very often require complex build scripts. The build script for a given project can be a mix of Bash, Perl, and Make scripts. It is often convenient to write a script that ensures that all the project’s dependencies are installed, of the right version, or that builds them if necessary.

We often also need much simpler things to be done, like generating a build number, from within the makefile script. If you use makefiles, you know just how fun they are to hack and you probably do the same as I do: minimally modify the same makefile you wrote back in 1995 (or “borrowed” from somewhere else.) In many cases, that makes perfect sense: it does just what it has to do. In this week’s post, I show how to interface (although minimally) Bash from Make.

Calling make from Bash isn’t a problem: make is a well behaved program like any other to which you can pass parameters and that returns appropriate errors codes for success and failure. The other way around isn’t seen as often, but is about as easy.

Let us suppose we want to generate a build number for the current build. Writing a Bash script that reads the content of a file, increments it, writes it back and returns it would go something like this:

#!/bin/bash
#filename: buildnumber.sh

if [ -e build.number.dat]
   then
    build_number=$((`cat build.number.dat`+1))
   else
    build_number=1000
fi

echo $build_number | tee build.number.dat

Invoking the script buildnumber.sh from the command like would simply print a number, starting at 1000, incrementing by 1 each time you invoke it. To call a shell script—or any other command—you use Make’s shell macro. The Make shell macro works pretty much as Bash’s back-quotes ` `: it calls the command enclosed and transforms its output (on stdout) into a string that is assignable to a variable. Adding buildnumber.sh to your Makefile could be performed as follows:

project:
        $(COMPILER) $(CFLAG) \
                -D__BUILD_NUMBER__=$(shell buildnumber.sh) \
                -c $(SRC)
        $(LINKER) $(LFLAG) $(LIBS) $(OBJECT) -o my_project

This will cause buildnumber.sh to be invoked every time you build your project: target. In this first example, buildnumber.sh doesn’t take any arguments, but you could pass it arguments if you needed to. The call would then become something like:

project:
        $(COMPILER) $(CFLAG) \
           -D__BUILD_NUMBER__=$(shell buildnumber.sh $(VARS)) \
                -c $(SRC)
        $(LINKER) $(LFLAG) $(LIBS) $(OBJECT) -o my_project

where VARS is some variable set in the Makefile environment.

This script has a major problem. It will increment the build number even if the compilation fails, which may or may not make sense for you. Let us modify the buildnumber.sh script to include a backup of the current build number:

#!/bin/bash
#filename: buildnumber.sh

if [ -e build.number.dat]
   then
    build_number=$((`cat build.number.dat`+1))
    cp build.number.dat build.number.dat.saved
   else
    build_number=1000
fi

echo $build_number | tee build.number.dat

This versions backups the current build number before overwriting it with the new build number. We must now modify the Makefile to rollback build numbers if build fails for some reason. The Makefile now contains:

project:
        if $(COMPILER) $(CFLAG) \
             -D__BUILD_NUMBER__=$(shell buildnumber.sh) \
             -c $(SRC) && \
           $(LINKER) $(LFLAG) $(LIBS) $(OBJECT) -o my_project ; \
        then \
           echo "success!" ; \
        else \
           echo "failed!" ; \
           cp build.number.dat.saved build.number.dat ; \
        fi

The if in Make works a lot like Bash’s if construct. Commands return “false” if they return an error, so they can be combined using the && or || operators. In the above modification, if either the compilation or the linkage fails, it echoes failed!, but more importantly, it reverts the build number so that this failed build doesn’t cause the number to be incremented.

One Response to Spiking Makefiles with BASH

  1. gam3 says:

    Thanks. This saved me from having to do a lot of rooting around.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: