手動編譯 gcc -c a.c; gcc -c b.c; gcc a.o b.o -o app 在檔案增多時極其繁瑣。Makefile 宣告依賴關係與構建規則,make 自動判斷哪些檔案需重新編譯,實現增量構建,大幅提升開發效率。
app: main.o a.o b.o
gcc -o app main.o a.o b.o
main.o: main.c
gcc -c main.c
a.o: a.c
gcc -c a.c
b.o: b.c
gcc -c b.c
clean:
rm *.o app
缺點:新增檔案需手動修改多處,維護成本高。
# 專案配置
TARGET = my_app
SRCS = $(wildcard *.c) # 自動掃描所有 .c
OBJS = $(SRCS:.c=.o) # 對應 .o 列表
CC = gcc
CFLAGS = -g -Wall -O2 -std=c11
INCLUDE = -I./include
LIBS = -lm -lpthread
# 預設目標
$(TARGET): $(OBJS)
$(CC) $^ -o $@ $(INCLUDE) $(LIBS)
# 隱含規則:%.o 依賴 %.c
%.o: %.c
$(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@
# 偽目標
.PHONY: clean
clean:
rm -f $(OBJS) $(TARGET)
| 符號/函數 | 含義 | 範例 |
|---|---|---|
$@ | 目標檔名 | app |
$^ | 所有依賴檔 | main.o a.o b.o |
$< | 第一個依賴檔 | main.c |
$(wildcard *.c) | 展開通配符 | a.c b.c main.c |
$(SRCS:.c=.o) | 字串替換 | a.o b.o main.o |
.PHONY | 宣告偽目標,避免與同名檔案衝突 | clean |
SRC_DIR = src
OBJ_DIR = obj
BIN_DIR = bin
SRCS = $(wildcard $(SRC_DIR)/*.c)
OBJS = $(patsubst $(SRC_DIR)/%.c,$(OBJ_DIR)/%.o,$(SRCS))
$(BIN_DIR)/$(TARGET): $(OBJS) | $(BIN_DIR)
$(CC) $^ -o $@ $(LIBS)
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(OBJ_DIR)
$(CC) $(CFLAGS) -c $< -o $@
$(OBJ_DIR) $(BIN_DIR):
mkdir -p $@
DEPFLAGS = -MMD -MP
CFLAGS += $(DEPFLAGS)
DEPS = $(OBJS:.o=.d)
-include $(DEPS) # 包含 .d 檔,自動追蹤 #include 依賴
ifeq ($(MODE),debug)
CFLAGS += -g -O0 -DDEBUG
else
CFLAGS += -O2 -DNDEBUG
endif
# 使用:make MODE=debug
CROSS_COMPILE ?= arm-linux-gnueabihf-
CC = $(CROSS_COMPILE)gcc
wildcard 包含不應編譯的測試檔 → 改用明確列表或過濾。make -n (乾跑)、make -p (列印內建規則)、make -d (詳細除錯)。一個優秀的 Makefile 應具備:自動發現源檔、增量編譯、依賴追蹤、多模式切換、目錄隔離、交叉編譯友善。本文模板涵蓋核心需求,可直接套用於中小型 C/C++ 專案。隨著專案增長,建議遷移至 CMake/Meson 等高階建構系統,但 Makefile 思維(依賴圖、增量構建)永遠通用。