Make: A Quick and Easy Guide

Table of Contents

1. Introduction

GNU make is a common unix programming helper that controls the building of targets
from prerequisites. At its most basic, it takes a look at the time-stamp of a target file
and compares that to the timestamp of its prerequisites. Think of targets as completed pieces of
a larger whole, or the whole itself. Think of prerequisites as source files. Any target whose
prerequisites have been changed (they are newer) is regenerated with the build rules you supply.

make can be used for any task where you want to turn sources into completed code.
You can use it to convert many .tex files into a final .ps, or
many .c files into a final object (making all the little .o files in between.)

Imagine you have 30 .c files,
each of which makes a .o, and then you link them into the final ELF executable.
Since make only rebuilds targets whose sources have changed, if you only change one
.c file, only one .o file is regenerated, and is then relinked with the pre-existing
and unchanged .o object files.

2. Basics

When you type make at the command prompt, make looks
in makefile or Makefile in the pwd

for your rules (how targets and prereqs. are related. You can
make a specific target to build:
make target, or even define macros: make EMPREL=emp_2410p4i kernel,
or with just make build the first target in the Makefile.

From the man page:

-f file file use file as a makefile
-n notify show what make would do, but don’t do it
-s silent do not print the commands as they are executed
-o file old file is treated as very old and its rules are ignored
-j # jobs number of jobs (commands) to run simultaneously
-d debug which files are being considered for remaking? …

The basic rule set to make a target from a dep is:

target: sources
________rule(s)/command(s) to turn sources into target (runs in a sh shell)

Don’t put in the underscores, this indicates a tab. If you forget to make this
a tab, and instead have spaces, make will give you very hard to diagnose errors.

2.1. Syntax

Make has a fairly picky language. You must write perfect Makefiles, or it will whine.
every command in a Makefile that make decides needs to be run is run inside it’s
own sub-shell. If you need sequential commands in one shell, you must &&

them together, or use ( ; ; 😉. You can use \ to split long lines.

: separates a target from a dep
= separates a macro name from its value
:= appends to the end of a macro name
+= appends to the end of a macro name
[TAB] is required before any command
# makes the rest of the current line a comment
\ continues this line to the next (kill the \n newline)
- before a command tells make to continue on failure
@ before a command prevents its echo to the terminal
$ token stands for the input file (first dependency)
$^ token stands for the input files (all dependencies)
$@ token stands for the output file (target)
$* token is the input file without its suffix
% glob’s files together like the shell’s *

2.2. Variables (Macros)

Make variables are defined in NAME = value pairs anywhere in the file.
You then use these later as $(NAME).
You can define things to be evaled by the shell with backticks (``).
Or, you can just make shorthand for easier coding later.

NAME = empclone## easily change base file name
EXT = .tgz## gzipped tar
VER = 248-saic## easily change version #

TGZFILE = .$(NAME).v$(VER)$(EXT)## archive file name

DATE = `date +%m%d%y`## current date
UNAME = `uname -r`## running kernel

INS = /usr/bin/install -D -m 644#
MKD = /bin/mkdir -p#
SYSLINUX_FILES = boot.msg vmlinuz.emp
SYSLINUX_FILES := $(SYSLINUX_FILES) ldlinux.sys param.msg\

syslinux.cfg empclone.ird
SYSLINUX_FILES += ldlinux.sys param.msg syslinux.cfg empclone.ird

2.3. Suffix and Pattern Rules

The following suffix rule is built into make, and it says to build
an object file from C source:

.c.o:
gcc -c ${CFLAGS} $

Pattern rules are better, they use the % sign to glob, just
like the shell does with
*. So now we have:

%.o: %.c
gcc -c -o $@ $(CFLAGS) $

Now, any time there is a .o file requested by some other
dependency, make will run off and build it from the .c using:
gcc -c -o source.o ${CFLAGS} source.c

2.4. Functions

If you info make, you will discover a vast amount of good stuff built into make.
There are several of these constructs used in our sample makefiles, but there are a
great many more available. Some examples: $(wildcard, $(shell, $(subst,
$(foreach, $(suffix, and $(basename.

3. Evolution Example

Usually we start with a simple makefile for a simple program and it evolves,
adding increasing complexity as our needs dictate.

Here we just give the full compilation command line. All intermediate objects
must be re-built every time.

foobar: foo.c bar.c foo.h
gcc -o foobar foo.c bar.c foo.h

clean:

rm -f foobar

Now, we split the objects out, so if only foo.c is touched,
only foo.o is made.
But we paid a terrible price, in that we typed foo.o4 times!

foobar: foo.o bar.o

gcc -o foobar foo.o bar.o

foo.o: foo.c foo.h
gcc -c foo.c

bar.o: bar.c foo.h
gcc -c bar.c

clean:
rm -f foobar foo.o bar.o

We really don’t want to keep typing foo.o bar.o foo.c and bar.c everywhere.
What if we were to split off modules called baz.c and fie.c?
Now we can just add them once to OBJECTS and SOURCES.

CC:=gcc
HEADERS:=foo.h
OBJECTS:=foo.o bar.o fie.o baz.o
SOURCES:=foo.c bar.c fie.c baz.c

foobar: $(OBJECTS)
$(CC) -o foobar $(OBJECTS)

$(OBJECTS): $(SOURCES) $(HEADERS)
gcc -c $(SOURCES)

clean:
rm -f foobar $(OBJECTS)

Now, what if someone gave us a bum gcc? We don’t want to replace gcc -c 29 times,
so right now before this gets any bigger, we just define CC:=gcc and use it as $(CC).
This will also allow us to issue make CC=kgcc and not even edit the makefile.
On a quad processor machine, we might even say make -j 16 for this small sample.
Be aware that inheritance in recurvive make can make make MAKE=`make -j 16` really a bad idea (its broke).

We also notice that we can obfuscate this a bit, and type less if we replace (in the commands)
$(SOURCES) with $. If we expand $(OBJECTS), we have
a target with multiple files, and each is built seperately using the same
template. Also note that foobar becomes $(FOOBAR)
and then becomes $@, the output file!

CC:=gcc# or kgcc, or g77 ...
HEADERS:=foo.h
OBJECTS:=foo.o bar.o fie.o baz.o
SOURCES:=foo.c bar.c fie.c baz.c
FOOBAR:=foobar

$(FOOBAR): $(OBJECTS)
$(CC) -o $@ $

$(OBJECTS): $(SOURCES)
$(CC) -c $

clean:

$(FOOBAR) $(OBJECTS)

4. EmpClone

Download this
example file (text format).

5. EmpWeb

Download this
example file (text format).

6. Resources