Makefile学习(二)- 语法(变量、伪目标)
一、简介
上一章节,我们介绍了Makefile的基本规则,如果不了解的可以先看这篇文章:https://blog.csdn.net/qq_26226375/article/details/151828250?sharetype=blogdetail&sharerId=151828250&sharerefer=PC&sharesource=qq_26226375&spm=1011.2480.3001.8118
Makefile语法是一套用于定义编译规则的特殊格式,核心是通过“规则”描述文件依赖关系和编译命令。下面我们来详细介绍下。
二、语法
%(通配符)
%可以匹配任意字符串,用于批量定义规则。例如上一章节的Makefile文件。
a.o: a.cgcc -o a.o -c a.c
利用通配符,可以修改为
%.o: %.cgcc -o %.o -c %.c
%.o表示所有的.o文件,%.c表示所有的.c文件。
但是这样写歧义会非常的大,make执行是会直接报错。
自动变量
简化命令中的文件引用,常用如下指令
$@
:当前规则的目标文件$<
:当前规则的第一个依赖文件$^
:当前规则的所有依赖文件(去重)$+
:当前规则的所有依赖文件(保留重复)
加入自动变量后,可以优化如下代码
test: a.o b.ogcc -o test a.o b.oa.o: a.cgcc -o a.o -c a.cb.o: b.cgcc -o b.o -c b.c
加入变量
test: a.o b.ogcc -o $@ $^%.o: %.cgcc -o $@ -c $<
$@为当前规则目标文件,则gcc -o test a.o b.o可以缩减为gcc -o $@ a.o b.o。
$^为当前规则依赖的所有文件,则gcc -o $@ a.o b.o可以缩减为gcc -o $@ $^
$<为当前规则的第一个依赖文件,则gcc -o b.o -c b.c可以缩减为gcc -o b.o -c $<
变量
变量用于简化重复内容,定义和引用方式。常见的表达方式有如下:
变量名 = 值 # 递归展开(可能有副作用)
变量名 := 值 # 简单展开(推荐)
变量名 ?= 值 # 若变量未定义则赋值
变量名 += 值 # 追加内容
$(变量名) # 引用变量
:=(立即赋值)
变量在定义时立即展开,后续修改依赖的变量不会影响当前变量。
A := hello
B := $(A) world # 定义时立即展开,B 的值固定为 "hello world"
A := hi # 后续修改 A 不影响 Btest:@echo "B = $(B)"
=(递归赋值)
变量在定义时不会展开,在使用时展开,后续变量修改会影响当前变量
SRC = a.c
OBJ = $(SRC:.c=.o) # 用 = 定义,依赖 SRC# 后续修改 SRC
SRC += b.ctest:@echo "OBJ = $(OBJ)"
?=(条件赋值)
仅当变量未被定义过时才赋值,若变量已经存在,则不改变其值。
SRC = a.c
SRC ?= b.cSRC2 = a.c
SRC2 = b.ctest:@echo "SRC = $(SRC), SRC2 = $(SRC2)"
+=(追加赋值)
向已定义的变量追加内容,自动在新内容前添加空格(分隔多个值)
SRC := a.c b.c
SRC += c.c test:@echo "SRC = $(SRC)"
按照这个,如下代码可以进一步优化。
test: a.o b.ogcc -o $@ $^%.o: %.cgcc -o $@ -c $<
引入变量
CC := gcc # 指定编译器
SRC := a.c b.c # 指定源文件
OBJ := $(SRC:.c=.o) # 指定目标文件test: $(OBJ)$(CC) -o $@ $^ # 也可以写为 $(CC) -o $@ $(OBJ)%.o: %.c$(CC) -o $@ -c $<
这里$(SRC:.c=.o)是一种字符串替换语法,用于将变量SRC中所有以.c结尾的文件名替换为.o结尾的文件名,从而自动生成目标文件列表。
伪目标
用于定义不生成文件的操作,避免与同名文件冲突。
例如,在文件夹中存在clean文件,此时执行make clean将会报错。
修改代码,声明伪目标
CC := gcc # 指定编译器
SRC := a.c b.c # 指定源文件
OBJ := $(SRC:.c=.o) # 指定目标文件
.PHONY: cleantest: $(OBJ)$(CC) -o $@ $^ # 也可以写为 $(CC) -o $@ $(OBJ)%.o: %.c$(CC) -o $@ -c $<clean:rm -f $(OBJ) test
all
这里需要介绍一个特殊的伪目标“all”
在Makefile中,all是一个特殊的伪目标,通常用于定义默认执行的任务,即当在命令行直接输入make而不指定目标时,make会执行all所定义的操作。例如下面的代码:
CC := gcc # 指定编译器
SRC := a.c b.c # 指定源文件
OBJ := $(SRC:.c=.o) # 指定目标文件
.PHONY: clean allall: test cleanecho "所有目标构建完成"test: $(OBJ)$(CC) -o $@ $^%.o: %.c$(CC) -o $@ -c $<clean:rm -f $(OBJ) test
运行make等价于make all,会依次构建test和clean,最后输出提示信息。若不定义all,make会默认执行第一个目标test,而忽略clean。
可以看到运行Make后,程序执行了make test 和make clean,导致最终并没有生成.o和test程序。
而all后面跟的目标文件的顺序也决定了程序运行的顺序,例如将all test clean更换为all clean test,在执行make
CC := gcc # 指定编译器
SRC := a.c b.c # 指定源文件
OBJ := $(SRC:.c=.o) # 指定目标文件
.PHONY: clean allall: clean test echo "所有目标构建完成"test: $(OBJ)$(CC) -o $@ $^ %.o: %.c$(CC) -o $@ -c $<clean:rm -f $(OBJ) test
可以看到,执行make后,先执行了make clean,再执行了make test。
@
运行make时,系统会打印执行的命令,如果不希望显示这些命令,可以在命令前加上@。
CC := gcc # 指定编译器
SRC := a.c b.c # 指定源文件
OBJ := $(SRC:.c=.o) # 指定目标文件
.PHONY: allall: clean test @echo "所有目标构建完成"test: $(OBJ)@$(CC) -o $@ $^ %.o: %.c@$(CC) -o $@ -c $<clean:@rm -f $(OBJ) test
可以看到,所有执行的命令都没有在窗口上回显出来。