# Target: dependencies
# command
# command
all: program # Default target (runs when you type 'make')
program: main.o utils.o # Target depends on object files
gcc -o program main.o utils.o # Link object files
main.o: main.c # Compile main.c to main.o
gcc -c main.c
utils.o: utils.c utils.h # Compile utils.c (depends on header too)
gcc -c utils.c
clean: # Remove built files
rm -f *.o program
# Define variables for reusability
CC = gcc # Compiler
CFLAGS = -Wall -g # Compiler flags
LDFLAGS = -lm # Linker flags
SRC = main.c utils.c # Source files
OBJ = $(SRC:.c=.o) # Replace .c with .o
# Use variables in targets
program: $(OBJ)
$(CC) $(CFLAGS) -o program $(OBJ) $(LDFLAGS)
%.o: %.c # Pattern rule: compile any .c to .o
$(CC) $(CFLAGS) -c $<
# Declare targets that don't create files
.PHONY: all clean test install
all: build # Default target
clean: # Clean build artifacts
rm -rf build/ *.o
test: # Run tests
./run_tests.sh
install: # Install the program
cp program /usr/local/bin/
# Special variables make provides
build: src/main.c
gcc $< -o $@ # $< = first dependency, $@ = target name
%.o: %.c %.h
gcc -c $< # $< = first prerequisite (the .c file)
libs: lib1.o lib2.o lib3.o
ar rcs libmylib.a $^ # $^ = all prerequisites
# Conditional compilation
DEBUG ?= 0 # Default to 0 if not set
ifeq ($(DEBUG), 1)
CFLAGS += -g -DDEBUG # Add debug flags
else
CFLAGS += -O2 # Add optimization
endif
# Check if file exists
ifneq ($(wildcard config.mk),)
include config.mk # Include if exists
endif
make # Build default target (usually 'all')
make clean # Run the 'clean' target
make install # Run the 'install' target
make -j4 # Build with 4 parallel jobs
make -n # Dry run (show commands without executing)
make -B # Force rebuild all targets
make TARGET=value # Pass variable to makefile
# Built-in functions
FILES = $(wildcard src/*.c) # Find all .c files in src/
OBJS = $(patsubst %.c,%.o,$(FILES)) # Replace .c with .o
DIRS = $(shell find . -type d) # Run shell command
# String manipulation
UPPER = $(shell echo $(VAR) | tr a-z A-Z) # Convert to uppercase
BASENAME = $(notdir $(FILES)) # Get filename without path
# Organize larger projects
SRC_DIR = src
BUILD_DIR = build
INC_DIR = include
SOURCES = $(wildcard $(SRC_DIR)/*.c)
OBJECTS = $(patsubst $(SRC_DIR)/%.c,$(BUILD_DIR)/%.o,$(SOURCES))
all: $(BUILD_DIR) program
$(BUILD_DIR): # Create build directory
mkdir -p $(BUILD_DIR)
program: $(OBJECTS)
$(CC) -o $@ $^ -I$(INC_DIR)
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c
$(CC) $(CFLAGS) -c $< -o $@ -I$(INC_DIR)
# Auto-generate header dependencies
DEPS = $(OBJECTS:.o=.d) # .d files track header dependencies
-include $(DEPS) # Include if they exist
%.o: %.c
$(CC) $(CFLAGS) -MMD -MP -c $< -o $@ # -MMD generates .d file