Make & Makefiles
make
is a tool which you can use to build programs and run commands. It reads in Makefiles which are plaintext files which are commands which you want make to automate. Makefiles come in a variety of different formats and there is no ‘right’ way to write them. That being said, you are expected to know how to write the basic one, as outlined below.
Anatomy of a Makefile
# Comment
MACRO=VALUE
target: dependencies
commands
Comments
- You can make single line comments using hash-tags ‘#’.
- Comments can be placed at the beginning of a line or at the end of one
Macros
- These are aliases which will be replaced wherever they are referenced.
- Can be referenced using $(MACRO_NAME)
- ie. wrapping in parenthesis and prefixing with a dollar sign.
Targets
- These are the ‘tasks’ that you want to run.
- The first target is the one that is run by default when you call make without arguments.
- For our purposes, we want the first target to be called ‘all’.
- After the target name we need to put a colon ‘:’.
- Will only run if dependencies are missing or have been modified.
Dependancies
- Come after the colon on a target line
- Before executing the target’s command, make will check to see if the file is missing or has been modified.
- If the file does not exist, make will try to build it by calling a target of the same name.
- If the file exists, but has been modified, will signal to execute the target’s commands.
Commands
- Must be indented from targets using tabs.
- Do not need to relate to C compiling!
- Can do things like mkdir, rm, or run programs like valgrind
Example
Using the following files we want to create a binary called animals in the bin directory.
.
├── Makefile
├── README
├── bin
├── include
│ ├── beavers.h
│ ├── cats.h
│ ├── kittens.h
│ ├── snakes.h
│ └── lizards.h
└── src
├── animals.c
├── beavers.c
├── cats.c (calls functions included in kittens)
├── kittens.c
└── lizards.c (calls functions included in lizards)
Makefile 1: The Basic Makefile
In this example we just runs make in the same way that you would explicitly from the command prompt.
all: animals # This line gets called when you just call make
animals: # all calls this target in turn
gcc -std=c99 -Wall src/beavers.c src/kittens.c src/lizards.c src/cats.c \
src/animals.c -Iinclude -o bin/animals -lncurses # split lines with '\'
Makefile 2: Separating Compilation and Linking
In this example we separate the compilation and linking steps. This is more efficient because compilation only occurs as-needed. This example also uses macros and includes a ‘clean’ target which removes all binary and object files.
CC=gcc
CFLAGS=-std=c99 -Wall
LDFLAGS=-lncurses
INCLUDES=-Iinclude
SRCDIR=src
BINDIR=bin
all: animals
animals: animals.o
mkdir -p $(BINDIR) # creates the bin directory if it doesn't already exist
$(CC) animals.o beavers.o cats.o kittens.o lizards.o \
-o $(BINDIR)/animals $(LDFLAGS)
animals.o: $(SRCDIR)/animals.c beavers.o cats.o kittens.o lizards.o
$(CC) -c $(CFLAGS) $(SRCDIR)/animals.c $(INCLUDES)
beavers.o: $(SRCDIR)/beavers.c
$(CC) -c $(CFLAGS) $(SRCDIR)/beavers.c $(INCLUDES)
cats.o: $(SRCDIR)/cats.c kittens.o # since cats' functions call kittens'
$(CC) -c $(CFLAGS) $(SRCDIR)/cats.c $(INCLUDES)
kittens.o: $(SRCDIR)/kittens.c
$(CC) -c $(CFLAGS) $(SRCDIR)/kittens.c $(INCLUDES)
snakes.o: $(SRCDIR)/snakes.c lizards.o
$(CC) -c $(CFLAGS) $(SRCDIR)/snakes.c $(INCLUDES)
lizards.o:
$(CC) -c $(CFLAGS) $(SRCDIR)/lizards.c $(INCLUDES)
clean: # can be called using 'make clean'
rm -r -f bin *.o *.dump