Notes on GNU Make
15 Oct 2020GNU Make is a tool to manage build systems. It binds targets to some dependencies and a set of commands.
Makefile
The program make gets its rules that instruct the generation of the targets via the makefile. The makefile is determined in the following order:
- If the file options is passed (
make [-f|--file] filename
) to make, thenfilename
is selected as its makefile; -
GNUmakefile
is selected next, although its generally discouraged, unless the makefile explicitly requires GNU Make (as opposed to other distributions); - If non the first exist in the directory, then
makefile
is selected; - Lastly,
Makefile
. This is the recommended name because appears at the beginning of als
and it is close to other project config files.
Recipe and Execution
A makefile consists of one or more recipes, each corresponding to one target and its dependencies and commands:
# The commands must start with TAB. Cannot be 4 spaces.
[target1 target2 ...] : [dependency1 dependency2 ...]
command1
command2
...
To generate the targets, simply run:
$ make [target1 target2 ...]
and targets will be generated in this order. If a previous target fails, the program stops and the commands for the following targets will be abandoned. For example we have makefile
target: dep
touch target
target2:
touch target2
where dep
doesn’t exist and leads to target
failing to build executing
$ make target target2
make: *** No rule to make target 'dep', needed by 'target'. Stop.
The execution stops right away and target2
is not created because of the previous failure.
By default, if no target is passed to make, make executes the commands for the first target in the makefile, excluding those starting with a dot (.
). In the previous example, make
is equivalent of make target
.
When a recipe is repeated for the same target, the former one is overridden. For example, if we append
target:
echo override > target
to the previous example, we have
$ make
Makefile\:6\: warning: overriding recipe for target 'target'
Makefile\:2\: warning: ignoring old recipe for target 'target'
echo override > target
Above is true for recipes, but in the case of dependencies, the dependencies in the additional rule is added to the target dependencies (instead of overwriting). And if the additional rule only specifies dependencies but not recipes, it will not yield a warning message. For example, if we instead append a new rule
target: dep
then dep
is added to target
’s dependency, and running make target
doesn’t give any warning.
Dependencies
Dependencies in make have two instructions:
- Build Instruction. Dependencies either need to be built first if they are targets (leading to a build dependency-graph), or need to exist as files (leading to failure if non-existing);
- Rebuild instruction. If dependency is newer than target, rebuild is required.
If dependencies cannot be resolved (e.g. circular dependency), then it is ignored:
# A simple example "target: target"
$ make target
make: Circular target <- target dependency dropped.
GNU describes dependencies in more details in Types of Prerequisites, where it further describes a special order-only prerequisites that specifies only instruction #1 for special use cases like directory creation.
This behavior guarantees that targets are only rebuilt when necessary. To force a rebuild when dependencies rules deem it unnecessary, use
# Option forcefully consider all dependencies out-of-date.
$ make [-B|--always-make]
Special Targets
Make has a list of special targets that are used for specific causes.
Oftentimes, these special targets impose specific rules to their dependencies. For example, .PHONY
’s dependencies are considered phony targets, meaning their recipe are always run. This is useful for abstract targets that does not correspond to physical files, e.g. clean
, all
.
But there are also special targets that offer generic instruction (similar to a flag), e.g. DELETE_ON_ERROR
(if specified, failed recipe leads to deletion of the target) and others that provide rules via their recipes, e.g. .DEFAULT
(its recipe is used for targets with no rules).
For a comprehensive list, see Special Built-in Target Names.
Target names not starting with .
do not have special meaning. However, GNU recommends some Standard Targets like all
, clean
, etc.
Macros
For a more complex makefile, make supports macros to complement the target rules. For macros, important concepts includes variables, directives, functions.
Variables
Make variables are names to represent texts.
Variables in make work by text substitution and they can be used in any context across the makefile by dereferencing using
$(var)
or ${var}
.
Variables come in two flavors: Simply Expanded Variable and Recursively Expanded Variables.
A simply expanded variable is defined by :=
or ::=
, and is similar to general concept of a variable in well-known programming languages. The variable is resolved at the time of the variable definition:
var1 := a
var2 := $(var1)
var1 := b
test: ; @echo $(var2)
# prints 'b'
A recursively expanded variable is defined by define
or =
, and is similar to target dependencies graph. It attempts to resolve all referenced variables on the right hand side before expanding:
var1 := a
var2 = $(var1)
var1 := b
test: ; @echo $(var2)
# prints 'a'
. On the case where a circular dependency occur, an error is reported:
var1 := a
var2 = $(var1)
var1 = $(var2)
test: ; @echo $(var2)
# Recursive variable 'var1' references itself (eventually). Stop.
?=
, +=
are some additional assignment operators that make supports. Apart from assignments, variables can also get their values from environment variables, pass-in from make arguments, etc.
Directives
Directives adds another layer of control for make. The most impactful ones being define
(variable manipulation), include
(scalability) and if
controls.
A full list of supported directives can be found in Make Manual Appendix.
Functions
Make has a rich selection of built-in functions for text manipulation, such as patsubst
, wildcard
, etc.
To call a function, use syntax $(func arg1,arg2...)
.
GNU provides a comprehensive list of built-in functions.
If the built-in function are not enough, additional options such as using call function to integrate built-in functions and operations or canned variables for more flexibility.
Symbols
Lastly, let’s overview some special symbols used by make.
-
@
at the start of a recipe silents the ‘echo’ so that the recipe is not printed out during execution; -
#
is used for comments; -
-
at the start of a recipe ignores the errors; -
%
for pattern matching; -
$
is used for dereferencing variables and calling functions. Also important is the combination of$
with other special characters for automatic variables, such as$@
for target,$<
for first prerequisite, etc.