make is a utility for building programs based on the contents of a specially formatted text file named Makefile or makefile under Linux and Unix environments. 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:
- CC, program for compiling C programs; default: cc
- CXX, program for compiling C++ programs; default: g++
- CFLAGS, extra flags for the C compiler
- CPPFLAGS, extra flags for the C preprocessor
- CXXFLAGS, extra flags for the C++ compiler
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,
- $@, the filename of the target of the rule,
- $<, the name of the first prerequisite,
- $?, the name of all prerequisites newer than the target, separated by spaces,
- $^, the name of all prerequisites with spaces in between them,
- $|, the names of all order-only prerequisites, with spaces in between them
- $*, the stem with which an implicit rule matches
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.
BUILT-IN IMPLICIT RULES
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
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 $@
$@ 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