一. Makefile规则
一个简单的 Makefile 文件包含一系列的“规则”,其样式如下:
目标(target)…: 依赖(prerequiries)…
<tab>命令(command)
目标(target):通常是要生成的文件的名称,可以是可执行文件或 OBJ文件,
也可以是一个执行的动作名称
依赖:用来产生目标的材料(比如源文件),一个目标经常有几个依赖。
命令:生成目标时执行的动作,一个规则可以含有几个命令,每个命令占一
行。
例1:
test:a.o b.o
gcc -o test a.o b.o
a.o : a.c
gcc -c -o a.o a.c
b.o : b.c
gcc -c -o b.o b.c
clean :
rm *.o test
test是要生成的目标文件,a.o b.o是依赖的两个文件,命令是输入<tab>键后的三条语句
注意:每个命令行前面必须是一个 Tab 字符,即命令行第一个字符是 Tab。这是
容易出错的地方。
Makefile的核心规则:当“目标文件(test)”不存在,或者某个依赖文件(a.o或者b.o)比目标文件“新”,则执行“命令”,此处的“新”指的是依赖文件最后的编写时间比目标文件晚。
二. Makefile的简单语法
1. 通配符:%.o
当你需要匹配一组具有相同前缀或后缀的文件时,可以使用通配符。使用通配符后可以将例1改为如下
例2:
test:a.o b.o
gcc -o test $^
%.o : %.c
gcc -c -o $@ $<
clean :
rm *.o test
第二行中,$^表示所有依赖文件
第四行中, $@表示要生成的目标文件,$<表示依赖,“<”表示第一个依赖文件,也可在第三行%.c后放其他依赖文件
2. 即时变量、延时变量、export
即时变量(简单变量)的定义:
A := xxx # A的值即刻确定,在定义时即确定
延时变量的定义:
B = xxx # B的值使用到时才确定,在定义/等于时并没有确定下来
B ?= xxx # 延时变量, 如果是第1次定义才起效, 如果在前面该变量已定义则忽略这句
附加定义:
C \+= xxx #它是即时变量还是延时变量取决于前面的定义
D ?=xxx #如果这个变量在前面已经被定义了,这句话就会不会起效果,
综合示例:
A := $(C) #$表示引用
B = $(C)
C = abc
D ?= R #D已经被定义了,所以次表达式不会生效
all:
@echo A = $(A) #echo前使用@就不会打印echo
@echo B = $(B) #延时变量
@echo D = $(D)
C += 123 #不管C在哪儿定义,B输出结果都是C
我们使用命令:make D=1234
最后输出结果时是:
A =
B = abc 123
D = 1234
D被定义了,所以代码中的赋值不起作用
3. 假想目标:.PHONY
使用makefile:
make [目标] # 若无目标,默认第一个目标(示例中的test)
我们执行 make clean 命令,当该路径下不存在名为clean的目标文件时,可以删除test文件,但是当存在一个名为clean的文件时,判断存在clean目标文件,并且没有新的依赖,无法删除test。此时引入假想目标.PHONY,机器就不会判断名为clean的文件是否存在。代码如下:
例3:
test:a.o b.o
gcc -o test $^
%.o : %.c
gcc -c -o $@ $<
clean :
rm *.o test
.PHONY: clean
三. Makefile相关函数
1. $(foreach var,list,text)
2. $(filter pattern…,text) #在text中取出符合patten格式的值
$(filter-out pattern…,text) #在text中取出不符合patten格式的值
3. $(wildcard pattern) #pattern定义了文件名的格式,wildcard取出其中存在的文件
4. $(patsubst pattern ,replacement,$(var)) #将变量var中所有pattern替换成replacement
综合示例,例4:
A = a b c
B = $(foreach f, $(A), $(f).o)
C = a b c d/
D = $(filter %/,$(C))
E = $(filter-out %/,%(C))
files = $(wildcard *.c)
files2 = a.c b.c c.c d.c e.c
files3 = $(wildcard $(files2))
dep_files $(%.c,%.d,$(files2))
all:
@echo B = $(B)
@echo D = $(D) #输出C中,包含/的值
@echo E = $(E) #输出C中,没有/的值
@echo files = $(files) #输出当前目录下满足.c结尾文件的名称
@echo files3 = $(files3) #输出files2中存在的文件
@echo dep_files = $(dep_files) #将files2中的.c后缀替换成.d后缀输出
使用make命令输出,结果为
B = a.o b.o c.o
D = d/
E = a b c
files = a.c b.c c. d
files3 = a.c b.c c.c
dep_files = a.d b.d c.d d.d e.d
四. Makefile示例
1. 改进:支持头文件依赖
gcc -M c.c #打印出依赖
gcc -M -MF c.d.c.d #把依赖写入文件c.d
gcc -c -o c.o c.c -MD -MF c.d #编译C.o,把依赖写入文件c.d
2. 添加CFLAGS---编译选项
例5:
objs = a.o b.o c.o
dep_files := $(patsubst %,.%d, $(objs))
dep_files := $(wildcard $(dep_files))
CFLAGS = -Werror -I. #-Werror是将所有警告都改为错误输出
#—I.是指定当前目录是编译默认的头文件目录
test: $(objs)
gcc -o test $^
ifneq ($(dep_files),)
include $(dep_files)
endif
%.o : %.c
gcc -c -o $@ $< -MD -MF .$@.d #自动生成依赖
clean:
rm *.o test
distclean:
rm $(dep_files) #删除依赖
.PHONY:clean