Abstract
This is the first installment of the make tutorial. The make tutorial is devised to help you learn about make, and
to apply that knowledge to the automatization of the compilation process. In this installment we will focus on the
make system, rule syntax, and defining and using variables.
Make is a tool that allows the programmer to write rules, in order to accomplish tasks. As such, make can be
used to automatize many tasks, but its main use is in programming projects that are deployed in source
format.
Many open source projects take advantage of make, since it’s readily available on all GNU/Linux systems. Calling
”make”, will cause the make system to search for a Makefile file, in the directory. The first rule encountered will be
handled as the default target. Make will try its best to accomplish every dependency of the target, and the target
itself.
A Make rule target has a special characteristic: it represents a file. When a rule is applied, and a target is fulfilled then a
file is traditionally created.
A rule has a specific format, comprised of three elements: target, dependencies and commands. Lets see a
example,
Listing 1. Makefile
Listing 1. Makefile
The result of the ”make” command will fail. When you run the command, make will try to create ”all” file. Its first
action will be to check the ”one” dependency, and this is where our Makefile fails. ”one” file does not exist, do ”all”
cannot be executed, and make complains it does not know how to create ”one”.
With the inclusion of the new rule, make now knows how to accomplish ”one”. If you execute ”make”, you’ll be greeted
with
Note: You can silence an echo command with a @ before the command (i.e. @echo).
There is a interesting point to the file-as-target system followed by make. If you do not create a file, and both the ”all”
and ”one” rules don’t, the rule will always execute.
Try the following
If you try to execute make, you’ll be informed that all ’all’ is up to date. However if you do,
And try make again, now you’ll be again greeted by the ”I’m executing ’all’ target.” again. This is because the ’all’ file
is just a little bit older than ’one’, and since make should update ’all’ when ’one’ is younger, ’make’ always tries to
execute every command associated with the ’all’ target.
Variables are useful in Makefiles. By default every variable is a space separated list of textual elements. This means that
when you set a variable to ”abc def” the variable has two elements: ”abc” and ”def”. There is a syntax for assigning
value to variables, and for referencing the value within the variable. When used directly, the programmer is
assigning a value to the variable, or defining it. When used with $(¡name¿) the programmer is using its
variable.
There are also two flavors of variables in Makefile. The recursively expanded variable, which are defined with ’=’, and
its evaluation is based on recursive expansion.
Listing 3. Makefile
Listing 3. Makefile
This flavor has two disavantages. One: it does not handle appending. Something like,
Listing 4. Makefile
Listing 4. Makefile
Will cause infinite expansion. The second disavantage is with functions will be rerun every time the variable is evaluated.
There are functions that will be studied in the next installments, like the wildcard that may cause unexpected results when
ran.
DEPENDENCIES=main.c module.c
OBJECTS=main.o module.o
OUTPUT=main
$(OUTPUT): $(OBJECTS)
echo ”I’m_executing_’all’_target.”
OBJECTS=main.o module.o
OUTPUT=main
$(OUTPUT): $(OBJECTS)
echo ”I’m_executing_’all’_target.”
$(OUTPUT) is substitute for main. $(OBJECTS) is substituted for the ”main.o module.o”.