什么是Makefile
Makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,文件之间有哪些依赖等。Makefile有自己的书写格式、关键字、函数。像C 语言有自己的格式、关键字和函数一样。而且在Makefile中可以使用系统shell所提供的任何命令来完成想要的工作。Makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率
Makefile里有什么
Makefile里包含了:显示规则、隐晦规则、变量定义、文件指示和注释
- 显式规则。显式规则说明了,如何生成一个或多的的目标文件。这是由Makefile的书写者明显指出,要生成的文件,文件的依赖文件,生成的命令。
- 隐晦规则。由于我们的make有自动推导的功能,所以隐晦的规则可以让我们比较粗糙地简略地书写Makefile,这是由make所支持的。
- 变量的定义。在Makefile中我们要定义一系列的变量,变量一般都是字符串,这个有点你C语言中的宏,当Makefile被执行时,其中的变量都会被扩展到相应的引用位置上。
- 文件指示。其包括了三个部分,一个是在一个Makefile中引用另一个Makefile,就像C语言中的include一样;另一个是指根据某些情况指定Makefile中的有效部分,就像C语言中的预编译#if一样;还有就是定义一个多行的命令。有关这一部分的内容,我会在后续的部分中讲述。
- 注释。Makefile中只有行注释,和UNIX的Shell脚本一样,其注释是用“#”字符,这个就像C/C 中的“//”一样。如果你要在你的Makefile中使用“#”字符,可以用反斜框进行转义,如:“#”。
Makefile的文件名
Makefile的文件名有两种方式:
- 默认的文件名 Makefile 或makefile
- 指定文件名 例如 Make.Linux,Make.Mac等
# 默认的文件名,Makefile、makefike
> make
# 指定文件名,Make.Linux
> make -f Make.linux
Makefile规则
显式规则
代码语言:txt复制target ... : prerequisites ...
command
- target: 需要生成目标,或者是标签
- prerequisites:生成目标所依赖的列表
- command:shell指令,<font color=red>格式为Tab开头</font>
Makefile规则的target 和prerequisites 存在依赖关系,target是依赖于prerequisites;Makefile 存在自动推导的能力;不断向上<font color=red>自动推导</font>;
参考示例:
代码语言:txt复制test : test1.o test2.o
cc -o test test1.o test2.o
test1.o : test1.c test1.h
cc -c test1.c
test2.o : test2.c test2.h
cc -c test2.c
clean :
rm test test1.o test2.o
Makefile会进行自动推到,层层依赖、推导关系如下,
- test 依赖于 test1.o 、test2.o
- test1.o 依赖于 test1.c 、test1.h
- test2.o 依赖于 test2.c 、test2.h
makefile会把所有依赖关系列举出来,执行make命令的时候,会根据依赖关系自动编译
隐晦规则
每个.o文件的依赖文件默认会有同名的.c文件,比如有一个target是test.o,那么test.c默认就是test.O的依赖文件,这个是makefile的隐晦规则,是make会自动推导出来的
make的工作方式
在默认命名的情况下,输入make命令做了什么?
- make命令会找当前工作路径下的Makefile 或makefile文件
- 找到文件,会寻找文件中,<font color=red>第一个目标文件(target)</font>,参考上面的示例就是”test“文件,并作为最终目标文件
- 如果test文件不存在,就会根据test文件所依赖的.o文件,根据.o文件来生成test文件
- 如果.o文件不存在,make会在makefile文件中,找到目标为.o文件,根据.o文件的依赖性,生成.o文件
make的依赖性,会自动推到一层层的依赖关系,最终编译出最终的目标。
makefile 变量
变量基础
例如:
代码语言:txt复制# 声明一个变量
objects = test1.o test2.o
test3.o
# 使用变量
test: $(objects)
cc -o test $(objects)
- 变量的引用可以使用 ${变量名} 或 $(变量名) 中括号或括号都可以
- Makefile 中的变量的使用其实非常的简单,因为它并没有像其它语言那样定义变量的时候需要使用数据类型。变量的名称可以由大小写字母、阿拉伯数字和下划线构成。<font color=red>等号左右的空白符没有明确的要求</font>,因为在执行 make 的时候多余的空白符会被自动的删除。至于值列表,既可以是零项,又可以是一项或者是多项。
变量的基本赋值
- 简单赋值 ( := ) 编程语言中常规理解的赋值方式,<font color=red>只对当前语句的变量有效。</font>
- 递归赋值 ( = ) 赋值语句可能影响多个变量,所有目标变量相关的其他变量都受影响。
- 条件赋值 ( ?= ) 如果变量未定义,则使用符号中的值定义变量。如果该变量已经赋值,则该赋值语句无效。
- 追加赋值 ( = ) 原变量用空格隔开的方式追加一个新值。
变量中的变量
在定义变量的值时,我们可以使用其它变量来构造变量的值,在Makefile中有两种方式来在用变量定义变量的值。
例如:
代码语言:txt复制x := foo
y := $(x)
追加变量值
代码语言:txt复制objects = main.o foo.o bar.o utils.o
objects = another.o
于是,我们的$(objects)值变成:“main.o foo.o bar.o utils.o another.o”(another.o被追加进去了)
如果变量之前没有定义过,那么,“ =”会自动变成“=”,如果前面有变量定义,那么“ =”会继承于前次操作的赋值符。如果前一次的是“:=”,那么“ =”会以“:=”作为其赋值符,如:
变量高级用法
变量值替换
<font color=red size=5> (var:a = b) 或{var:a = b }</font>
把变量“var”中所有以“a”字串“结尾”的“a”替换成“b”字串。这里的“结尾”意思是“空格”或是“结束符还是看一个示例吧:
代码语言:txt复制foo := a.o b.o c.o
bar := $(foo:.o=.c)
# 把“$(foo)”中所有以“.o”字串“结尾”全部替换成“.c”,最终bar的值是a.c b.c c.c
把变量值再当成变量
代码语言:txt复制x = y
y = z
a := $($(x))
# 在这个例子中,$(x)的值是“y”,所以$($(x))就是$(y),于是$(a)的值就是“z”
makefile注释
Makefile中只有行注释,注释使用”#“ 字符 例如:
代码语言:txt复制# 这是makefile的注释
引用其他的Makefile
在Makefile使用include关键字可以把别的Makefile包含进来,make命令开始时,会把找寻include所指出的其它Makefile,并把其内容安置在当前的位置。这很像C语言的#include,被包含的文件会原模原样的放在当前文件的包含位置。include的语法是:
代码语言:txt复制include <filename>
# 在include前面可以有一些空字符,但是绝不能是[Tab]键开始
# filename可以是当前操作系统Shell的文件模式(可以保含路径和通配符)
-include <filename>
# 无论include过程中出现什么错误,都不要报错继续执行。上面那条指令若是找不到include的目标文件,会报错
伪目标
代码语言:txt复制clean:
rm *.o temp
<font color=red size=4>伪目标不会自动被执行,只能显式地调用执行。</font>但是上面伪目标的写法有一个缺陷,若是当前目录下存在有一个文件名为"clean",那么根据我们的规则,command将不会被执行,因为目标已经存在了,为了解决这个问题,我们可以使用一个特殊的标记“.PHONY”来显示地指明一个目标是“伪目标”,向make说明,不管是否有这个文件,这个目标就是“伪目标”
代码语言:txt复制.PHONY : clean
clean:
rm *.o temp
通过.PHONY,无论是否存在“clean”文件,我们的command都将会被执行了
常见伪目标
伪目标 | 描述 |
---|---|
all | 这个伪目标是所有目标的目标,其功能一般是编译所有的目标。 |
clean | 这个伪目标功能是删除所有被make创建的文件。 |
install | 这个伪目标功能是删除所有被make创建的文件。 |
这个伪目标的功能是例出改变过的源文件。 | |
tar | 这个伪目标功能是把源程序打包备份。也就是一个tar文件。 |
dist | 这个伪目标功能是创建一个压缩文件,一般是把tar文件压成Z文件。或是gz文件。 |
TAGS | 这个伪目标功能是更新所有的目标,以备完整地重编译使用。 |
check 和test | 这两个伪目标一般用来测试makefile的流程。 |
常见问题
1. @echo,命令不回显
- @echo,make会把其要执行的命令行在命令执行前输出到屏幕上。当我们用“@”字符在命令行前,那么,这个命令将不被make显示出来
2. 立即赋值(:=) 和延迟赋值(=)
:=
: 强制按先后顺序执行,立即赋值。=
:赋值的结果会等到整个路径执行完再决定,后面的会覆盖前面的,延迟赋值。
示例:
代码语言:txt复制$ cat Makefile
a = foo
b1 := $(a) bar
b2 = $(a) bar
a = xyz
all:
@echo b1=$(b1)
@echo b2=$(b2)
$ make
b1=foo bar
b2=xyz bar
3. 在 Makefile 表达式中使用逗号和空格变量
逗号和空格是 Makefile 表达式中的特殊符号
参考文档:
https://juejin.cn/post/6844904001872330760#heading-4
https://blog.csdn.net/weixin_38391755/article/details/80380786/