简述

什么是makefile

一个工程中的源文件不计其数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为 makefile就像一个Shell脚本一样,也可以执行操作系统的命令。(摘自百度百科)

简单来说就是面对大型项目中的源文件手动一个个执行编译太过繁琐,建立makefile文件进行一键执行,是Linux下进行开发的重要工具。

当然Windows下就几乎用不到,因为有很多方便的IDE。

编译和链接

无论是C语言还是C++,都需要把源文件先编译成中间目标文件,就是.o 文件即ObjectFile,这个动作就是编译。然后再把相关中间目标文件合成执行文件,这个动作叫做链接。

makefile

make 命令执行时,需要一个makefile文件,告诉make 命令该以什么规则去编译源文件。

举一个例子,我们有一个工程需要编译,可能会出现以下方案:

  • 该工程没有被编译过,所有的c文件都要进行编译和链接
  • 该工程只有一部分c文件被修改,需要重新编译
  • 该工程的头文件改变,需要编译引用了这个头文件的源文件并链接目标程序

只要定义好了makefile文件,我们就只需要执行一次make 命令就可以方便的完成编译任务。

make命令运行方式

  1. 读入所有的Makefile。
  2. 读入被include的其它Makefile。
  3. 初始化文件中的变量。
  4. 推导隐晦规则,并分析所有规则。
  5. 为所有的目标文件创建依赖关系链。
  6. 根据依赖关系,决定哪些目标要重新生成。
  7. 执行生成命令。

1-5步为第一个阶段,6-7为第二个阶段。第一个阶段中,如果定义的变量被使用了,那么,make会把其展 开在使用的位置。但make并不会完全马上展开,make使用的是拖延战术,如果变量出现在依赖关系的规则 中,那么仅当这条依赖被决定要使用了,变量才会在其内部展开。

语法

1
2
target:prerequisites
commands
  • target:可以是目标文件、执行文件、标签
  • prerequisites:生成该target所依赖的文件和/或target
  • command:该target要执行的命令

这是一个文件的依赖关系,也就是说,target这一个或多个的目标文件依赖于prerequisites中的文件, 其生成规则定义在command中。说白一点就是说:

prerequisites中如果有一个以上的文件比target文件要新的话,command所定义的命令就会被执行。

这就是makefile的规则,也是核心内容。

举例

1
2
foo.o: foo.c defs.h
cc -c -g foo.c

foo.o 是我们的目标, foo.cdefs.h 是目标所依赖的源文件,而只有一个命令 cc -c -g foo.c (以Tab键开头)。这个 规则告诉我们两件事:

  1. 文件的依赖关系, foo.o 依赖于 foo.cdefs.h 的文件,如果 foo.cdefs.h 的文件日期要比 foo.o 文件日期要新,或是 foo.o 不存在,那么依赖 关系发生。
  2. 生成或更新 foo.o 文件,就是那个cc命令。它说明了如何生成 foo.o 这个文件。 (当然,foo.c文件includedefs.h文件)

使用变量

如果工程中源文件较多,新增或者删除文件就容易弄混,就可以使用变量来定义:

1
object=main.o aaa.o bbb.o ccc.o ddd.o

然后就可以使用$(object) 来使用变量:

1
2
3
4
5
6
7
object=main.o aaa.o bbb.o ccc.o ddd.o
edit:$(object)
cc -o edit $(object)

.PHONY:clean
clean:
rm edit $(object)