学习Makefile例子

更详细的内容可以看《跟我一起写Makefile》,由陈皓 编写。这里只是提供学习Makefile的笔记和例子。

1. 概述

1.1 为什么学习Makefile

什么是makefile?或许很多Windows 的程序员都不知道这个东西,因为那些Windows 的集成开发环境(integrated development environment,IDE)都为你做了这个工作,但我觉得要作一个好的和专业的程序员,makefile 还是要懂。

makefile 带来的好处就是——“自动化编译”,一旦写好,只需要一个make 命令,整个工程完全自动编译,极大的提高了软件开发的效率。make 是一个命令工具,是一个解释makefile 中指令的命令工具,一般来说,大多数的IDE 都有这个命令,比如:Delphi 的make,Visual C++ 的nmake,Linux 下GNU 的make。可见,makefile 都成为了一种在工程方面的编译方法。

1.2 关于程序的编译和链接

一般来说,无论是C 还是C++,首先要把源文件编译成中间代码文件,在Windows 下也就是.obj 文件,UNIX 下是.o 文件,即Object File,这个动作叫做编译(compile)。然后再把大量的Object File 合成执行文件,这个动作叫作链接(link)。

一般来说,每个源文件都应该对应于一个中间目标文件(.o 文件或.obj 文件)。链接时,主要是链接函数和全局变量。所以,我们可以使用这些中间目标文件(.o 文件或.obj 文件)来链接我们的应用程序。链接器并不管函数所在的源文件,只管函数的中间目标文件(Object File),在大多数时候,由于源文件太多,编译生成的中间目标文件太多,而在链接时需要明显地指出中间目标文件名,这对于编译很不方便。所以,我们要给中间目标文件打个包,在Windows 下这种包叫“库文件”(Library File),也就是.lib 文件,在UNIX 下,是Archive File,也就是.a 文件。

2. makefile 介绍

make 命令执行时,需要一个makefile 文件,以告诉make 命令需要怎么样的去编译和链接程序。
首先,我们用一个示例来说明makefile 的书写规则,以便给大家一个感性认识。这个示例来源于gnu的make 使用手册,在这个示例中,我们的工程有8 个c 文件,和3 个头文件,我们要写一个makefile来告诉make 命令如何编译和链接这几个文件。我们的规则是:

  1. 如果这个工程没有编译过,那么我们的所有c 文件都要编译并被链接。
  2. 如果这个工程的某几个c 文件被修改,那么我们只编译被修改的c 文件,并链接目标程序。
  3. 如果这个工程的头文件被改变了,那么我们需要编译引用了这几个头文件的c 文件,并链接目标程序。

只要我们的makefile 写得够好,所有的这一切,我们只用一个make 命令就可以完成,make 命令会自动智能地根据当前的文件修改的情况来确定哪些文件需要重编译,从而自动编译所需要的文件和链接目标程序。

注释。Makefile 中只有行注释,和UNIX 的Shell 脚本一样,其注释是用# 字符,这个就像C/C++中的// 一样。如果你要在你的Makefile 中使用# 字符,可以用反斜杠进行转义,如:# 。最后,还值得一提的是,在Makefile 中的命令,必须要以Tab 键开始。

默认的情况下,make 命令会在当前目录下按顺序找寻文件名为“GNUmakefile”、“makefile”、“Makefile”的文件,找到了解释这个文件。在这三个文件名中,最好使用“Makefile”这个文件名,因为,这个文件名第一个字符为大写,这样有一种显目的感觉。

2.1 一个示例

正如前面所说,如果一个工程有3 个头文件和8 个c 文件,为了完成前面所述的那三个规则,我们的makefile 应该是下面的这个样子的。
在这里插入图片描述

反斜杠(\ )是换行符的意思。这样比较便于makefile 的阅读。我们可以把这个内容保存在名字为“makefile”或“Makefile”的文件中,然后在该目录下直接输入命令make 就可以生成执行文件edit。如果要删除执行文件和所有的中间目标文件,那么,只要简单地执行一下make clean 就可以了。在这个makefile 中,目标文件(target)包含:执行文件edit 和中间目标文件(*.o ),依赖文件(prerequisites)就是冒号后面的那些.c 文件和.h 文件。每一个.o 文件都有一组依赖文件,而这些.o文件又是执行文件edit 的依赖文件。依赖关系的实质就是说明了目标文件是由哪些文件生成的,换言之,目标文件是哪些文件更新的。

在定义好依赖关系后,后续的那一行定义了如何生成目标文件的操作系统命令,一定要以一个Tab键作为开头。 记住,make 并不管命令是怎么工作的,他只管执行所定义的命令。make 会比较targets 文件和prerequisites 文件的修改日期,如果prerequisites 文件的日期要比targets 文件的日期要新,或者target 不存在的话,那么,make 就会执行后续定义的命令。

这里要说明一点的是,clean 不是一个文件,它只不过是一个动作名字,有点像c 语言中的label 一样,其冒号后什么也没有,那么,make 就不会自动去找它的依赖性,也就不会自动执行其后所定义的命令。要执行其后的命令,就要在make 命令后明显得指出这个label 的名字。这样的方法非常有用,我们可以在一个makefile 中定义不用的编译或是和编译无关的命令,比如程序的打包,程序的备份,等等。

2.2 make 是如何工作的

在默认的方式下,也就是我们只输入make 命令。那么,

  1. make 会在当前目录下找名字叫“Makefile”或“makefile”的文件。
  2. 如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到“edit”这个文件,并把这个文件作为最终的目标文件。
  3. 如果edit 文件不存在,或是edit 所依赖的后面的.o 文件的文件修改时间要比edit 这个文件新,那么,他就会执行后面所定义的命令来生成edit 这个文件。
  4. 如果edit 所依赖的.o 文件也不存在,那么make 会在当前文件中找目标为.o 文件的依赖性,如果找到则再根据那一个规则生成.o 文件。(这有点像一个堆栈的过程)
  5. 当然,你的C 文件和H 文件是存在的啦,于是make 会生成.o 文件,然后再用.o 文件生成make的终极任务,也就是执行文件edit 了。

2.3 makefile 中使用变量

比如,我们声明一个变量,叫objects ,OBJECTS ,objs ,OBJS ,obj 或是OBJ ,反正不管什么啦,只要能够表示obj 文件就行了。我们在makefile 一开始就这样定义:
在这里插入图片描述
于是,我们就可以很方便地在我们的makefile 中以$(objects) 的方式来使用这个变量了,于是我们的改良版makefile 就变成下面这个样子:
在这里插入图片描述

2.4 让make 自动推导

GNU 的make 很强大,它可以自动推导文件以及文件依赖关系后面的命令,于是我们就没必要去在
每一个.o 文件后都写上类似的命令,因为,我们的make 会自动识别,并自己推导命令。
只要make 看到一个.o 文件,它就会自动的把.c 文件加在依赖关系中,如果make 找到一个
whatever.o ,那么whatever.c 就会是whatever.o 的依赖文件。并且cc -c whatever.c 也会被推导
出来,于是,我们的makefile 再也不用写得这么复杂。我们的新makefile 又出炉了。

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/wokaowokaowokao12345/article/details/127726190
今日推荐