Makefile 是 Unix/Linux 下自動化構建的核心工具。本文記錄從零學習到編寫大型項目 Makefile 的完整路徑,含最佳實踐模式、核心語法精華、實戰案例與避坑指南。
傳統遞歸 Make(每個子目錄一個 Makefile)弊端:依賴傳遞困難、並行構建失效、重複代碼多。現代最佳實踐:單一 Makefile + 模塊化包含。
# 頂層 Makefile
include common.mk
MODULES := asyn_frame rsa platform torrent_parser utility
# 包含目錄到編譯路徑
CFLAGS += $(patsubst %,-I%,$(MODULES))
CFLAGS += -I. -I./include
CFLAGS += -DLINUX -DNDEBUG -O2
# 自動收集所有 .c 文件
SRC := $(wildcard *.c)
# 包含各模塊的 module.mk(定義各模塊的 SRC/OBJ)
MODULES_MK := $(patsubst %,src/%/module.mk,$(MODULES))
include $(MODULES_MK)
# 目標文件
OBJ := $(patsubst %.c,%.o,$(SRC))
LIB := libcommon.a
# 構建規則
$(LIB): $(OBJ)
@echo 'Building library...'
$(AR) $(LIB) $(OBJ)
$(RM) $(OBJ)
@echo 'Library built.'
all: $(LIB)
# 依賴自動生成(側邊效應)
%.d: %.c
@set -e; $(RM) $@; \
$(CC) -M $(CFLAGS) $< > $@.$$$$; \
sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
$(RM) $@.$$$$
-include $(SRC:.c=.d)
.PHONY: clean all test
test:
@echo 'SRC:' $(SRC)
@echo 'OBJ:' $(OBJ)
@echo 'MODULES:' $(MODULES)
clean:
$(RM) $(LIB) $(OBJ)
find . -name "*.d" | xargs rm -f
# src/asyn_frame/module.mk
MODULE_SRC := $(wildcard src/asyn_frame/*.c)
SRC += $(MODULE_SRC)
| 語法 | 含義 | 示例 |
|---|---|---|
| $@ | 目標文件名 | app: main.o → $@=app |
| $< | 第一個依賴 | main.o: main.c → $<=main.c |
| $^ | 所有依賴(去重) | app: main.o util.o → $^=main.o util.o |
| $+ | 所有依賴(保留順序/重複) | |
| $? | 比目標新的依賴 | 增量編譯核心 |
| $(patsubst %,%,...) | 模式替換 | $(patsubst %.c,%.o,x.c y.c) → x.o y.o |
| $(wildcard ...) | 文件通配 | $(wildcard *.c) |
| $(filter ...) | 過濾單詞 | $(filter %.c, a.c b.h) → a.c |
| $(shell ...) | 執行 shell 命令 | $(shell date) |
| ifeq/ifneq | 條件判斷 | ifeq ($(OS),Windows_NT) |
| define/endef | 多行變量/宏 | define CMD ... endef |
CC = gcc # C編譯器
CXX = g++ # C++編譯器
AR = ar # 靜態庫工具
RM = rm -f # 刪除命令
CFLAGS = -Wall -O2 # C編譯選項
CXXFLAGS= -Wall -O2 # C++編譯選項
LDFLAGS = # 鏈接選項
LDLIBS = # 鏈接庫
= 遞歸展開,:= 即時展開,?= 未定義才賦值,+= 追加*.c 在規則中不展開,需 $(wildcard *.c)-MMD -MP 編譯選項自動生成 .d 文件,-include 包含跨平台項目建議直接用 CMake:
# CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(MyProject C)
add_library(common STATIC src/common.c src/utils.c)
target_include_directories(common PUBLIC include)
add_executable(app main.c)
target_link_libraries(app common)
CMake 生成 Makefile/VS Project/Xcode Project,一份配置多平台構建。
Makefile 是構建系統的「汇编語言」——底層、強大、靈活但易出錯。掌握非遞歸架構、自動依賴、模式規則、變量函數四大核心,能應對 90% 的構建需求。現代項目優先選 CMake,但理解 Makefile 原理是診斷構建故障、定制構建流程的前提。