Makefile 实用技巧
1. 字符串替换函数: patsubst
$(patsubst PATTERN,REPLACEMENT,TEXT) 函数用于在 TEXT 中以空格分隔的单词里查找匹配 PATTERN 的部分,并将其替换为 REPLACEMENT,最后返回替换后的单词列表。
2. 自动化变量
$@: 指代规则中的目标文件。$(@D): 指代目标文件的目录。$(@F): 指代目标文件的基本名称(不含目录)。$<: 指代第一个依赖文件。$?: 指代所有比目标文件新的依赖文件列表,以空格分隔。$^: 指代规则中所有依赖文件的列表,不含重复项,以空格分隔。$+: 类似于$^,也是所有依赖文件的集合,但会保留重复项。$(%F): 当目标文件是静态库时,代表库的一个成员名。例如,目标是libfoo.a(bar.o),则$(%F)是bar.o,$@是libfoo.a。如果目标不是静态库,则为空。
3. 特殊字符与命令
@: 在命令前加上@,可以阻止 Makefile 在执行时打印该命令本身,只显示命令的输出。-: 在命令前加上-,可以忽略该命令执行失败的错误,继续执行后续命令。$: 用于引用 Makefile 中定义的变量。$$: 用于引用 Shell 变量。include: 用于包含其他 Makefile 文件。
4. Shell 语句: set -e
在 Makefile 的 Shell 命令中,可以使用 set -e 来确保只要有任何一个命令执行失败,整个脚本就会立即终止。这有助于防止错误蔓延。需要注意的是,在 Makefile 中,set -e 的作用范围通常仅限于单行命令。可以通过分号将多个命令写在同一行,或者使用反斜杠 \ 进行行续写来扩展其管辖范围。
# 示例:
RUN_COMMANDS = \
@echo "Starting process..." ; \
set -e ; \
./script1.sh ; \
./script2.sh ; \
@echo "Process finished."
5. 动态依赖生成
可以通过编译器选项(如 GCC 的 -MMD 或 -MM)来自动生成源文件的依赖关系。然后,将这些依赖关系转换成 Makefile 的格式,并使用 -include 指令将它们包含进来。这样,当源文件发生变化时,只有受影响的文件及其依赖项会被重新编译,而不是全部重新编译。
CC = g++
CFLAGS = -Wall -O2 -std=c++11 -MMD -MP
SRC_DIR = src
OBJ_DIR = obj
BIN_DIR = bin
SRCS = $(wildcard $(SRC_DIR)/*.cpp)
OBJS = $(patsubst $(SRC_DIR)/%.cpp, $(OBJ_DIR)/%.o, $(SRCS))
DEPS = $(patsubst $(SRC_DIR)/%.cpp, $(OBJ_DIR)/%.d, $(SRCS))
TARGET = $(BIN_DIR)/my_program
all: $(TARGET)
$(TARGET): $(OBJS)
@mkdir -p $(@D)
$(CC) $^ -o $@ $(CFLAGS)
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.cpp
@mkdir -p $(@D)
$(CC) -c $< -o $@ $(CFLAGS)
# 自动包含依赖文件
-include $(DEPS)
clean:
@rm -rf $(OBJ_DIR)
@rm -rf $(BIN_DIR)