Linux学习三(Makefile)
Linux学习三 : Makefile
make是一个命令工具,是一个解释makefile中指令的命令工具
make工具在构造项目的时候需要加载一个叫做makefile的文件,makefile关系到了整个工程的编译规则。一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为makefile就像一个Shell脚本一样,其中也可以执行操作系统的命令。
makefile的好处就是可以自动化部编译,一旦写好,只需要执行make指令,整个工程就可以自动编译。
makefile文件有两种命名方式 makefile 和 Makefile,构建项目的时候在哪个目录下执行构建命令 make这个目录下的 makefile 文件就会别加载,因此在一个项目中可以有多个 makefile 文件,分别位于不同的项目目录中。
Makefile的语法规则
1 |
|
每条规则由三个部分组成分别是目标(target), 依赖(depend)和命令(command)。
命令(command): 当前这条规则的动作,一般情况下这个动作就是一个 shell 命令。
* 例如:通过某个命令编译文件、生成库文件、进入目录等。
* 动作可以是多个,每个命令前必须有一个Tab缩进并且独占占一行。
依赖(depend): 规则所必需的依赖条件,在规则的命令中可以使用这些依赖。
- 例如:生成可执行文件的目标文件(*.o)可以作为依赖使用。
- 如果规则的命令中不需要任何依赖,那么规则的依赖可以为空。
- 当前规则中的依赖可以是其他规则中的某个目标,这样就形成了规则之间的嵌套。
- 依赖可以根据要执行的命令的实际需求, 指定很多个。
目标(target): 规则中的目标,这个目标和规则中的命令是对应的。
* 通过执行规则中的命令,可以生成一个和目标同名的文件。
* 规则中可以有多个命令, 因此可以通过这多条命令来生成多个目标, 所有目标也可以有很多个。
* 通过执行规则中的命令,可以只执行一个动作,不生成任何文件,这样的目标被称为伪目标。
1 |
|
规则的执行
在调用 make 命令编译程序的时候,make 会首先找到 Makefile 文件中的第 1 个规则,分析并执行相关的动作。但是如果依赖不存在的话,就不会执行。需要先将依赖生成出来,就可以在makefile中添加新的规则,将不存在的依赖作为这个新的规则中的目标,当这条新的规则对应的命令执行完毕,对应的目标就被生成了,同时另一条规则中需要的依赖也就存在了。
这样,makefile中的某一条规则在需要的时候,就会被其他的规则调用,直到makefile中的第一条规则中的所有的依赖全部被生成,第一条规则中的命令就可以基于这些依赖生成对应的目标,make 的任务也就完成了。
规则的嵌套
1 |
|
当依赖不存在的时候,make就是查找其他的规则,看哪一条规则是用来生成需要的这个依赖的,找到之后就会执行这条规则中的命令。因此规则2, 规则3, 规则4里的命令会相继被执行,当规则1中依赖全部被生成之后对应的命令也就被执行了,因此规则1的目标被生成,make工作结束。
如果想要执行makefile中非第一条的命令,就要在make后面加上具体的命令,比如 make c.o 单独执行规则4。
make判断是否进行编译
- make进行编译的时候,如果目标的时间戳>依赖的时间戳,就不再进行编译,因为依赖没有更新,也就不会生成新的目标。
- 当目标的时间戳<依赖的时间戳,证明需要更新,依赖变化了。
- 目标不存在,那么肯定更新。
根据上文的描述, 先执行 make 命令,基于这个 makefile 编译这几个源文件生成对应的目标文件。然后再修改例子中的 a.c, 再次通过make编译这几个源文件,那么这个时候先执行规则2更新目标文件a.o, 然后再执行规则1更新目标文件app,其余的规则是不会被执行的。
make有时候并不会完全依赖makefile,会进行自动推导,比如: 使用命令 make 编译扩展名为.c 的 C 语言文件的时候,源文件的编译规则不用明确给出。这是因为 make 进行编译的时候会使用一个默认的编译规则,按照默认规则完成对.c文件的编译,生成对应的.o 文件。它使用命令cc -c来编译.c 源文件。
变量
makefile中的变量分为三种:自定义变量,预定义变量和自动变量。
自定义变量:用 Makefile 进行规则定义的时候,用户可以定义自己的变量,称为用户自定义变量。定义变量后用$符号将变量取出。
1 |
|
1 |
|
预定义变量:在 Makefile 中有一些已经定义的变量,用户可以直接使用这些变量,不用进行定义。在进行编译的时候,某些条件下 Makefile 会使用这些预定义变量的值进行编译。这些预定义变量的名字一般都是大写的。
1 |
|
自动变量:Makefile 中的规则语句中经常会出现目标文件和依赖文件,自动变量用来代表这些规则中的目标文件和依赖文件,并且它们只能在规则的命令中使用。
1 |
|
1 |
|
模式匹配
模式匹配将一系列的相同操作整理成一个模板,所有类似的操作都通过模板去匹配 makefile 会因此而精简不少,只是可读性会有所下降。
1 |
|
函数
makefile的函数主要是为了方便的获取返回值,写法是这样的: $(函数名 参数1, 参数2, 参数3, …)。
wildcard函数:获取指定目录下指定类型的文件名,其返回值是以空格分割的、指定目录下的所有符合条件的文件名列表。该函数的参数只有一个, 但是这个参数可以分成若干个部分, 通过空格间隔。
1 |
|
patsubst函数:按照指定的模式替换指定的文件名的后缀。有三个参数, 参数之间使用逗号间隔。
1 |
|
makefile编写:一步一步编写makefile文件。
1 |
|
版本1:最简单版本:书写简单,但只要依赖中的某一个源文件被修改,所有的源文件都需要被重新编译,太耗时、效率低。
1 |
|
版本2:修改哪一个源文件, 哪个源文件被重新编译, 不修改就不重新编译。但是代码冗余。
1 |
|
版本3:降低冗余,使用变量和模式匹配。代码相对简洁,但是变量obj的值需要手动写出来,如果需要编译的项目文件很多,都用手写出来不现实。
1 |
|
版本4:在makefile中使用函数。解决了自动加载项目文件的问题,解放了双手,但没有文件删除的功能,不能删除项目编译过程中生成的目标文件(*.o)和可执行程序。
1 |
|
版本5:在makefile文件中添加新的规则用于删除生成的目标文件(*.o)和可执行程序。可以用make clean删除不需要的文件和程序。
1 |
|
版本6:在makefile中将clean声明为一个伪目标,在 makefile 中声明一个伪目标需要使用 .PHONY 关键字, 声明方式为: .PHONY:伪文件名称。
1 |
|
较复杂结构的makefile编写
1 |
|
参考列表:
https://blog.csdn.net/haoel/article/details/2886
https://subingwen.cn/