Building with make

Typing four gcc commands after every change is slow, and it is easy to forget one. make reads a file called Makefile that lists how each file is built, then runs only the commands that are needed.

A Makefile

CC = gcc
CFLAGS = -Wall -Wextra -std=c11 -g

app: main.o stack.o
	$(CC) $(CFLAGS) -o app main.o stack.o

main.o: main.c stack.h
	$(CC) $(CFLAGS) -c main.c

stack.o: stack.c stack.h
	$(CC) $(CFLAGS) -c stack.c

clean:
	rm -f app *.o

.PHONY: clean

Each rule has three parts:

  • the target before the colon: the file this rule makes (main.o);
  • the prerequisites after it: the files it is made from (main.c stack.h);
  • the recipe on the next lines: the commands. Each recipe line must start with a Tab, not spaces. "missing separator" means spaces crept in.

CC and CFLAGS are variables; $(CFLAGS) is replaced by their value, so you change the flags in one place.

How make decides

Type make and it builds the first target, app. For each target it asks one question: is any prerequisite newer than the target? If yes (or the target does not exist yet), it runs the recipe. Before that, it does the same for each prerequisite, so the files are built in the right order.

#include <stdio.h>

/* A tiny make: rebuild a target only if something it needs is newer. */
struct file {
    const char *name;
    int time;   /* when it last changed: bigger means newer */
};

struct file main_c = {"main.c", 5}, stack_c = {"stack.c", 9}, stack_h = {"stack.h", 2};
struct file main_o = {"main.o", 6}, stack_o = {"stack.o", 7}, app = {"app", 8};
int now = 10;

void build(struct file *target, struct file *a, struct file *b) {
    if (a->time > target->time || b->time > target->time) {
        printf("rebuild %s\n", target->name);
        target->time = now++;
    } else {
        printf("%s is up to date\n", target->name);
    }
}

int main(void) {
    build(&main_o, &main_c, &stack_h);
    build(&stack_o, &stack_c, &stack_h);
    build(&app, &main_o, &stack_o);
    return 0;
}
Open in compiler

This program does make's job for three rules. Each file has a time of its last change. stack.c (time 9) was edited after stack.o (time 7) was built, so stack.o is rebuilt. That makes it newer than app, so app is linked again. main.o is newer than both its prerequisites, so it is left alone.

Useful commands

CommandWhat it does
makebuild the first target
make stack.obuild one target
make cleanrun the clean rule: delete the built files
make -nshow the commands without running them
make -j4run up to 4 recipes at the same time

.PHONY: clean tells make that clean is not a file, so it always runs. Bigger projects often use CMake, which writes the Makefile for you, but the idea is the same.

Quick check

1. When does make rebuild main.o?
2. make says 'missing separator'. What is wrong?
3. You edited stack.h. Which files does make rebuild?

Practice problems for this lesson

My notes

Write down what you want to remember from this lesson. Notes are saved on this device as you type, and appear on your dashboard.