First Makefile
Last updated on 2026-10-07 | Edit this page
Overview
Questions
- How do I write a simple Makefile?
Objectives
- Introduce key parts of a Makefile, rules, targets, dependencies and actions.
- Run Make from the shell.
- Edit a Makefile
- Explain when and why to mark targets as
.PHONY.
Save your work from the previous episode and checkout the branch for this episode:
BASH
$ git add report.tex
$ git commit -m "my work"
$ git switch -c my-02-branch remotes/origin/02-first-makefile
$ git ls-files
which should yield
OUTPUT
.gitignore
Makefile
README
abyss.txt
analyze.py
isles.txt
local.bib
report.tex
Look at the file called Makefile with
OUTPUT
SHELL=bash
# First target, report.pdf, is the default.
report.pdf : local.bib report.tex abyss.pdf isles.pdf results.tex abyss.head
latexmk -pdf report.tex
# Using a new feature of gnu-make as of version 4.3 that permits making
# several targets with one rule. See
# https://www.gnu.org/software/make/manual/make.html#Multiple-Targets
abyss.dat abyss.pdf abyss.two &: analyze.py abyss.txt
python analyze.py abyss.txt abyss.dat abyss.pdf |tee abyss.two
isles.dat isles.pdf isles.two &: analyze.py isles.txt
python analyze.py isles.txt isles.dat isles.pdf |tee isles.two
results.tex: abyss.two isles.two
@echo Create $@ with an editor from $^ ; exit 1
abyss.head: abyss.dat
@echo Write a rule in the Makefile to create $@ from $^ ; exit 1
This is a build file, which for Make is called a Makefile - a file executed by Make. All of the indentation in this Makefile consists of TABs. It is unfortunate that while the difference between TABs and multiple SPACEs is invisible, the difference is significant for the program make.
If we try to run make we get
OUTPUT
python analyze.py abyss.txt abyss.dat abyss.pdf |tee abyss.two
top_two=[4044, 2807]
python analyze.py isles.txt isles.dat isles.pdf |tee isles.two
top_two=[3822, 2460]
Create results.tex with an editor from abyss.two isles.two
make: *** [Makefile:19: results.tex] Error 1
Let us talk about what happened.
If make is invoked without arguments, it uses the default name Makefile for instructions. In Makefile, since the first target is report.pdf, make will try to build that. Now we will talk about each of the elements of the rule
report.pdf : local.bib report.tex abyss.pdf isles.pdf results.tex abyss.head
latexmk -pdf report.tex
-
report.pdfis the target, the file to be created, or built. - A colon,
:, separates targets from dependencies. -
latexmk -pdf report.texis the action that will be executed once the dependencies exist. -
report.texandlocal.bibare the first two dependencies, files that are needed by the action to update the target. Targets can have zero or more dependencies. Those two dependencies already existed and were listed by git ls-files. -
abyss.pdfandisles.pdfare the third and fourth dependencies. Since they did not exist, make invoked the second and third rules in the Makefile to build them using analyze.py. -
results.tex,the fifth dependency did not exist. So make invoked the fourth rule. The action for the fourth rule (on line 19 of the Makefile) has a return value of 1 which indicates an error. So when it is invoked make terminates and reports the error. - Together, the target, dependencies, and actions form a rule.
The dependencies results.tex and abyss.head don’t exist, and the rules in the Makefile to build them return errors.
Dependencies
The order of rebuilding dependencies is arbitrary. You should not assume that they will be built in the order in which they are listed.
Dependencies must form a directed acyclic graph. A target cannot depend on a dependency which itself, or one of its dependencies, depends on that target.
Let’s create results.tex from the results of running analyze.py with a text editor and create abyss.head with
Checking the files we find:
OUTPUT
the 4044
and 2807
of 1907
a 1594
to 1515
in 1221
i 974
was 695
it 680
for 675
OUTPUT
Book & First & Second & Ratio\\ \hline
abyss & 4044 & 2807 & 1.44 \\
isles & 3822 & 2460 & 1.55
Finally we can build and view the pdf document with
In the next episode, we will restructure the Makefile and the python code to automate building result.tex. But before that, we will make some easy changes.
First, put the command we used to build abyss.head into the Makefile. The modified rule should be
abyss.head: abyss.dat
head -n 10 abyss.dat |awk '{print $$1, $$2}' > abyss.head
Make maps the double $$ to a single $ before executing the action. We can verify that with
OUTPUT
head -n 10 abyss.dat |awk '{print $1, $2}' > abyss.head
Next add the following block to Makefile
clean:
rm -f abyss.head *.pdf *.dat *.two *.aux *.bbl *.blg *.fdb_latexmk \
*.fls *.log
touch clean
Now try the following sequence
Try again
OUTPUT
make: 'clean' is up to date.
Change the block to
.PHONY: clean
clean:
rm -f abyss.head *.pdf *.dat *.two *.aux *.bbl *.blg *.fdb_latexmk *.fls *.log
touch clean
and test with
Finally change the block to
.PHONY: clean
clean:
rm -f abyss.head *.pdf *.dat *.two *.aux *.bbl *.blg *.fdb_latexmk *.fls *.log
“Up to Date” Versus “Nothing to be Done”
If we ask Make to build a file that already exists and is up to date, then Make informs us that:
OUTPUT
make: `isles.dat' is up to date.
If we ask Make to build a file that exists but for which there is no rule in our Makefile, then we get message like:
OUTPUT
make: Nothing to be done for `analyze.py'.
up to date means that the Makefile has a rule with one
or more actions whose target is the name of a file (or directory) and
the file is up to date.
Nothing to be done means that the file exists but either
:
- the Makefile has no rule for it, or
- the Makefile has a rule for it, but that rule has no actions
Finally, if we ask Make to build a file that doesn’t exist and for which there is no rule in our Makefile we get a self explanatory message like this
OUTPUT
make: *** No rule to make target 'foo'. Stop.
Makefiles as Documentation
By explicitly recording the inputs to and outputs from steps in our analysis and the dependencies between files, Makefiles act as a type of documentation, reducing the number of things we have to remember.
Makefiles Do Not Have to be Called
Makefile
We don’t have to call our Makefile Makefile. However, if
we call it something else we need to tell Make where to find it. This we
can do using -f flag. For example, if our Makefile is named
MyOtherMakefile:
Sometimes, the suffix .mk will be used to identify
Makefiles that are not called Makefile
e.g. install.mk, Rules.mk etc.
When it is asked to build a target, Make checks the ‘last modification time’ of both the target and its dependencies. If any dependency has been updated since the target, then the actions are re-run to update the target. Using this approach, Make knows to only rebuild the files that, either directly or indirectly, depend on the file that changed. This is called an incremental build.
- Use
#for comments in Makefiles. - Write rules as
target: dependencies. - Specify update actions in a tab-indented block under the rule.
- Use
.PHONYto mark targets that don’t correspond to files.