make is a utility for building programs based on the contents of a specially formatted text file named Makefile or makefile. A makefile has explicit rules, implicit rules, variable definitions, directives and comments. In the primer make tutorial, we looked at some example makefiles containing explicit rules. In this tutorial we will look at the variable definitions, directives and implicit rules.
There are two kinds of variables in make. The first one is called recursively expanded variables. These are defined
using the =
operator. The variables are expanded in a lazy fashion, just at the time required. For example,
x = $(y)
y = $(z)
z = a
all:
@echo $(x)
The substitutions happen when the value of x is to be printed in the echo command. The value printed is a.
The second kind of variables are called Simply expanded variables and are defined using the :=
operator. For example,
x := $(y)
y := $(z)
z := a
all:
@echo $(x)
Since y is not initialized (it does not exist) at the time x is assigned, x does not have a value and the execution of command echo prints nothing.
Some of the pre-defined used by make for implicit rules are:
There are automatic variables in make. Automatic variables are computed afresh for each execution of a rule based on target and prerequisites. The automatic variables are valid only in the commands for a rule and are not defined for the target part or the prerequisites part of a rule. Some of the important automatic variables are,
A directive is a special command for make to read another makefile, decide whether to use or ignore a part of makefile based on values of some variables and defining a variable verbatim from a string containing multiple lines. The following part of a makefile gives an example of a directive defining a multi-line variable.
OBJECTS = scheduler_app.o scheduler.o linklist.o
define complete_message
@echo $(base_msg)
@echo $(additional_info)
endef
base_msg = target
additional_info = made
scheduler-app: $(OBJECTS)
gcc $(OBJECTS) -o scheduler_app
$(complete_message)
After scheduler_app has been made, it displays target
on one line and made
on the
next. The variables defined using define directive are recursively expanded variables.
make works in two phases. First it reads the entire makefile. It captures all the variables and rules in its internal variables and structures. It, then, decides which targets need to be built and executes the command for doing so.
There are built-in implicit rules in make to build various targets from corresponding pre-requisites. Built-in rules
use the customary command and pre-defined variables to build targets. You can change the default value of pre-defined variables
to fine-tune built-in rules to your requirements. For example, the built-in implicit rule of making a .o
file from
.c
file is by the command cc -c file.c.
#
# Makefile
#
OBJECTS = scheduler_app.o scheduler.o linklist.o
scheduler_app: $(OBJECTS)
gcc $(OBJECTS) -o scheduler_app
scheduler_app.o: scheduler.h
scheduler.o: scheduler.h
linklist.o: linklist.h
.PHONY: clean
clean:
rm *.o scheduler_app
In most cases, especially in Linux environments, cc defaults to gcc, cc, being a symbolic link to gcc. The object files scheduler_app.o, scheduler.o and linklist.o are all compiled by the default command,
$(CC) -c $(CFLAGS) $(CPPFLAGS) file.c
Similarly, the default command for compiling C++ programs is
$(CXX) -c $(CXXFLAGS) $(CPPFLAGS) file.cc
Please note that the preferred extension for C++ programs is .cc.
We can define implicit rules by writing pattern rules. In a pattern rule, the target contains a %
character.
A %
matches a non-empty string in the target. For example %.o matches any
.o
file. The sub-string matched by %
is called the stem. For example, %.o matches
scheduler.o and scheduler is the stem. In a rule, %
refers
to the same stem. So, if cc and gcc do not mean the same command on your system, you can ensure that gcc
is used for compilation by writing the following pattern rule,
%.o: %.c
gcc -c $< -o $@
$<
and $@
are the automatic variables described earlier in this tutorial. $<
refers to the first
prerequisite while $@
refers to name of the target of the rule. This is the rule for building any .o
file from
.c
file. And the complete makefile incorporating the pattern rule is,
#
# makefile
#
OBJECTS = scheduler_app.o scheduler.o linklist.o
%.o: %.c
gcc -c $< -o $@
scheduler-app: $(OBJECTS)
gcc $(OBJECTS) -o scheduler_app
scheduler.o: scheduler.h
linklist.o: linklist.h
scheduler_app.o: scheduler.h
.PHONY: clean
clean:
rm *.o scheduler_app