为什么要学习Makefile?
Linux环境下的程序员如果不会使用GNU make来构建和管理自己的工程,应该不能算是一个合格的专业程序员,至少不能称得上是Unix程序员。在Linux(unix)环境下使用GNU的make工具能够比较容易的构建一个属于你自己的工程,整个工程的编译只需要一个命令就可以完成编译、连接以至于最后的执行。不过这需要我们投入一些时间去完成一个或者多个称之为Makefile 文件的编写。
--引用某du
根据上面黄色标注的文字,你觉得需要学习Makefile吗?
《概念篇》
Makefile的概念
一个工程中的源文件不计其数,其按类型、功能、模块分别放在若干个目录中,Makefile定义了一系列的规则来指定哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为 Makefile就像一个Shell脚本一样,也可以执行操作系统的命令。
Makefile的命名规则
默认的情况下,make命令会在当前目录下按顺序找寻文件名为“GNUmakefile”、 “makefile”、“Makefile”的文件,在这三个文件名中,最好使用“Makefile”。因为,这个文件名第一个字符为大写,这样有一种显目的感觉。最好不要用“GNUmakefile”,是GNU的make识别的。有另外一些make只对全小写的“makefile”文件名敏感,大多数的make都支持“makefile”和“Makefile”这两种默认文件名。
Makefile也可以为其他名字,比如makefile.linux,但你需要使用make的参数(-f or --file)制定对应的文件,例如:
代码语言:javascript复制make -f makefile.linux
Makefile的规则
Makefile的规则:
代码语言:javascript复制target: prerequisites
command
- target:可以是一个object file(目标文件),也可以是一个执行文件,还可以是一个标签(label)。
- prerequisites:生成该target所依赖的文件和/或target
- command:该target要执行的命令
上面三者的关系:target这一个或多个的目标文件依赖于prerequisites中的文件, 其生成规则定义在command中。
举个例子,比如我们平时要编译一个文件:
代码语言:javascript复制gcc main.c -o main
换成Makefile的书写格式:
代码语言:javascript复制main:main.c
gcc main.c -o main
Makefile的工作流程
- 执行make会在当前目录下找名字叫“Makefile” or “makefile”的文件。
- 如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到“main”这个 文件,并把这个文件作为最终的目标文件。
- 如果main文件不存在,或是main所依赖的后面的
.o
文件的文件修改时间要比main
这个 文件新,那么,他就会执行后面所定义的命令来生成main这个文件。 - 如果main所依赖的
.o
文件也不存在,那么make会在当前文件中找目标为.o
文件 的依赖性,如果找到则再根据那一个规则生成.o
文件。(这有点像一个堆栈的过程) - 当然,你的C文件和H文件是存在的啦,于是make会生成
.o
文件,然后再用.o
文件生 成make的终极任务,也就是执行文件main
了。
执行make的依赖性,make会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件。在执行过程中,如果出现错误,比如被依赖的文件找不到,那么make就会直接退出,并报错,而对于所 定义的命令的错误,或是编译不成功,make根本不理。make只管文件的依赖性。
在上述黄色自体中,可以明确的是,make执行时,它会校验依赖文件的更性时间,如果目标文件跟依赖文件时间一致,则不会相应的命令。
Makefile包含什么内容
Makefile里主要包含了五个东西:显式规则、隐晦规则、变量定义、文件指示和注释。
- 显式规则。显式规则说明了如何生成一个或多个目标文件。这是由Makefile的书写者明显指出要生成的 文件、文件的依赖文件和生成的命令。
- 隐晦规则。由于我们的make有自动推导的功能,所以隐晦的规则可以让我们比较简略地书写 Makefile,这是由make所支持的。
- 变量的定义。在Makefile中我们要定义一系列的变量,变量一般都是字符串,这个有点像你C语言中的 宏,当Makefile被执行时,其中的变量都会被扩展到相应的引用位置上。
- 文件指示。其包括了三个部分,一个是在一个Makefile中引用另一个Makefile,就像C语言中 的include一样;另一个是指根据某些情况指定Makefile中的有效部分,就像C语言中的预编译#if一 样;还有就是定义一个多行的命令。
- 注释。Makefile中只有行注释,和UNIX的Shell脚本一样,其注释是用
#
字符,这个就 像C/C 中的//
一样。如果你要在你的Makefile中使用#
字符,可以用反斜杠进行 转义,如:#
。
make的工作方式
make的执行步骤如下:
- 读入所有的Makefile。
- 读入被include的其它Makefile。
- 初始化文件中的变量。
- 推导隐晦规则,并分析所有规则。
- 为所有的目标文件创建依赖关系链。
- 根据依赖关系,决定哪些目标要重新生成。
- 执行生成命令。
Makefile分为多个章节进行书写,有些概念描述参考与某些资料。