目录
Makefile的规则
Makefile的语法
通配符
假想目标
变量
Makefile的函数
foreach函数
filter和filter-out
wildcard
patsubst
修改头文件无法make解决
CFLAGS
Makefile的规则
当我们在使用gcc进行编译链接的时候,我们需要手动在shell窗口键入命令。比如说我们目前有两个c文件,想要对它们进行编译生成test可执行文件
gcc -o test a.c b.c
通过执行如上代码,我们可以生成一个test可执行文件
那么问题就来了,如果存在几千个文件同时需要进行编译链接的话,难道也需要这样一个个键入命令生成吗,所以,我们在嵌入式系统中,使用的是makefile进行批处理
同时,如上代码还有一个缺点,那就是如果修改了a.c文件,那么执行如上命令的时候,即使b.c文件没有动过,也会被重新编译一次,如果文件很多,这是很消耗时间的
Makefile的最核心的规则如下
基于这个核心规则,把如下的命令转化为makefile
gcc -o test a.c b.c
#test可执行文件依赖a.o b.o test : a.o b.o gcc -o test a.o b.o #a.o依赖a.c a.o : a.c #编译生成a.o但是不链接 gcc -c -o a.o a.c #b.o依赖b.c b.o : b.c #编译生成b.o但是不链接 gcc -c -o b.o b.c
新建一个Makefile文件,然后键入如上内容,然后直接输入命令make就可以完成编译和链接了
Makefile的语法
通配符
原先通过a.o和b.o链接生成test可执行文件的makefile过于的繁琐,因为每有一个文件,都要在下方新添加一个编译规则
test : a.o b.o gcc -o $@ $^ %.o : %.c gcc -c -o $@ $<
假想目标
当我们需要通过makefile一键清除生成的文件和目标的时候,可以添加一个clean
test : a.o b.o gcc -o $@ $^ %.o : %.c gcc -c -o $@ $< clean: rm *.o test
删除test可执行文件以及所有.o后缀的文件
由此可知,如果make没有带目标名,那么它是生成规则的第一个目标,比如说只键入make,那么它就是执行第一个目标,生成test文件,如果是make clean,那么就是执行clean处的操作
由于makefile执行的条件是,目标文件不存在或者依赖文件比目标文件新,如果存在如下情况,那么make clean就无法正常执行
创建了一个名为clean的文件,makefile会判断clean文件已经存在,并且没有依赖比目标文件新,则无法正常执行,此时如果有这种情况那么我们就需要使用假想目标
假想目标(有时也称为伪目标或标签)不代表一个真正的文件名。它更多的是作为一个目标集合或动作,用于在执行make命令时指定并执行其所在规则定义的命令。假想目标不生成文件,只是作为几个命令组成特殊规则的名称。可以理解为,假想目标只执行命令,不是用于生成文件
test : a.o b.o gcc -o $@ $^ %.o : %.c gcc -c -o $@ $< clean: rm *.o test .PHONY : clean
添加最后一行后,便可以正常执行make clean,即使存在clean文件
变量
:= 为即时变量,假如 A:= C ,那么A就是执行到这一句的时候C的值,后续不管C在怎么改变,A都不会因为C的改变而改变。它可以确保变量的值在赋值时就是确定的,并且在整个
Makefile
中保持不变,除非被重新赋值。= 为延时变量,假如 A=C,那么直到结束,C不管改变多少次,C最终是多少A就是多少
?= 延时变量如果是第1次定义才起效,如果在前面该变量已定义则忽略这句
+= 附加
Makefile的函数
foreach函数
$(foreach var,list,text)
类似与for循环,把list中的字符串通过空格划分,然后执行text中的表达式,然后赋值给var,直到list结束
输出结果为a.o b.o c.o,分出的第一个字符为a,执行增加.o的操作,赋值给f,以此叠加直到list也就是A中无法在通过空格划分出字符
filter和filter-out
#在text中取出符合pattern的值 $(filter pattern...,text) #在text中取出不符合pattern的值 $(filter-out pattern...,text)
示例如下
文件夹中存在如下不同格式的文件,如何筛选出只包含.c为后缀的文件呢
通过执行shell命令ls列举出所有文件,然后赋值给C,然后使用filter函数,在C变量中,把满足以c为后缀的变量赋值给D,就可以获得只以c为后缀的文件了
如果我们在这里使用的是filter-out函数,那就是排除以.c为后缀的文件,那么获得的就是
D = a.d b.d c.d d.d了
wildcard
此函数可以从该文件夹目录中取出指定的文件的格式
#pattern定义了文件名格式 $(wildcard pattern)
执行的结果如下
patsubst
$(patsubst pattern,replacement,text)
把text中的所有满足pattern的内容替换为replacement的内容,其他的内容不会进行改变,除了满足的变量进行操作外,不满足的也不会被剔除
执行的结果如下
修改头文件无法make解决
当我们在使用makefile的时候,可能会存在如下的问题
假设我有如下三个文件,a.c b.c c.c ,c.c文件包含了c.h这个头文件,当我们make生成了可执行文件之后,我们修改头文件,然后打算重新编译的时候会被告知无法重新进行编译,因为,我们的makefile中没有文件依赖h文件生成
那么我们如何解决这个问题呢,这里就需要用到依赖文件,通过如下makefile,即使只是头文件被修改了,重新进行make编译也是可以编译的,因为这里我们使用了依赖文件,头文件被更改了,依赖文件也会被更改
OBJ = a.o b.o c.o DEPEND_FILE:=$(patsubst %,.%.d,$(OBJ)) DEPEND_FILE:=$(wildcard $(DEPEND_FILE)) test : $(OBJ) gcc -o $@ $^ @echo depend_file = $(DEPEND_FILE) ifneq ($(DEPEND_FILE),) include $(DEPEND_FILE) endif %.o : %.c gcc -c -o $@ $< -MD -MF .$@.d clean_d: rm $(DEPEND_FILE) clean: rm *.o test
总的来说,就是通过获取对应文件的依赖文件,并包含进makefile当中,只要包含进去,makefile会自动识别出当哪些头文件发生变化的时候,需要对哪些文件进行重新编译
CFLAGS
CFLAGS可以理解为一个宏,可以再上面添加各种的编译参数
一般来说,项目的文件夹是这样的结构
h文件是单独放在一个文件夹里面的,那么当我们在包含此头文件的时候,就不能使用""而是应该使用<>,那么,我们就要在编译参数里面指定头文件的路径