Introduction to GNU Make

By sajiimori, edited by tepples

The purpose of GNU Make (as opposed to batch files) is to selectively rebuild the parts of a project that need rebuilding. It does this by checking the modification dates of the input files and comparing them to the dates on the output files (if they even exist yet). If the input file has been modified more recently, then the output file needs to be rebuilt.

If you have a file called mypicture.bmp and you want to convert it to a raw binary called mypicture.chr, you could express the input and output files this way:

mypicture.chr: mypicture.bmp

The thing on the left is called the "target", and the thing on the right is the "prerequisite". The whole line is called a "rule". Rules can have multiple targets and prerequisites, such as if you had a tool to convert multiple .bmp files to a pair of a .c file and an .h file:

all_bmps.c all_bmps.h: graphic1.bmp graphic2.bmp graphic3.bmp

Of course, I haven't mentioned how to actually run the commands that produce the targets. Let's say you have a tool called bmp2tiles, and you're going to use that for the first rule above. You could finish the rule this way:

mypicture.chr: mypicture.bmp
	bmp2tiles -b gba mypicture.bmp mypicture.chr

The indented text will be run on the command line, and I call it the "body" of the rule but I don't know if that's standard. Note that you have to use a hard tab, so make sure your editor doesn't convert tabs to spaces.

There's nothing that says you have to actually create the target in the body:

mypicture.chr: mypicture.bmp
	echo I don't feel like doing work right now.

The rule will get executed if mypicture.chr doesn't exist, or if it's older than the prerequisite, so if you don't actually create the file the rule will be run every time you run the makefile, but make won't complain.

Rules can be chained: If A depends on B, and B depends on C, then if C changes then B will be rebuilt, which will cause A to be rebuilt.

A: B
B: C

You can write generic rules (called "implicit rules") that match patterns in filenames to determine how one kind of file is converted to another kind. Here's how you might tell make how to convert any .bmp file to a .chr file:

%.chr: %.bmp
        bmp2tiles -b gba $< $@

% is the wildcard obviously. $< is the input file, and $@ is the target — these are worth memorizing. After this implicit rule is in place, you can have anything depend on a .chr file, and make will automatically look for a .bmp file to convert to .chr in order to satisfy the prerequisite.

So, if you had some .bmp files, and bmp2tiles, and a program to take multiple .chr files and make an archive out of them, you could convert all the .bmp files to an archive this way:

archive.bin: graphic1.chr graphic2.chr graphic3.chr
	make-archive graphic1.chr graphic2.chr graphic3.chr

%.chr: %.bmp
        bmp2tiles -b gba $< $@

Make will look for the prerequisite .chr files, and when it doesn't find them, it will look for a rule to build them. Then it will see the bmp->chr rule, and search for corresponding .bmp files, then run the rule on them to satisfy the prerequisite.

Duplicating the list of .chr files is ugly. Use variables to clean things up:

CHR_FILES = graphic1.chr graphic2.chr graphic3.chr

archive.bin: $(CHR_FILES)
	make-archive $(CHR_FILES)

GNU Make comes with some built-in implicit rules, notably for converting .c files to .o files. To build a PC executable using GCC, you specify which C compiler to use (with the CC variable) and then do this:

CC = gcc
OBJS = main.o file1.o file2.o

my_program.exe: $(OBJS)
	gcc -o my_program.exe $(OBJS)

If main.c includes file1.h and file2.h, make sure to list those as prerequisites so that when they change, main.o will be rebuilt.

main.o: file1.h file2.h 

Return to gbadev.org Beginners' FAQ