使用变量

廖雪峰
资深软件开发工程师,业余马拉松选手。

当我们在Makefile中重复写很多文件名时,一来容易写错,二来如果要改名,要全部替换,费时费力。

编程语言使用变量(Variable)来解决反复引用的问题,类似的,在Makefile中,也可以使用变量来解决重复问题。

以上一节的Makefile为例:

world.out: hello.o main.o
	cc -o world.out hello.o main.o

clean:
	rm -f *.o world.out

编译的最终文件world.out重复出现了3次,因此,完全可以定义一个变量来替换它:

TARGET = world.out

$(TARGET): hello.o main.o
	cc -o $(TARGET) hello.o main.o

clean:
	rm -f *.o $(TARGET)

变量定义用变量名 = 值或者变量名 := 值,通常变量名全大写。引用变量用$(变量名),非常简单。

注意到hello.o main.o这个“列表”也重复了,我们也可以用变量来替换:

OBJS = hello.o main.o
TARGET = world.out

$(TARGET): $(OBJS)
	cc -o $(TARGET) $(OBJS)

clean:
	rm -f *.o $(TARGET)

如果有一种方式能让make自动生成hello.o main.o这个“列表”,就更好了。注意到每个.o文件是由对应的.c文件编译产生的,因此,可以让make先获取.c文件列表,再替换,得到.o文件列表:

# $(wildcard *.c) 列出当前目录下的所有 .c 文件: hello.c main.c
# 用函数 patsubst 进行模式替换得到: hello.o main.o
OBJS = $(patsubst %.c,%.o,$(wildcard *.c))
TARGET = world.out

$(TARGET): $(OBJS)
	cc -o $(TARGET) $(OBJS)

clean:
	rm -f *.o $(TARGET)

这样,我们每添加一个.c文件,不需要修改Makefile,变量OBJS会自动更新。

思考:为什么我们不能直接定义OBJS = $(wildcard *.o)make列出所有.o文件?

内置变量

我们还可以用变量$(CC)替换命令cc

$(TARGET): $(OBJS)
	$(CC) -o $(TARGET) $(OBJS)

没有定义变量CC也可以引用它,因为它是make的内置变量(Builtin Variables),表示C编译器的名字,默认值是cc,我们也可以修改它,例如使用交叉编译时,指定编译器:

CC = riscv64-linux-gnu-gcc
...

自动变量

Makefile中,经常可以看到$@$<这样的变量,这种变量称为自动变量(Automatic Variable),它们在一个规则中自动指向某个值。

例如,$@表示目标文件,$^表示所有依赖文件,因此,我们可以这么写:

world.out: hello.o main.o
	cc -o $@ $^

在没有歧义时可以写$@,也可以写$(@),有歧义时必须用括号,例如$(@D)

为了更好地调试,我们还可以把变量打印出来:

world.out: hello.o main.o
	@echo '$$@ = $@' # 变量 $@ 表示target
	@echo '$$< = $<' # 变量 $< 表示第一个依赖项
	@echo '$$^ = $^' # 变量 $^ 表示所有依赖项
	cc -o $@ $^

执行结果输出如下:

$@ = world.out
$< = hello.o
$^ = hello.o main.o
cc -o world.out hello.o main.o

参考源码

可以从GitHub下载源码。

GitHub

小结

使用变量可以让Makefile更加容易维护。

查看官方手册:



Comments

Loading comments...