Contents

MAKEFILE Introduction

Introduction to Makefiles

Makefiles are automation tools used to manage project builds and workflows. Originally created for software compilation, they can automate any task with file dependencies.

Key features:

  • Dependency tracking
  • Incremental builds
  • Portable automation
  • Parallel execution

Basic Syntax

Components

  1. target: File/action name
  2. prerequisites: Dependencies
  3. recipe: Commands to execute
target: prerequisite1 prerequisite2
<TAB>recipe-command1
<TAB>recipe-command2

Simple Example

hello: hello.c
    gcc hello.c -o hello

Variables

User-defined Variables

CC = gcc
CFLAGS = -Wall -O2

Automatic Variables

VariableMeaning
$@Target name
$<First prerequisite
$^All prerequisites

Variable Usage Example

app: main.o utils.o
    $(CC) $^ -o $@

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

Implicit Rules

Pattern Rules

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

Suffix Rules (Legacy)

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

Phony Targets

For targets not representing files:

.PHONY: clean

clean:
    rm -f *.o app

Key Properties

  1. Dependency Resolution: Builds targets only when prerequisites are newer
  2. Incremental Builds: Skips up-to-date targets
  3. Variable Expansion: Supports recursive and simple variables
  4. Parallel Execution: Use -j N flag for parallel jobs

Common Automations

Multi-stage Build

all: compile test package

compile: app

test: compile
    ./run-tests.sh

package: compile
    tar -czf app.tar.gz app

Automated Testing

test: CFLAGS += -coverage
test: app
    ./run-tests.sh
    gcov main.c

Documentation Generation

docs:
    doxygen Doxyfile
    pandoc README.md -o docs.pdf

Advanced Features

Conditional Logic

DEBUG ?= 0

ifeq ($(DEBUG),1)
    CFLAGS += -g -DDEBUG
endif

Functions

SOURCES = $(wildcard src/*.c)
OBJECTS = $(patsubst src/%.c,build/%.o,$(SOURCES))

Include Directives

include config.mk

Complete Example Project

CC = gcc
CFLAGS = -Wall -O2
SRC = src/main.c src/utils.c
OBJ = $(SRC:.c=.o)
EXEC = app

.PHONY: all clean

all: $(EXEC)

$(EXEC): $(OBJ)
    $(CC) $^ -o $@

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

clean:
    rm -f $(OBJ) $(EXEC)

Best Practices

  1. Always declare .PHONY targets
  2. Use variables for compiler/linker flags
  3. Structure targets from specific to generic
  4. Add help target with usage documentation
  5. Use -MMD flag for automatic dependency generation

Conclusion

Makefiles provide powerful automation through:

  • Declarative dependency management
  • Flexible pattern matching
  • Extensible variable system
  • Cross-platform compatibility