Linux项目自动化构建工具—make与makefile
目录
make/Makefile
为什么需要用到make/Makefile呢?
具体谈谈make与Makefile
Makefile
make
例子
项目清理
make的工作原理
make/Makefile
我们先来简单认识一下什么是make与Makefile,它们是用于自动化构建软件项目的工具,主要用于编译源代码文件,管理项目依赖关系,并确保只重新编译那些自上次构建以来有所更改的文件。
为什么需要用到make/Makefile呢?
在window平台下编写C或者C++代码的时候,我们一般使用的都是配置完成的集成的开发环境,在我们完成代码编写的时候,我们只需要点击运行,平台便会自动的帮我们生成可执行文件,至于中间具体的过程,平台向用户进行了省略。
如在vs下我们生成可执行文件a.out时,需要经过四个步骤
- 由源文件test.c经过预编译(-E)得到test.i
- 由test.i经过编译(-S)得到test.s
- 由test.s经过汇编(-C)得到test.o
- 最后经过链接得到可执行文件a.out
而在Linux下则不同,每一个源文件都需要用户手动地进行指令编译生成可执行文件。
在Linux中,我们也需要这种一站式服务,于是make和Makefile就出现了。
具体谈谈make与Makefile
Makefile
Makefile是一个文件。它是一个工程文件的编译规则,它记录了原始码如何编译的详细信息、描述了整个工程的编译链接等规则。
Makefile存在的意义就是为了构建依赖关系和依赖原理。
Makefile带来的好处是“自动化编译”,一旦写好的话,只需要一个make命令,整个工程的完全自动编译,极大的提高了软件开发的效率。
target(目标文件):文件1 文件2(依赖文件列表) //依赖关系<Tab>gcc -o 欲建立的执行文件 目标文件1 目标文件2 ///依赖方法command......
target为目标文件,即最终做需要的可执行文件。
-
依赖文件列表即生成目标文件所需要的文件或目标。
-
<Tab>即这一行必须以TAB键开头,注意不能空格敲四下来代替。
-
<Tab>后面的即make需要执行的命令(这里可以是任意的shell命令)。
- 如果在依赖文件列表中,有一个及以上的文件或目标比target文件新的话,command命令就会被执行。即make并不会管命令是怎么工作的,它只管执行所定义的命令,make会比较target和依赖文件列表中文件的修改日期,若后者比前者文件日期要新或者target不存在,则make会执行后续定义的命令(在后面的make原理会讲到)。
make
make是一个命令工具,是一个解释Makefile中指令的命令工具。
总结:make是一个命令,Makefile是一个文件,他们两个搭配使用,完成项目的自动化创建。
例子
这里我们简单举个例子,利用多文件的方式来使用make/Makefile进行编译。
- 我们先在Practice目录下touch三个文件分别保存函数的声明(.h文件),函数的实现(.c文件),主函数
#pragma once
#include<stdio.h>
extern int count(int a,int b);
#include"mytest.h"
int count(int a,int b)
{return a+b; }
#include"mytest.h"
int main(){int a=40;int b=50;int ret=count(a,b);printf("%d\n",ret);return 0;}
2、下面进行Makefile的编写
为了方便书写,Linux中还有特殊符号,如$@ 表示目标文件、$<表示第一个依赖文件、$^表示所有的依赖文件,所以上面的Makefile我们还可以写为
如图,我们想生成一个可执行文件processOn,它依赖于main.c和mytest.c,第二行是它的依赖方法。此时我们使用make命令,则目录下会自动生成一个可执行文件processOn.
接着我们运行processOn
那么问题来了,Makefile的第三至5行是什么呢?
项目清理
- 工程是需要被清理的。
- 像clean这种没有被第一个目标文件直接或者间接关联,那么它后面的命令就不会被执行,所以我们可以显式要make执行,即make clean,以此来一次性清除所有生成的目标文件,以便于重新编译。
- .PHONY是一个关键字,被该关键字修饰的对象是一个伪目标,该目标总是被执行,不是拿时间来作为新旧文件的对比。所以一般将clean设置成伪目标。
make的工作原理
- make会在当前目录下找名字叫“Makefile”或“makefile”的文件,所以在创建makefile文件时首字母大小写都可以。
- 如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到processOn这个文件, 并把这个文件作为最终的目标文件。
- 如果processOn文件不存在,或是processOn所依赖的后面的mytest.c/main.c文件的文件修改时间要比processOn这个文件新,那么,他就会执行后面所定义的命令来生成processOn这个文件。
那么make怎么判断文件的新旧呢?以什么依据作为判断条件呢?
我们可以使用Linux下的stat命令来查看源文件和可执行文件的所有属性
如图,上面一共有三个时间分别是 Acess(最后一次访问该文件的时间)、Modify(最后一次更改该文件属性或状态的时间)、Change(最后一次修改该文件内容的时间),make将Change的时间作为比较新旧的标准。
若processOn的时间比依赖文件列表main.c或mytest.c的时间晚,则不会执行make后面的命令。
而如果像前面的clean一样加了关键字.PHONY后成为伪目标,则make会执行后边的命令,即使目标文件的时间比依赖文件的时间晚。
- 如果processOn所依赖的mytest.c/main.c文件不存在,那么make会在当前文件中找目标为mytest.c/main.c文件的依赖性,如果找到则再根据那一个规则生成mytest.c/main.c文件。(这有点像一个堆栈的过程)
- 这就是整个make的依赖性,make会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件。
例如,下面的Makefile文件,目标文件test的形成依赖于test.o文件,但是test.o文件不存在,所以寻找test.o文件的依赖关系,找到test.s文件,而test.s文件也不存在,再找test.s的依赖关系,就这么一层层找下去,直到找到最后一个文件的依赖关系,生成test.i文件,再一层层往上生成,所以看起来有点像一个堆栈的过程。
- 在找寻的过程中,如果出现错误,比如最后被依赖的文件找不到,那么make就会直接退出,并报错, 而对于所定义的命令的错误,或是编译不成功,make根本不理。
- make只管文件的依赖性,即,如果在我找了依赖关系之后,冒号后面的文件还是不在,那么对不起, 我就不工作啦。