Makefile中的自动变量是在规则命令执行时,由“make” 自动填充的变量。它们代表了当前规则中的特定目标或依赖,让你无需重复写出冗长的文件名,使规则变得通用和简洁。
1. `$@` (Target)
代表当前规则的目标。这是最常用的变量。
例:
output/main.o: src/main.c src/common.h
gcc -c $< -o $@ # 相当于:gcc -c src/main.c -o output/main.o
2. `$<` (First Prerequisite)
代表第一个依赖。在从 `.c` 编译 `.o` 的规则中,它一般就是源文件。
例:
%.o: %.c
gcc -c $< -o $@ # 对于 `foo.o: foo.c`,$< 就是 `foo.c`
3. `$^` 与 `$+` (All Prerequisites)
– `$^`:所有依赖,去重。这是最安全、最常用的选择。
– `$+`:所有依赖,保留原顺序和重复项。用于某些特殊场景(如需要重复链接某个库)。
例:
假设:myapp: foo.o bar.o foo.o (依赖中 foo.o 出现两次)
myapp: foo.o bar.o foo.o
gcc $^ -o $@ # 链接命令实际为:gcc foo.o bar.o -o myapp (去重)
gcc $+ -o $@ # 链接命令实际为:gcc foo.o bar.o foo.o -o myapp (保留重复)
4. `$?` (Newer Prerequisites)
代表所有比目标文件更新的依赖文件。常用于只想重新编译发生变化的文件。
例:
lib.a: foo.o bar.o baz.o
ar rcs $@ $? # 只有当 foo.o, bar.o, baz.o 中有比 lib.a 新的时,才将其加入库
5. `$*` (Stem)
在模式规则 (`%`) 中,代表与模式匹配的部分(“词干”)。
例:
# 规则:将任意 .c 文件编译为对应的 .o 文件
%.o: %.c
gcc -c $< -o $@ # 对于 `test.o: test.c`,$* 就是 `test`
echo “正在编译词干为 $* 的文件” # 会输出:正在编译词干为 test 的文件
6. `$%` (Archive Member)
专门用于归档文件(静态库) 规则。
例:
# 规则:将对象文件 bar.o 加入归档库 foo.a
foo.a(bar.o): bar.o
ar rv $@ $% # 相当于:ar rv foo.a bar.o
最佳实践与常见陷阱
1.习惯使用自动变量:它们能让你的 `makefile` 更短、更通用、更易于维护。
2.为命令加上 `@` 前缀:在命令前加 `@`(如 `@echo “编译 $@”`)可以阻止 `make` 回显该命令本身,让输出更干净。
3.注意变量与普通文本的区分:`$` 是特殊符号。如果你需要在命令中使用真正的美元符号,需要用 `$$` 进行转义。
4.小心空格:自动变量代表的是一个完整的词(文件名),不会有前导或尾随空格。
5.`$<` 在显式规则和隐式规则中的区别:在显式规则中,`$<` 是第一个依赖;在隐含规则(如 `.c.o:`)中,它代表添加了后缀的依赖名。
一个综合示例
# 定义编译器和标志
CC = gcc
CFLAGS = -Wall -I./include
# 最终目标
TARGET = myapp
OBJS = main.o utils.o parser.o
# 链接:使用 $^ 代表所有对象文件
$(TARGET): $(OBJS)
$(CC) $^ -o $@
@echo “程序 $@ 构建成功!”
# 编译:模式规则,使用 $< 和 $@
%.o: src/%.c
$(CC) $(CFLAGS) -c $< -o $@
# 清理:使用 $(RM)是良好习惯,由于不同系统的 rm 命令可能不同
clean:
$(RM) $(OBJS) $(TARGET)
.PHONY: clean