Makefile总览
- Makefile基本语句:
目标(target) : 依赖 (prerequisites)
[TAB]命令(command)
目标包括:执行文件(第一个目标文件),中间目标文件(执行文件的依赖),标签(类似clean)
- Makefile文件的执行:
- 使用
make
命令执行Makefile文件,默认第一条规则的目标为最终目标,也就是可执行文件,也可以在文件前面先指定目标的名称,到后面再完善它的依赖和命令; - make [目标名]:执行指定目标规则的命令;
- 当目标文件不存在,或者某一个依赖文件比目标文件新的时候,执行命令。
- 执行其他目录下的Makefile文件:
make -c /dir -f make.built
:执行/dir目录下名为make.built的编译文件(-c:指定目录,-f:指定Makefile文件名称,-I:指定包含的的其他文件目录) - make命令中也可以用
=
指定Makefile中定义的宏,实现条件编译;
- 使用
make BOOT_MEDIA=emmc AMP_TYPE=linux all #指定两个条件,编译目标all
Makefile文件执行:
make
程序会把整个Makefile文件一次性读进去,再对文件进行解析。-
Makefile变量:
- Makefile中的变量都是字符串,和C/C++中的宏是一个意思。
- 调用变量时,使用变量的引用操作符:$(变量名称);
- Makefile输出字符串命令:
echo $
(变量名); - 取消终端打印命令:在命令前添加
@
; - Makefile中的注释:以
#
开头。
-
Makefile中对变量有两种赋值方式:
包括:即时变量和延时变量- 赋值符号
=
:延时变量,变量的值在使用时才被确定,在定义时并没有确定; - 赋值符号
:=
:即使变量,变量的值在定义时就被确定(即时确定); - 赋值符号
?=
:延时变量,变量如果未被赋值,则使用赋予新值,如果赋值了,则使用之前的值,不使用等号后面的值; - 变量追加
+=
:在原有的变量后面追加新的变量字符串。
- 赋值符号
Makefile里的函数
-
$(var : a = b)
:对于var中以a
结尾的,把结尾a
替换为b
value = a.o b.o c.o
value1 = $(value : .o = .c)
value = a.o b.o c.o
value1 = $(value : %.o = %.c)#使用%时,变量必须都具有相同的模式
则value1
的值均为:a.c,b.c,c.c
-
$(a)_$(b)
:将变量a
和变量b
组合成a_b
,类似这种字符串的拼接可以进行任意组合,
-
$(subst a, b, value)
:字符串替换函数,将value中的字符a
替换为字符b
value = value1
res = $(subst 1, 2, $(value))
变量res
的值为value2
-
$(foreach var, list, text)
:对list里的每一个变量执行text格式的转换
A = a b c
B = $(foreach f, $(A), $(f).o)
则B = a.o b.o c.o
-
$(filter pattern, text)
:过滤函数,取出text中符合pattern格式的变量
$(filter-out pattern, text)
:反过滤函数,取出text中不符合pattern格式的变量
A = a b c d/
B = $(filter %/, $(A))
C = $(filter-out %/, $(A))
则B的结果为:d/
;C的结果为:a b c
-
$(wildcard pattren)
:列出当前目录下符合pattern格式的文件
files = a.c b.c c.c d.o
files1 = $(wildcard *.c)
files2 = $(wildcard $(file)) #取出file中真实存在的文件
则files1的值为:a.c b.c c.c
;files2的值为:a.c b.c c.c d.o
-
$(patsubst pattern, repalcement, $(var))
:模式字符串替换函数,支持通配符,把列表var中符合pattern格式的变量替换成replacement格式的变量
file = a.c b.c c.c d.o
dep_flies = $(patsubst %.c, %.d, $(files))
则dep_files的值为:a.d b.d c.d d.o
,符合格式的就替换,不符合格式的就不替换,之后存入对应的变量中。
Makefile举例:
main: a.o b.o c.o
gcc -o main $^
%.o : %.c
gcc -c -o $@ $<
clean:
rm *.o main
.PHONY: clean
-
%
:文件通配符,如%.o : %.c
目标和依赖都是一系列文件,依赖中%所代表的字符串取决于目标中%所代表的字符串。 - 使用了通配符时,命令中用
$@
表示所有目标,使用$>
表示依赖的第一个文件(使用$^表示所有的依赖) - 若使用
make clean
则执行目标clean
的命令;但此时若目录下有名为clean的文件,则会覆盖clean命令的执行,(因为根据Makefile的执行规则,clean文件已存在,而且clean没有依赖,无法通过时间判断文件是否更新,所以不执行clean的命令);
所以把clean定义为假想目标.PHONY:
,这样Makefile在执行时就直接执行目标'clean'的命令; - 伪目标只是一个标签,并不是文件。
包含头文件的问题
main : a.o b.o c.o
gcc -o main $^
%.o : %.c
gcc -c -o $@ $<
- 问题的原因:
.o
文件的依赖只有.c
文件,若只修改头文件而不修改.c
文件,则编译时并不会准确修改编译的结果,相当于修改的头文件并没有起到效果;- 比如:修改
c.h
,则添加规则
c.o : c.c c.h
:表示c.o的依赖是c.c
和c.h
,命令为空;
- 比如:修改
- 但Linux中头文件过于庞大,手写规则会过于复杂,所以用以下命令添加规则
gcc -MM c.c #打印出c.c的依赖文件
gcc -MM -MF c.d c.c #将c.c的依赖写进c.d
gcc -c -o c.o c.c -MD -MF #把依赖写进c.d,编译生成c.o
或者编译前make clean
,再进行编译,也可以编译出新的.o文件
- 包含头文件问题的解决:
objs = a.o b.o c.o
dep_files := $(patsubst %, .%.d, $(objs)) #将变量名修改为为.x.o.d并赋值给dep_files,用于存储各个.c文件的依赖
#dep_files := $(foreach f, $(objs), .$(f).d)也可以使用foreach函数
dep_files := $(wildcard $(dep_files)) #取出dep_files中真实存在的文件,存入dep_files中,若不存在则dep_files为空
main : $(objs)
gcc -o main $^
ifneq ($(dep_files), ) #如果dep_files非空
include $(dep_files) #则包含依赖dep_fiels
endif
%.o : %.c
gcc -c -o $@ $< -MD -MF .$@.d #编译文件,并将依赖分别写进.x.o.d文件中
clean :
rm *.o main
distclean:
rm $(dep_files)
- 依赖关系:
.o
依赖于.d
文件,.d
文件依赖于.c
文件。 - include关键字:
- 可用于在一个Makefile中包含另一个Makefile,典型的用法就是包含子目录的Makefile文件;
- 在后面的文件名中可以包含路径,通配符,变量的引用;
- include前面不能用TAB键,include后面可以有空格
补充:由于编译的要求,Makefile可能会用到CFLAGS添加编译参数,包括:输出警告信息CFLAGS = -Wall
;编译debug版本CFLAGS = -g
;编译优化CFLAGS = -O
;将警告当成错误CFLAGS = -Werror
。