Quick start
If you're working in a project that already uses make,
there should be a file called Makefile in the top-level directory.
Running:
$ make
should build the project from source.
What is make?
make is a tool designed to manage dependencies in a build process.
For instance, if you have two source files, a.in and
b.in, and you need to build b.in before you build
a.in, then you might have a Makefile in the same
directory that looks like this:
a.out: a.in b.out commands to generate a.out from a.in and b.out b.out: b.in commands to generate b.out from b.in
Now, suppose you want to build a.out; you run:
$ make a.out
At this point, make's thought process becomes:
-
Run
make b.out. -
If
a.outis newer thana.inandb.out, halt. -
If
a.outis older thana.inorb.out, run:commands to generate a.out from a.in and b.out
If you run:
$ make b.out
then make's thought process becomes:
- If
b.outis newer thanb.in, halt. - If
b.outis older thanb.in, run:commands to generate b.out from b.in
In short, when ordered to build a file,
make will ensure that all dependencies are up to date,
and it will not rebuild any those which need not be rebuilt.
Makefile structure
Makefiles contain definitions and rules.
A definition has the form:
VAR=value
A rule has the form:
output files: input files commands to turn inputs to outputs
All commands must be tab-indented.
To reference the variable VAR, surround it with
$(VAR).
A simple example for C
Here's a simple example for a C program that defines
foo() in foo.c, but uses it in bar.c.
CC=gcc bar: bar.c foo.h foo.o $(CC) -o bar bar.c foo.o foo.o: foo.c foo.h $(CC) -c foo.c run: ./bar clean: rm -f bar foo.o
Conventions
It's common to include pseudo-targets in makefiles to automate other parts of the development process.
make clean
The rule for clean
in the previous example
doesn't build a file called clean.
Rather, it deletes all of the build targets to allow a fresh build from scratch.
Including a clean rule is a common convention
for make.
make test
Running make test often runs
the test suite for the software.
make install
Running make install typically installs
the compiled binary in a PATH-accessible location.
For software using the autoconf toolchain,
the installation directory is set by running ./configure
command prior to using make.
make run
For simple programs, make run acts like an alias
to the compiled binary.
Generic rules
If a project contains many conversions of one file type to another,
e.g. .c to .o,
it gets tedious to write the commands over and over.
First, define file suffixes
with a .SUFFIXES declaration; for example:
.SUFFIXES: .tex .pdf
Then, create a generic rule for converting one suffix to another:
.input.output: commands to convert something.input into something.output
Within the commands, you can reference
the input filename with $<.
You can reference the output filename
with $@.
For a complete example with LaTeX:
all: doc.pdf .SUFFIXES: .tex .pdf .tex.pdf: pdflatex $<
Running make file.pdf will now run
pdflatex file.tex.
Complex generic rules
For complex generic rules, the pattern % may be used
in both inputs and outputs.
For example, we might want a LaTeX build to depend on both the source file and the auto-generated bibliography file:
%.dvi: %.tex %.blg latex $<
Somewhat confusingly, the commands for these generic rules cannot reference
% to insert the basename of the build target.
To refer to the basename of the target file, use $(basename $@).
Substituting shell commands
To substitute in the value of a shell command,
use $(shell command).
For instance, suppose you wanted to compile all
.java files simultaneously, you might have this rule:
Foo.class: $(shell find . -name *.java) javac $<
Further reading
Robert Mecklenberg provides one of the few comprehensive overviews of (GNU) make in book form.
It's also available as an O'Reilly open book.
And, after make, there is
the autotool site.
Twitter: @mattmight
Instagram: @mattmight
LinkedIn: matthewmight
Mastodon: @mattmight@mathstodon.xyz
Sub-reddit: /r/mattmight