Makefile 是自動化建構的核心,掌握它等於掌握了專案編譯的完整控制權。本文以 GNU Make 為標準,從基本規則語法、變數、模式規則、自動推導、偽目標、函式、條件判斷、模組化設計,逐步建構適用於中大型 C/C++ 專案的標準化 Makefile 模板。
CC = gcc
CFLAGS = -Wall -O2
TARGET = myapp
SRCS = main.c utils.c
OBJS = $(SRCS:.c=.o)
$(TARGET) : $(OBJS)
$(CC) -o $@ $^
%.o : %.c
$(CC) $(CFLAGS) -c $< -o $@
clean :
rm -f $(TARGET) $(OBJS)
執行 make → 自動編譯連結;make clean → 清理。
DEPFLAGS = -MMD -MP
CFLAGS += $(DEPFLAGS)
-include $(OBJS:.o=.d) # 包含 .d 檔,- 忽略不存在錯誤
編譯時自動產生 .d 檔記錄 #include 相依,修改 .h 精確觸發重編。
project/ ├── Makefile ├── src/ │ ├── module1/ │ │ ├── mod1.c │ │ ├── mod1.h │ │ └── Makefile.module │ └── module2/ │ ├── mod2.c │ ├── mod2.h │ └── Makefile.module └── include/ └── common.h
主 Makefile 包含子模組:
include src/module1/Makefile.module
include src/module2/Makefile.module
各 Makefile.module 定義區域變數 MOD1_SRCS、MOD1_OBJS,主檔彙總至 OBJS。
BUILD ?= release # make BUILD=debug
ifeq ($(BUILD),debug)
CFLAGS += -g -O0 -DDEBUG
else
CFLAGS += -O2 -DNDEBUG
endif
CROSS_COMPILE ?= # make CROSS_COMPILE=arm-linux-gnueabihf-
CC = $(CROSS_COMPILE)gcc
| 函式 | 用途 | 範例 |
|---|---|---|
wildcard | 擴展檔案通配符 | SRCS = $(wildcard src/*.c) |
patsubst | 模式替換 | OBJS = $(patsubst %.c,%.o,$(SRCS)) |
shell | 執行 Shell 命令 | VERSION = $(shell git describe --tags) |
foreach | 迴圈展開 | $(foreach d,$(DIRS),$(wildcard $(d)/*.c)) |
if/filter | 條件過濾 | $(if $(filter debug,$(MAKECMDGOALS)),-g,) |
.PHONY: all clean install uninstall test distclean
all: $(TARGET)
install: $(TARGET)
install -m 755 $(TARGET) $(DESTDIR)/usr/bin/
test: $(TEST_TARGET)
./$(TEST_TARGET)
distclean: clean
rm -f *.d *.log config.cache
# === 專案設定 ===
PROJECT = myproject
VERSION = 1.0.0
BUILD ?= release
CROSS_COMPILE ?=
# === 工具鏈 ===
CC = $(CROSS_COMPILE)gcc
CXX = $(CROSS_COMPILE)g++
AR = $(CROSS_COMPILE)ar
STRIP = $(CROSS_COMPILE)strip
# === 編譯選項 ===
CFLAGS = -Wall -Wextra -std=c11 -fPIC
CXXFLAGS = -Wall -Wextra -std=c++17 -fPIC
LDFLAGS = -shared
DEPFLAGS = -MMD -MP
ifeq ($(BUILD),debug)
CFLAGS += -g -O0 -DDEBUG
CXXFLAGS += -g -O0 -DDEBUG
else
CFLAGS += -O2 -DNDEBUG
CXXFLAGS += -O2 -DNDEBUG
endif
# === 目錄 ===
SRCDIR = src
OBJDIR = build/$(BUILD)
BINDIR = bin/$(BUILD)
# === 自動搜尋來源檔 ===
SRCS = $(shell find $(SRCDIR) -name '*.c' -o -name '*.cpp')
OBJS = $(patsubst $(SRCDIR)/%,$(OBJDIR)/%,$(SRCS:.c=.o))
OBJS := $(OBJS:.cpp=.o)
DEPS = $(OBJS:.o=.d)
# === 目標 ===
TARGET = $(BINDIR)/$(PROJECT)
# === 規則 ===
all: $(TARGET)
$(TARGET): $(OBJS) | $(BINDIR)
$(CXX) -o $@ $^ $(LDFLAGS)
$(OBJDIR)/%.o: $(SRCDIR)/%.c | $(OBJDIR)
$(CC) $(CFLAGS) $(DEPFLAGS) -c $< -o $@
$(OBJDIR)/%.o: $(SRCDIR)/%.cpp | $(OBJDIR)
$(CXX) $(CXXFLAGS) $(DEPFLAGS) -c $< -o $@
$(OBJDIR) $(BINDIR):
mkdir -p $@
-include $(DEPS)
.PHONY: all clean install test
clean:
rm -rf build bin
install: all
install -D -m 755 $(TARGET) $(DESTDIR)/usr/bin/$(PROJECT)
$(VAR) 或 ${VAR},單字元可省略括號 $@ $<。.PHONY 避免與檔案同名衝突。-include 載入相依檔,忽略首次建置不存在情況。| $(DIR) (順序依賴),避免重複觸發。CROSS_COMPILE 以連字號結尾 (如 arm-linux-)。掌握此模板,新專案複製調整變數區即可投入生產,建構過程透明、可控、可擴展。