keepcalculator

06 Numbers and Powers

Makefile Tutorial: Syntax, Targets, and Examples

Card No. 06·54037 · Updated · Standard formula, shown below

Average pace 5.00 min/km
Average speed
12.00 km/h
Result
Finish time
50 min
You entered
Distance
10 km
You entered
You enter
Finish time, Distance
You get
Average pace, Average speed
Rounding
2 dp

A Makefile is a script that tells the make utility how to build and manage a project, typically by compiling source code into executables or libraries. You write rules that define targets (what to build), prerequisites (files needed), and recipes (commands to run), then run make to execute only the necessary steps. This article explains the core concepts with concrete examples, common pitfalls, and practical usage patterns.

Formula

The calculator above works out running pace and speed from finish time and distance. The formulas are:

  • Average pace (min/km) = finish time (minutes) ÷ distance (km)
  • Average speed (km/h) = distance (km) ÷ (finish time (minutes) ÷ 60)

Variables: t = finish time in minutes, d = distance in kilometers. Pace is expressed as minutes per kilometer, speed as kilometers per hour.

Worked Example

Using the default values: finish time = 50 minutes, distance = 10 km.

  1. Pace = 50 ÷ 10 = 5.00 min/km
  2. Speed = 10 ÷ (50 ÷ 60) = 10 ÷ 0.8333 = 12.00 km/h

The calculator shows 5.00 min/km and 12.00 km/h, matching the manual calculation.

Why Use a Makefile?

Makefiles automate repetitive build tasks. Instead of typing long compiler commands each time, you define them once. More importantly, make checks file timestamps: if a source file hasn’t changed since the last build, it skips recompiling that part. For large projects with hundreds of files, this saves significant time. Makefiles also handle dependencies—if a header changes, all files that include it get rebuilt automatically.

Basic Syntax and Structure

A makefile consists of rules with this shape:

target: prerequisites
	recipe
  • Target: usually a file to generate (e.g., program) or a phony name like clean.
  • Prerequisites: files that must exist before the target is built. If any prerequisite is newer than the target, the recipe runs.
  • Recipe: shell commands, each line starting with a tab character (not spaces).

Example for a C program:

program: main.c utils.c
	gcc -o program main.c utils.c

If you run make program, make checks whether program exists and whether main.c or utils.c is newer. If so, it executes the gcc command.

Variables and Automatic Variables

Variables reduce repetition. Define them with NAME = value and use with $(NAME).

CC = gcc
CFLAGS = -Wall -O2

program: main.c utils.c
	$(CC) $(CFLAGS) -o $@ $^

Here $@ is the target name (program), and $^ is all prerequisites (main.c utils.c). Other useful automatic variables:

  • $< – first prerequisite
  • $? – prerequisites newer than target
  • $* – stem of the target (for pattern rules)

Pattern rules use % as a wildcard. To compile any .c file into a .o file:

%.o: %.c
	$(CC) $(CFLAGS) -c $< -o $@

Phony Targets and Common Workflows

Targets like clean don’t correspond to files. If a file named clean exists, make would think it’s up to date and skip the recipe. Declare them as phony:

.PHONY: clean all

all: program

clean:
	rm -f *.o program

Now make clean always runs. A typical workflow includes all (default target), clean, install, and test. The first target in the makefile is the default when you run make with no arguments.

Common Mistakes and Troubleshooting

  • Missing tab: Recipes must start with a tab, not spaces. Use cat -e -t makefile to see tabs as ^I.
  • Spaces in filenames: Escape them or use quotes. Better to avoid spaces in source file names.
  • Incorrect dependencies: If you forget a header in prerequisites, make won’t rebuild when that header changes. Use gcc -MM to generate dependency lists automatically.
  • Using make with directories: If a target is a directory, make may skip it. Use phony targets or .PHONY to avoid confusion.
  • Parallel builds: make -j4 runs four jobs simultaneously. Ensure your recipes don’t depend on each other in ways that break under parallelism.

Comparison with Other Build Tools

Make is the classic Unix tool, but modern alternatives exist:

  • CMake: Generates makefiles (or Ninja files) from a higher-level description. Handles cross-platform builds and library detection.
  • Ninja: Focuses on speed; designed for large projects with many files. Often used with CMake.
  • Meson: Python-based, fast, and user-friendly, with built-in support for many languages.
  • Bazel / Buck: Monorepo-scale build systems with advanced caching and remote execution.

Make is still widely used for small to medium projects, system administration, and as a generic task runner (e.g., for data pipelines). Its simplicity and ubiquity make it worth learning even if you later switch to a more specialized tool.

Advanced Techniques

  • Conditionals: Use ifeq, ifneq, ifdef to vary behavior by platform or configuration.
  • Functions: $(wildcard *.c) lists all .c files, $(patsubst %.c,%.o,$(SRCS)) converts a list.
  • Include files: include other.mk splits makefiles into modules.
  • Automatic dependency generation: Use gcc -MMD to create .d files that list header dependencies, then include them.

Example using wildcard and patsubst:

SRCS = $(wildcard *.c)
OBJS = $(patsubst %.c,%.o,$(SRCS))

program: $(OBJS)
	gcc -o $@ $^

Frequently Asked Questions

What is the difference between = and := in a makefile?

= is recursive expansion: the variable is expanded every time it’s used, which can lead to infinite loops if not careful. := is simple expansion: the value is expanded once when defined. Use := for variables that reference other variables to avoid surprises.

Why does make say “Nothing to be done for ‘all’”?

This means the target all is up to date—none of its prerequisites have changed. If you expected a rebuild, check that the prerequisites are correctly listed and that file timestamps are actually newer. Sometimes cleaning first (make clean) forces a full rebuild.

How do I pass arguments to a make target?

You can pass variables on the command line: make target VAR=value. Inside the makefile, use $(VAR). Alternatively, use environment variables, but command-line assignments override them.

Can I use make for non-C projects?

Yes. Make is a general task runner. You can use it to run tests, deploy websites, process data, or any sequence of shell commands. The same rules apply: targets, prerequisites, and recipes.

How do I debug a makefile?

Run make -n to print commands without executing them. make -d gives detailed debugging output about what make is deciding. make -p prints the database of variables and rules. These help identify why a target isn’t being rebuilt.

What is a “phony” target and why do I need it?

A phony target is not a real file. Without declaring it .PHONY, make might skip it if a file with that name exists. Declaring it ensures the recipe always runs. Common phony targets are clean, all, and install.

Related

Formula used
in t | Finish time | min | 50 | 0
in d | Distance | km | 10 | 0 | 100000
out pace | Average pace | min/km | t / d | 2
out speed | Average speed | km/h | d / (t / 60) | 2

Each line is one input (in, sel, list) or one result (out: label, unit, formula, decimals). How the calculators work

Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.

More cards in this drawer

The whole Numbers and Powers drawer →