As far as build systems are concerned, this article will not present anything particularly new – instead, we will focus on some piece of software that one might even considered “done”. “Done” in the sense that it is essentially bug-free and feature-complete. How many software products do you know (apart from TeX) that can be considered this way?
GNU Make is just plain fantastic, so let’s put this old fella to a test and see what it has up its sleeves!
But before sending the old veteran to compete in a classic example (make sandwich) with the youngsters, let’s have a look whether build system X actually plays in the same league and what makes make so special:
- Does build system X offer auto-threadability? (make does)
- Is system X as near to the shell as it can get? (make is)
- Can it auto-resume on tasks? (make can)
- Can it intelligtenly figure out ways to find a faraway goal, where you haven’t given it direct clues on how to get there? (make will)
- Is it as concise as possible, while maintaining a maximum of flexibility and thus can be considered “mighty”? (hell yes!)
- Can it be called “declarative”? (make is the definition of declarative!)
Sounds too good to be true? Then, let’s visit a classic example, highlighting a few key concepts on the way: let it make a sandwich for us!
Let’s start out very simple and let us – in best declarative manner – define how our sandwich is to be built from the ground up.
Well actually: we’re coming from the end result. A sandwich is made of sliced bread, butter, ham, sliced tomatoes and a little salad (preferably washed).
Let’s write a file named
Makefile and declare our rules:
sandwich: sliced.bread washed.salad sliced.tomatoes butter ham echo sliced.bread washed.salad sliced.tomatoes butter ham > sandwich
Now, how do we instruct make to make us a sandwich?
Given you have provided the ingredients, issue a simple
make sandwich on the commandline and there you go!
$ touch sliced.bread washed.salad sliced.tomatoes butter ham $ make sandwich $ ls [...] sandwich
sandwich is the first and only target we have defined in our Makefile, we might also just say
make – and make will pick up the first target it finds. To make sure we will always get a sandwich when we just say
make, let’s set up a default target which we will keep on top of our Makefile, and, for simplicity’s sake, call it
default: sandwich sandwich: sliced.bread washed.salad sliced.tomatoes butter ham echo sliced.bread washed.salad sliced.tomatoes butter ham > sandwich
Since we’re at it, let’s shorten this down a little and get to know our first funky variable,
default: sandwich sandwich: sliced.bread washed.salad sliced.tomatoes butter ham echo $^ > sandwich
$^ will hold the names of all prerequisites of our target, namely:
butter, and, finally
The rule or recipe of our target will paste all of these into our yummy-file called
Let’s assume we only have bread (in our case a file called
bread), not yet “sliced”. How do we declare how to slice the bread? We will assume that renaming the file
bread to be
sliced.bread will do the job in this case:
sliced.bread: bread mv bread sliced.bread
sliced.bread will be made out of
bread by renaming it. Nice. Let’s see if it works, by creating a file called bread, and then having it sliced for us through make:
$ touch bread $ make sliced.bread $ ls sliced.bread
Let’s follow right up and do the same for the tomatoes:
sliced.tomatoes: tomatoes mv tomatoes sliced.tomatoes
See the pattern? Let’s go ahead, DRY this out a little and refactor, so we can roll this same pattern (slicing) into one rule for both ingredients: tomatoes and bread. On the way we will yet again meet funky variables (also called “Automatic Variables“) and, through the refactoring, arrive at a so called “Pattern Rule“.
sliced.%: % mv $< $@
Here we go: both rules rolled into one! A “sliced something” is made by taking something and slicing (in our case renaming) it from something to sliced.something! Easy, huh?
The percent sign
% in this case is very similar to the shell’s globbing
*. Its name here is
% however, so that it doesn’t collide with the shell’s glob-
Now how about
$@? As one can easily infer from our former rules,
$< will hold the prerequisite‘s name (
bread in one case,
tomatoes in the other), while
$@ will hold the target‘s name:
Very well, now that we can slice bread as well as tomatoes, let’s move on! Washing the salad (or anything else) should be easy enough now:
washed.%: % mv $< $@
How about we loosen up a little and make the washing itself a little more configurable? Maybe someone will come along and would like to wash the ingredients differently rather than with
mv? We can certainly account for that, so let’s declare the washing mechanism a variable:
WASH ?= mv washed.%: % $(WASH) $< $@
Excellent! Make now knows how to make
sliced.bread from bread,
sliced.tomatoes from tomatoes and
washed.salad from salad!
What if we were to copy rather than move in order to wash the salad and how would we go about it? Again, this task is trivial: we simply set the variable that we just declared in order to change the executing mechanism by setting the variable
cp in our environment:
$ make washed.salad WASH=cp cp salad washed.salad
Beautiful! Now what if we would like to wash the salad again?
$ make washed.salad WASH=cp make: 'washed.salad' is up to date.
Obviously, there is no need to wash the salad again and make informs us that everything was done already: clever! Make will figure out that there is nothing to do by inspecting the timestamps: the file
washed.salad is younger than the
salad so there can’t be any possible changes that would force us to do the same work another time.
To wrap up for this first episode, let’s see what we have up to now and continue to have make do the groceries for us in our next installment:
WASH ?= mv default: sandwich sliced.%: % mv $< $@ washed.%: % $(WASH) $< $@ sandwich: sliced.bread washed.salad sliced.tomatoes butter ham echo $^ > sandwich
Until we have taught make how to do groceries always make sure you have the right ingredients prepared for it by creating the necessary files using
touch bread salad tomatoes butter ham.
Continue reading: GNU make – an oldie but goldie (Part II)
[fblike style=”button_count” showfaces=”false” width=”90″ verb=”like” font=”arial” float=”left”] [fbshare type=”button” float=”left”] [google_plusone size=”medium” float=”left”] [twitter style=”horizontal” float=”left” lang=”de”] [linkedin_share style=”right” float=”left”]