The make
program (several implementation possible, usually the GNU is installed) is a lightweight and portable tool to automate compilation tasks.
make
rules are built around time-stamps and file existence.
A rule is triggered if the file targeted does not exists or its timestamp is too old.
Even though targets are not always files (see later), we first assume this is the case
target: prerequisites
recipe-1
recipe-2
recipe-3
tab
and are executed (in parallel) as independent process.make
supports the definition of variables as well as the interaction with environment variables. Unless enforced, the variables work in a verbatim way, meaning that they are not evaluated at creation but at use (the same way a macro would work).
# you can shorten long expressions
VAR1 = /path/to/your/file
# this will replace everywhere VAR2 with ${HOME} and evaluate it at use
VAR2 = ${HOME}
# you can force the evaluation of the variable at creation
VAR3 := ${HOME}
# sets a default value to the variable if not defined
VAR4 ?= ${HOME}
.PHONY
targetsmake
provides a few special targets (see bellow for more details) that help to control the behavior of dependencies.
Among them the .PHONY
target
.PHONY: target
instructs to make
that the target is not associated to a file. The consequence is that everytime the target is called (through make target
or as prerequisites) the rule is executed.
It is quite common (not mandatory) to have a few phony targets defined in your makefile:
make clean
should cleanup the current buildmake reallyclean
should really cleanup the current build, including every file that might stay during the clean
rulemake info
displays some information about your programmake
can build multiple recipes in parallel. To do so, simply add the option -j N
where N
is the maximum number of processes. Some user might be tempted to use make -j
without specifying a number, which can cause weird behaviors such as signals 2
or 9
. I would therefore refrain to do so.
There is two ways to handle multi(-line) recipes:
add lines: this will lead to the (maybe parallel and unordered) execution of the recipes. You should also pay attention that every recipe will be executed starting from the location where make
has been called.
use the \
and/or &&
symbols to break lines and introduce dependencies between recipes
Some examples:
# this will work as the recipes are executed both from the same location
my_dir:
cd /path/to/my_dir
mkdir -p my_dir
# This will work as a single recipe
my_dir:
cd /path/to/my_dir && mkdir -p my_dir
# equivalently we can format the recipe over two lines
my_dir:
cd /path/to/my_dir && \
mkdir -p my_dir
You can remove the time-stamp constrain on the prerequisites and only check for existence using |
:
target: file_1 file_2 | file_3
touch target
Here an up-to-date timestamp for file_1
and file_2
is requested while only the existence of file3
is checked.
See here for more information.
It is very convenient to generalize the recipes using the automatic variables defined automatically by make.
Some are particularly famous:
$@
contains the target name,$<
the name of the first prerequisite only,$^
the name of all the prerequisites,$?
the name of all the prerequisites that are newer than the target,$*
the name of the matched pattern.To automate even further the rules, you can use pattern matching and automatic variables (see bellow).
To compile all your .o
files you can for example use the following rule:
%.o: %.c
CXX $^ -o $@
Special targets can be used to further fine-tune the behavior of make
.
.NOPARALLEL:
forbids the execution of make
in parallel for all the recipes contained in this file. This does not apply to the sub-make
processes that might have been called..EXPORT_ALL_VARIABLES
exports all the current variables to a sub-make
call performed in the recipes.make
also comes with convenient functions:
wildcard
foreach