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

% is really useful, but is somewhat confusing because of the variety of situations it can be used in.

% 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.