Makefile 基础
简述
什么是 makefile
一个工程中的源文件不计其数,其按类型、功能、模块分别放在若干个目录中,makefile 定义了一系列的规则来指定哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为 makefile 就像一个 Shell 脚本一样,也可以执行操作系统的命令。(摘自百度百科)
简单来说就是面对大型项目中的源文件手动一个个执行编译太过繁琐,建立 makefile 文件进行一键执行,是 Linux 下进行开发的重要工具。
当然 Windows 下就几乎用不到,因为有很多方便的 IDE。
编译和链接
无论是 C 语言还是 C++,都需要把源文件先编译成中间目标文件,就是.o
文件即 ObjectFile,这个动作就是编译。然后再把相关中间目标文件合成执行文件,这个动作叫做链接。
makefile
make
命令执行时,需要一个 makefile 文件,告诉 make
命令该以什么规则去编译源文件。
举一个例子,我们有一个工程需要编译,可能会出现以下方案:
- 该工程没有被编译过,所有的 c 文件都要进行编译和链接
- 该工程只有一部分 c 文件被修改,需要重新编译
- 该工程的头文件改变,需要编译引用了这个头文件的源文件并链接目标程序
只要定义好了 makefile 文件,我们就只需要执行一次 make
命令就可以方便的完成编译任务。
make 命令运行方式
- 读入所有的 Makefile。
- 读入被 include 的其它 Makefile。
- 初始化文件中的变量。
- 推导隐晦规则,并分析所有规则。
- 为所有的目标文件创建依赖关系链。
- 根据依赖关系,决定哪些目标要重新生成。
- 执行生成命令。
1-5 步为第一个阶段,6-7 为第二个阶段。第一个阶段中,如果定义的变量被使用了,那么,make 会把其展 开在使用的位置。但 make 并不会完全马上展开,make 使用的是拖延战术,如果变量出现在依赖关系的规则 中,那么仅当这条依赖被决定要使用了,变量才会在其内部展开。
语法
1 | target:prerequisites |
- target:可以是目标文件、执行文件、标签
- prerequisites:生成该 target 所依赖的文件和 / 或 target
- command:该 target 要执行的命令
这是一个文件的依赖关系,也就是说,target 这一个或多个的目标文件依赖于 prerequisites 中的文件, 其生成规则定义在 command 中。说白一点就是说:
prerequisites 中如果有一个以上的文件比 target 文件要新的话,command 所定义的命令就会被执行。
这就是 makefile 的规则,也是核心内容。
举例
1 | foo.o: foo.c defs.h |
foo.o
是我们的目标, foo.c
和 defs.h
是目标所依赖的源文件,而只有一个命令 cc -c -g foo.c
(以 Tab 键开头)。这个 规则告诉我们两件事:
- 文件的依赖关系,
foo.o
依赖于foo.c
和defs.h
的文件,如果foo.c
和defs.h
的文件日期要比foo.o
文件日期要新,或是foo.o
不存在,那么依赖 关系发生。 - 生成或更新
foo.o
文件,就是那个 cc 命令。它说明了如何生成foo.o
这个文件。 (当然,foo.c
文件include
了defs.h
文件)
使用变量
如果工程中源文件较多,新增或者删除文件就容易弄混,就可以使用变量来定义:
1 | object=main.o aaa.o bbb.o ccc.o ddd.o |
然后就可以使用 $(object)
来使用变量:
1 | object=main.o aaa.o bbb.o ccc.o ddd.o |