You can imagine how tedious the build process could be if you had a large number of source code files for a particular project. Manually entering individual compiler and linker commands on the command line becomes tiresome very quickly. In order to avoid this, a makefile can be used. A makefile is a script that tells the make utility how to build a particular program. (The make utility is typically installed with the other GNU tools.) The make utility follows the rules in the makefile in order to automatically generate output files from a set of input source files.
Makefiles might be a bit of a pain to set up, but they can be a great timesaver and a very powerful tool when building project files over and over (and over) again. Having a sample available can reduce the pain of setting up a makefile.
The basic layout for a makefile build rule is:
target: prerequisite command
The target
is what is going to
be built, the prerequisite
is a file
that must exist before the target
can
be created, and the command
is a
shell command used to create the target
. There can be multiple prerequisites
on the target line (separated by
white space) and/or multiple command lines. But be sure to put a tab,
not spaces, at the beginning of every line containing a command
.
Hereâs a makefile for building our Blinking LED program:
XCC = arm-elf-gcc LD = arm-elf-ld CFLAGS = -g -c -Wall \\ -I../include LDFLAGS = -Map blink.map -T viperlite.ld -N all: blink.exe led.o: led.c led.h $(XCC) $(CFLAGS) led.c blink.o: blink.c led.h $(XCC) $(CFLAGS) blink.c blink.exe: blink.o led.o viperlite.ld $(LD) $(LDFLAGS) -o $@ led.o blink.o clean: -rm -f blink.exe *.o blink.map
The first four statements in this makefile contain variables for use in the makefile. The variable names are on the left side of the equal sign. In this makefile, the respective variables do the following:
XCC
Defines the compiler executable program
LD
Defines the linker executable program
CFLAGS
Defines the flags for the compiler
LDFLAGS
Defines the flags for the linker
Variables in a makefile are used to eliminate some of the
duplication of text as well as to ease portability. In order to use a
variable in the code, the syntax $()
is used with the variable name enclosed in
the parentheses.
Note that if a line in a makefile gets too long, you can continue
it on the following line by using the backslash (\\
), as
shown with the CFLAGS
variable.
Now for the build rules. The build targets in this file are
all
, led.o
, blink.o
, and blink.exe
. Unless you specify a target when
invoking the make utility, it searches for the first target
(in this case, the first target is all
) and tries to build it; this, in turn, can
lead to it finding and building other targets. The make utility creates (or re-creates, as the
case may be) the target file if it does not exist or if the prerequisite
files are more recent than the target file.
At this point, it might help to look at the makefile from the
bottom up. In order for blink.exe
to
be created, blink.o
and led.o
need to be built as shown in the
prerequisites. However, since these files donât exist, the make utility will need to create them first.
It will search for ways to create these two files and will find them
listed as targets in the makefile. The make utility can create these files because
the prerequisites (the source files) for these two targets exist.
Because the targets led.o
and
blink.o
are handled similarly, letâs
focus on just one of them. The prerequisites for the target led.o
are led.c
and led.h
. As stated above, the command tells the
make utility how to create the
target. The first part of the command for led.o
is a reference to the variable XCC
, as indicated by the syntax $(XCC)
, and the next part of the command is a
reference to the variable CFLAGS
, as
indicated by the syntax $(CFLAGS)
.
The make utility simply replaces
variable references with the text assigned to them in the makefile. The
final part of the command is the source file led.c
. Strung together, these elements
construct the command that the make
utility executes. This generates a command on the shell command line as
follows:
arm-elf-gcc -g -c -Wall -I../include led.c
This is the same command we entered by hand in order to compile the led.c file earlier in this chapter, in the section âBuilding the Blinking LED Program.â The make utility compiles blink.c in the same way.
At this point, the make
utility has all of the prerequisites needed to generate the target
blink.exe
default target. The command
that the make utility executes (the
same command we entered by hand to link and locate the Blinking LED
program) to build blink.exe
is:
arm-elf-ld -Map blink.map -T viperlite.ld -N -o blink.exe led.o blink.o
You may notice that in this makefile the linker is invoked directly. Instead, gcc could have been used to invoke the linker indirectly with the following line:
arm-elf-gcc -Wl,-Map,blink.map -T viperlite.ld -N -o blink.exe led.o blink.o
When invoking the linker indirectly, the special option âWl is used so that gcc passes the request to generate a linker map file to the linker rather than trying to parse the argument itself. While this simple Blinking LED program does not need to link using gcc, you should remember that more complex C programs may need special runtime library support from gcc and will need to be linked in this way.
The last part of the makefile is the target clean
. However, because it was not needed for
the default target, the command was not executed.
To execute the makefileâs build instructions, simply change to the directory that contains the makefile and enter the command:
# make
The make utility will search the current directory for a file named makefile. If your makefile has a different name, you can specify that on the command line following the -f option.
With the previous command, the
make utility will make the first target it
finds. You can also specify targets on the command line for the
make utility. For example, because
all
is the default target in the
preceding makefile, you can just as easily use the following
command:
# make all
A target called clean
is
typically included in a makefile, with commands for removing old object
files and executables, in order to allow you to create a fresh build.
The command line for executing the clean
target is:
# make clean
Keep in mind that weâve presented a very basic example of the make utility and makefiles for a very basic project. The make utility contains very powerful tools within its advanced features that can benefit you when executing large and more complex projects.
Tip
It is important to keep the makefile updated as your project changes. Remember to incorporate new source files and keep your prerequisites up to date. If prerequisites are not set up properly, you might change a particular source file, but that source file will not get incorporated into the build. This situation can leave you scratching your head.
Additional information about the GNU make utility can be found online at http://www.gnu.org as well as in the book Managing Projects with GNU make, by Robert Mecklenburg (OâReilly). These resources will give you a deeper understanding of both the make utility and makefiles and allow you to use their more powerful features.
Get Programming Embedded Systems, 2nd Edition now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.