本文用于分析一个Makefile文件。
# Usage:
# make # 编译所有c文件
# make clean # 删除所有二进制文件和对象
# make help #显示帮助信息
.PHONY: all help clean # 将目标all clean声明为一个伪目标
SRCS=$(wildcard *.c) # 返回值为当前目录下所有.c文件的列表。
BIN=$(SRCS:%.c=%) # 这叫做替换引用。在这种情况下,如果SRCS有值'foo.c bar.c',那么BINS将有'foo bar'
CC := gcc # gcc为C语言编译器、g++为c++编译器
CFLAGS=-Wall -g # 为gcc设置编译标志
all: $(BIN) # 伪目标调用${BINS}中的值作为单独的目标
help: # 显示帮助信息
@printf "Usage:\n"
@printf " make\t\t\t编译所有c文件\n"
@printf " make clean\t\t删除所有二进制文件和对象\n"
@printf " make help\t\t显示帮助信息\n"
clean: # 清除所有BIN产生的文件
@echo "Cleaning up..."
rm $(BIN)
运行效果:
$ make
gcc -Wall -g cp02.c -o cp02
gcc -Wall -g cp01.c -o cp01
$ make help
Usage:
make 编译所有c文件
make clean 删除所有二进制文件和对象
make help 显示帮助信息
$ ls
Makefile cp01 cp01.c cp02 cp02.c
$ make clean
Cleaning up...
rm cp02 cp01
$ ls
Makefile cp01.c cp02.c
函数讲解:
- PHONY: all clean
Makefile中的clean目标实际上并没有创建名为“clean”的文件,因此不符合我们为目标所描述的模式。相反,clean目标是作为一个快捷方式来运行清除项目构建文件的命令(命令开头加上“@”,Make在运行时将不会打印到终端)。我们将这样的目标标记为. PHONY的“依赖项”,这是一个不会被构建的伪目标。当Make实用程序遇到伪目标时,它将自动运行相关的命令,而不执行任何依赖项检查。
- $(wildcard PATTERN)
函数名称:获取匹配模式文件名函数-wildcard函数功能:列出当前目录下所有符合模式“PATTERN”格式的文件名。
返回值:空格分割的、存在当前目录下的所有符合模式“PATTERN”的文件名。函数说明:“PATTERN”使用shell可识别的通配符,包括“?”(单字符)、“*”(多字符)等。
- CFLAGS
CFLAGS变量为gcc设置编译标志:
-g 使用调试信息编译
-Wall 给出详细的编译警告
-O0 不优化生成的代码
-std=c99 使用c99标准语言定义函数
对于隐含规则
如果要使用隐含规则生成你需要的目标,你所需要做的就是不要写出这个目标的规则。那么,make会试图去自动推导产生这个目标的规则和命令,如果 make可以自动推导生成这个目标的规则和命令,那么这个行为就是隐含规则的自动推导。当然,隐含规则是make事先约定好的一些东西。例如,我们有下面的一个Makefile:
foo : foo.o bar.o
cc –o foo foo.o bar.o $(CFLAGS) $(LDFLAGS)我们可以注意到,这个Makefile中并没有写下如何生成 foo.o 和 bar.o 这两目标的规则和命令。因为make的“隐含规则”功能会自动为我们自动去推导这两个目标的依赖目标和生成命令。
make会在自己的“隐含规则”库中寻找可以用的规则,如果找到,那么就会使用。如果找不到,那么就会报错。在上面的那个例子中,make调用的隐含规则是,把 .o 的目标的依赖文件置成 .c ,并使用C的编译命令 cc –c $(CFLAGS) foo.c 来生成 foo.o 的目标。也就是说,我们完全没有必要写下下面的两条规则:
foo.o : foo.c
cc –c foo.c $(CFLAGS)
bar.o : bar.c
cc –c bar.c $(CFLAGS)因为,这已经是“约定”好了的事了,make和我们约定好了用C编译器 cc 生成 .o 文件的规则,这就是隐含规则。
当然,如果我们为 .o 文件书写了自己的规则,那么make就不会自动推导并调用隐含规则,它会按照我们写好的规则忠实地执行。
还有,在make的“隐含规则库”中,每一条隐含规则都在库中有其顺序,越靠前的则是越被经常使用的,所以,这会导致我们有些时候即使我们显示地指定了目标依赖,make也不会管。如下面这条规则(没有命令):
foo.o : foo.p
依赖文件 foo.p (Pascal程序的源文件)有可能变得没有意义。如果目录下存在了 foo.c 文件,那么我们的隐含规则一样会生效,并会通过 foo.c 调用C的编译器生成 foo.o 文件。因为,在隐含规则中,Pascal的规则出现在C的规则之后,所以,make找到可以生成 foo.o 的C的规则就不再寻找下一条规则了。如果你确实不希望任何隐含规则推导,那么,你就不要只写出“依赖规则”,而不写命令。
因此,在all: $(BIN) 命令下,Makefile将自动按照隐含规则,用$(CC)对c文件进行编译,并生成可执行文件。
参考
- [1] Makefile原文出处
- [2] Markdown 添加文献引用
- [5] 跟我一起写Makefile