Make
: Automating Processes with a Makefile
Makefiles are used to help decide which parts of a large program need to be recompiled. In the vast majority of cases, C or C++ files are compiled. Other languages typically have their own tools that serve a similar purpose as Make
. Make
can also be used beyond compilation too, when you need a series of instructions to run depending on what files have changed.
A Makefile consists of a set of rules. A rule generally looks like this:
target: prerequisites command command command
The targets are usually file names. Typically, there is only one per rule.
The commands are a series of steps typically used to make the target(s). These need to start with a tab character, not spaces.
The prerequisites are also file names, separated by spaces. These files need to exist before the commands for the target are run. These are also called dependencies.
make
uses the filesystem timestamps as a proxy to determine if something has changed. This is a reasonable heuristic, because file timestamps typically will only change if the files are modified. But it's important to realize that this isn't always the case. You could, for example, modify a file, and then change the modified timestamp of that file to something old. If you did, Make would incorrectly guess that the file hadn't changed and thus could be ignored.
Variables
Variables can only be strings. You'll typically want to use :=
, but =
also works.
Here's an example of using variables:
files := file1 file2 some_file: $(files) echo "Look at this variable: " $(files) touch some_file file1: touch file1 file2: touch file2 clean: rm -f file1 file2 some_file
Single or double quotes have no meaning to Make. They are simply characters that are assigned to the variable. Quotes are useful to shell/bash, though, and you need them in commands like printf. In this example, the two commands behave the same:
a := one two# a is set to the string "one two" b := 'one two' # Not recommended. b is set to the string "'one two'" all: printf '$a' printf $b
Reference variables using either ${} or $()
x := dude all: echo $(x) echo ${x} # Bad practice, but works echo $x
%
Wildcard
- When used in "matching" mode, it matches one or more characters in a string. This match is called the stem.
- When used in "replacing" mode, it takes the stem that was matched and replaces that in a string.
%
is most often used in rule definitions and in some specific functions.
Automatic Variables
There are many automatic variables, but often only a few show up:
hey: one two # Outputs "hey", since this is the target name echo $@ # Outputs all prerequisites newer than the target echo $? # Outputs all prerequisites echo $^ # Outputs the first prerequisite echo $< touch hey one: touch one two: touch two clean: rm -f hey one two
Phony Targets
A phony target is one that is not really the name of a file; rather it is just a name for a recipe to be executed when you make an explicit request. There are two reasons to use a phony target: to avoid a conflict with a file of the same name, and to improve performance.
If you write a rule whose recipe will not create the target file, the recipe will be executed every time the target comes up for remaking. Here is an example:
clean: rm *.o temp
Because the rm
command does not create a file named clean, probably no such file will ever exist. Therefore, the rm command will be executed every time you type make clean.
In this example, the clean
target will not work properly if a file named clean is ever created in this directory. Since it has no prerequisites, clean would always be considered up to date and its recipe would not be executed. To avoid this problem you can explicitly declare the target to be phony by making it a prerequisite of the special target .PHONY
as follows:
.PHONY: clean clean: rm *.o temp
Once this is done, make clean
will run the recipe regardless of whether there is a file named clean.