Mikefile文件的工作原理
我们写的C代码都要编译后才能运行,那么写出来的C代码怎么组织编译呢?因为有些代码可能要依赖其他的代码,编译的顺序就要有个先后。Makefile就像一张编译的清单,它可以将这些依赖关系表达清楚,让make编译工具按我们所想的去编译我们的源代码。
所以Makefile就是一份告诉make编译工具如何编译我们源代码的文件。编写一份make编译工具能看得懂的Makefile文件,以下这些知识,是我们应该要掌握的。现在就开始吧。
基本知识
规则:是Makefile用来告知make如何编译的。通常,一条规则包括三部分:
目标:先决条件(有人喜欢说是依赖)
执行的命令
执行的命令用先决条件里提供的资源(当然就是我们的源文件或其他已经生成的二进制文件)产出目标,产出的是一个二进制文件。目标像将要做的一道菜,先决条件就是组成这道菜的材料,执行的命令就是做出这道菜的方法。执行时,执行的命令用材料,做出这道菜。以下是个例子:
hello:hello.c
gcc hello.c -o hello
这里,我们的目标是hello二进制文件,hello.c是要提供的先决条件,然后gcc命令编译hello.c,产出hello二进制文件。
目标也可以是虚拟的,不一定就是个二进制文件,如:
say_hello:
@echo "Hello world"
这个虚拟目标say_hello只是输出一些信息而已,因此它可以没有先决条件,也不会产出任何二进制文件,这在Makefile里是允许的。
提个醒:@echo 和echo的作用是一样的,唯一不同的是有@的话,只会输出“Hello world”,否则还会输出打印的命令,如:
$ make
echo "Hello world"
Hello world
一般来说,一个C项目的Makefile都有很多规则,这种情况下执行make命令时,默认只执行第一个规则,如:
say_hello:
@echo "Hello World"
generate:
@echo "Creating text files..."
clean:
@echo "Cleaning up..."
$ make
Hello World
确实只有第一个规则会被执行,其他的都没有被执行。如果我们不想Makefile的第一个规则成为默认执行的规则,可以使用.DEFAULT_GOAL来设定,从而改变默认执行的规则,如:
say_hello:
@echo "Hello World"
generate:
@echo "Creating text files..."
clean:
@echo "Cleaning up..."
.DEFAULT_GOAL := generate
$ make
Creating text files...
generate规则就会在make执行时,默认被执行。
make命令也可以直接执行对应的规则,如执行clean:
$ make clean
.DEFAULT_GOAL 只能执行一个目标,有没有办法执行多个规则呢?当然!我们可以用一个虚拟目标all,来同时指定要执行的目标,但这一行要放在Makefile文件的最前面,原因是make执行会默认执行第一行,如:
all: say_hello generate
say_hello:
@echo "Hello World"
generate:
@echo "Creating text files..."
clean:
@echo "Cleaning up..."
因为all say_hello generate clean都是虚拟目标,如因为它们都不支真正产出二制文件,在这个例子里,因此最好加上.PHONY来标识一下。
.PHONY: all say_hello generate clean
all: say_hello generate
say_hello:
@echo "Hello World"
generate:
@echo "Creating text files..."
clean:
@echo "Cleaning up..."
.PHONY 的意思就是虚假的意思,make不会管文件或文件名是不是存在,直接执行命令。
$ make
Hello World
Creating empty text files...
执行make时,say_hello和generate会被执行。
进阶
在Makefile里可以使用变量,然后进行引用,这样用到的地方只需要引用变量即可,省去大量的冗余代码。如:
CC = gcc
以面定义了CC变量,代表gcc命令,引用方式可以是以两种方式中的任意一种(${变量名}
,$(变量名)
):
hello: hello.c
${CC} hello.c -o hello
hello: hello.c
$(CC) hello.c -o hello
用=
定义方式变量不安全,一不小心就会引发无限循环递归调用的问题,如:
CC = gcc
CC = ${CC}
all:
@echo ${CC}
错误如下:
$ make
Makefile:8: *** Recursive variable 'CC' references itself (eventually). Stop.
为了避免这种问题我们应该将=
换成:=
:
CC := gcc
最后,结合一个从别人那里复制来的例子,记录一下Makefile的了解一下模式和函数的使用:
.PHONY = all clean
CC := gcc # compiler to use
LINKERFLAG = -lm
SRCS := $(wildcard *.c)
BINS := $(SRCS:%.c=%)
all: ${BINS}
%: %.o
@echo "Checking.."
${CC} ${LINKERFLAG} $< -o $@
%.o: %.c
@echo "Creating object.."
${CC} -c $<
clean:
@echo "Cleaning up..."
rm -rvf *.o ${BINS}
- 以#开头的行都是注释
- SRCS := $(wildcard *.c)这一句的$(wildcard pattern)是用于文件名的函数之一。它的意思是所有以
.c
结尾的文件名都会匹配上,然后这些文件就会存在SRCS变量中。 - BINS := $(SRCS:%.c=%)这一句里的%号就是我们要引用的部分,这一句的意思是将SRCS里存储的文件名,不包括
.c
部分,都提取出来存到BINS变量中。如SRCS中有hello.c那么BINS中就会有hello。 - all: ${BINS}这一句也很有意思,all这个虚拟目标会将BINS存在的值作为一个目标去调用。这跟我们前面讲的是一个道理。
%: %.o
@echo "Checking.."
${CC} ${LINKERFLAG} $< -o $@
在上面这几行中,我们假设BINS变量中有个值为hello,那么%就会匹配到hello,那么这条规则展开会就如下所示:
hello: hello.o
@echo "Checking.."
gcc -lm hello.o -o hello
由此可见,$<
代表的就是先决条件,$@
代表的就是目标。
%.o: %.c
@echo "Creating object.."
${CC} -c $<
同理,上面展开后,如下所示:
hello.o: hello.c
@echo "Creating object.."
gcc -c hello.c
我觉得讲到这,也差不多了。有问题随时都可以交流。
文章来源: blog.csdn.net,作者:WongKyunban,版权归原作者所有,如需转载,请联系作者。
原文链接:blog.csdn.net/weixin_40763897/article/details/122773258
- 点赞
- 收藏
- 关注作者
评论(0)