Skip to content

A Real C Build

Scope: the canonical multi-file example, end to end — every command below was actually run.

The program

Three small files. greet.h declares a function, greet.c defines it, main.c calls it:

/* greet.h */
#ifndef GREET_H
#define GREET_H
void greet(const char *name);
#endif
/* greet.c */
#include <stdio.h>
#include "greet.h"

void greet(const char *name)
{
    printf("Hello, %s! Built with Make.\n", name);
}
/* main.c */
#include "greet.h"

int main(void)
{
    greet("Mere Mortal");
    return 0;
}

The Makefile

CC = gcc
CFLAGS = -Wall -Wextra -g

.PHONY: all clean
all: hello

hello: main.o greet.o
    $(CC) $(CFLAGS) -o $@ $^

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

clean:
    rm -f hello *.o

Every idea from the previous chapters is here: variables for the compiler and flags, automatic variables ($@, $<, $^), one pattern rule for both object files, and .PHONY for the task targets.

The build

$ make
gcc -Wall -Wextra -g -c main.c -o main.o
gcc -Wall -Wextra -g -c greet.c -o greet.o
gcc -Wall -Wextra -g -o hello main.o greet.o
$ ./hello
Hello, Mere Mortal! Built with Make.

Run make again and nothing happens — make: 'hello' is up to date. Edit the header, and Make rebuilds both object files and relinks, because the pattern rule lists greet.h as a prerequisite:

$ touch greet.h
$ make
gcc -Wall -Wextra -g -c main.c -o main.o
gcc -Wall -Wextra -g -c greet.c -o greet.o
gcc -Wall -Wextra -g -o hello main.o greet.o

Why the header is listed

The pattern rule says %.o: %.c greet.h. Both object files depend on the header, so a header change rebuilds everything that includes it. Forget the header, and Make happily links stale object files against your new declarations — the classic "but I changed it!" bug. (Large projects generate these header dependencies automatically with gcc -MM; look it up when your project outgrows a hand-written list.)

Check: make clean && make rebuilds from scratch. If any step fails, the error names the file and line — C errors are refreshingly specific compared to Makefile errors.