Makefile 里面是由一系列的规则组成的,这些规则格式如下:
目标…... : 依赖文件集合……
命令 1
命令 2
……
main : main.o input.o calcu.o
gcc -o main main.o input.o calcu.o
这个规则的目标是main,main的依赖文件有main.o input.o calcu.o,第二行代表生成main这个目标所需要执行的命令
命令列表中的每条命令必须以 TAB 键开始,不能使用空格!
main : main.o input.o calcu.o
gcc -o main main.o input.o calcu.o
对上述makefile,在这之中main.o input.o calcu.o写了两遍,那我们有没有什么办法可以简化他呢?
我们可以使用makefile中的变量,可以用一个变量指代这些文件。
在我们要用到变量时,则需$(变量名)
OBJS = main.o input.o calcu.o
main : $(OBJS)
gcc -o main $(OBJS)
使用“=”在给变量的赋值的时候,不一定要用已经定义好的值,也可以使用后面定义的值
a=5
final = $(a)
a=6
最终final的结果为6。
不会使用后面定义的变量
a=5
final := $(a)
a=6
final的结果为5
final ?= a
如果变量 final 前面没有被赋值,那么此变量就是“a”,如果前面已经赋过值了,那么就使用前面赋的值。
CFLAGS= -o
CFLAGS+= -Wall -c
Makefile 中的变量是字符串,有时候我们需要给前面已经定义好的变量添加一些字符串进去,此时就要使用到符号“+=”
main.o: main.c
gcc -c main.c
input.o: input.c
gcc -c input.c
calcu.o: calcu.c
gcc -c calcu.c
每一个C文件都要编写一个对应的规则,这样文件一多就会使得makefile异常冗长。为此,我们可以使用 Makefile中的模式规则,通过模式规则我们就可以使用一条规则来将所有的.c 文件编译为对应的.o 文件。
模式规则中,至少在规则的目标定定义中要包涵“%”,否则就是一般规则,目标中的“%”表示对文件名的匹配,“%”表示长度任意的非空字符串,比如“%.c”就是所有的以.c 结尾的文件,类似与通配符,a.%.c 就表示以 a.开头,以.c 结束的所有文件。
自动化变量就是这种变量会把模式中所定义的一系列的文件自动的挨个取出,直至所有的符合模式的文件都取完,自动化变量只应该出现在规则的命令中
常用的自动化变量
自动化变量 | 描述 |
---|---|
$@ | 规则中的目标集合,在模式规则中,如果有多个目标的话,“$@”表示匹配模式中定义的目标集合。 |
$% | 当目标是函数库的时候表示规则中的目标成员名,如果目标不是函数库文件,那么其值为空。 |
$< | 依赖文件集合中的第一个文件,如果依赖文件是以模式(即“%”)定义的,那么“$<”就是符合模式的一系列的文件集合。 |
$? | 所有比目标新的依赖目标集合,以空格分开。 |
$^ | 所有依赖文件的集合,使用空格分开,如果在依赖文件中有多个重复的文件,“$^”会去除重复的依赖文件,值保留一份。 |
$+ | 和“$^”类似,但是当依赖文件存在重复的话不会去除重复的依赖文件。 |
$* | 这个变量表示目标模式中"%"及其之前的部分,如果目标是test/a.test.c,目标模式为 a.%.c,那么“$*”就是 test/a.test。 |
Makefile 有一种特殊的目标——伪目标,一般的目标名都是要生成的文件,而伪目标不代表真正的目标名,在执行 make 命令的时候通过指定这个伪目标来执行其所在规则的定义的命令。
使用伪目标的主要是为了避免 Makefile 中定义的只执行命令的目标和工作目录下的实际文件出现名字冲突,有时候我们需要编写一个规则用来执行一些命令,但是这个规则不是用来创建文件的。
例如以下代码就是用来清理文件的
clean:
rm *.o main -rf
需要执行rm *.o main -rf
,只需在terminal中输入make clean,即可执行clean下的命令。
这个命令代表了清除生成的所有.o结尾的文件和最后的可执行文件main。
如果我们在工作目录下创建一个名为“clean”的文件,那就不一样了,当执行“make clean”的时候,规则因为没有依赖文件,所以目标被认为是最新的,因此后面的 rm 命令也就不会行,我们预先设想的清理工程的功能也就无法完成。为了避免这个问题,我们可以将 clean 声明为伪目标,声明方式如下:
.PHONY : clean
声明 clean 为伪目标以后不管当前目录下是否存在名为“clean”的文件,输入“make clean”的话规则后面的 rm 命令都会执行。
.PHONY:clean
clean:
rm *.o myFFT -rf
Makefile 支持条件判断,语法有两种如下:
<条件关键字>
<条件为真时执行的语句>
endif
以及
<条件关键字>
<条件为真时执行的语句>
else
<条件为假时执行的语句>
endif
其中条件关键字有 4 个:ifeq、ifneq、ifdef 和 ifndef,这四个关键字其实分为两对、ifeq 与 ifneq、ifdef 与 ifndef,先来看一下 ifeq 和 ifneq,ifeq 用来判断是否相等,ifneq就是判断是否不相等
ifeq 用法如下:
ifeq (<参数 1>, <参数 2>)
ifeq ‘<参数 1 >’,‘ <参数 2>’
ifeq “<参数 1>”, “<参数 2>”
ifeq “<参数 1>”, ‘<参数 2>’
ifeq ‘<参数 1>’, “<参数 2>”
上述用法中都是用来比较“参数 1”和“参数 2”是否相同,如果相同则为真,“参数 1”和“参数 2”可以为函数返回值。ifneq 的用法类似,只不过 ifneq 是用来了比较“参数 1”和“参数 2”是否不相等,如果不相等的话就为真。
ifdef 和 ifndef 的用法如下:
ifndef <变量名>
如果“变量名”的值非空,那么表示表达式为真,否则表达式为假。“变量名”同样可以是一个函数的返回值。ifndef 用法类似,但是含义与 ifdef 相反。
函数 subst 用来完成字符串替换,调用形式如下:
$(subst ,,)
此函数的功能是将字符串 中的内容替换为,函数返回被替换以后的字符串
$(subst abc,ABC,my name is abc)
把字符串“my name is abc”中的“abc”替换为“ABC”,替换完成以后的字符串为“myname is ABC”。
函数 patsubst 用来完成模式字符串替换,使用方法如下:
$(patsubst ,,)
此函数查找字符串 中的单词是否符合模式,如果匹配就用来替换掉,可以使用包括通配符“%”,表示任意长度的字符串,函数返回值就是替换后的字符串。如果中也包涵“%”,那么中的“%”将是中的那个 “%” 所代表的字符串
$(patsubst %.c,%.o,a.c b.c c.c)
将字符串“a.c b.c c.c”中的所有符合“%.c”的字符串,替换为“%.o”,替换完成以后的字符串为“a.o b.o c.o”。
函数 dir 用来获取目录,使用方法如下:
$(dir )
此函数用来从文件名序列中提取出目录部分,返回值是文件名序列的目录部分
$(dir )
提取文件“/src/a.c”的目录部分,也就是“/src”。
函数 notdir 看名字就是知道去除文件中的目录部分,也就是提取文件名
$(notdir )
此函数用与从文件名序列中提取出文件名非目录部分
$(notdir )
提取文件“/src/a.c”中的非目录部分,也就是文件名“a.c”。
foreach 函数用来完成循环
$(foreach , ,)
此函数的意思就是把参数中的单词逐一取出来放到参数中,然后再执行 所包含的表达式。每次 都会返回一个字符串,循环的过程中, 中所包含的每个字符串会以空格隔开,最后当整个循环结束时, 所返回的每个字符串所组成的整个字符串将会是函数 foreach 函数的返回值。
通配符“%”只能用在规则中,只有在规则中它才会展开,如果在变量定义和函数使用时,通配符不会自动展开,这个时候就要用到函数 wildcard
$(wildcard PATTERN…)
$(wildcard *.c)
上面的代码是用来获取当前目录下所有的.c 文件,类似“%”。
此文章结合了正点原子的手册的部分资料,跟我一起写makefile以及网上的一些其他参考资料