Make

Make是一个在软件开发中所使用的构建工具，用于自动化建构软件。它通过一个名为 Makefile 的文本文件来描述源代码文件之间的依赖关系和构建规则。Make 会根据这些规则和依赖关系，判断哪些文件需要重新编译，并执行相应的编译命令，以确保最终生成可执行文件或其他目标文件（这些目标被称为“target”）。大多数情况下，它被用来编译源代码，生成结果代码，然后把结果代码连接起来生成可执行文件或者库文件。

许多现代软件的开发环境中（如Microsoft Visual Studio），集成开发环境已经取代make，但是在Unix环境中，仍然有许多工程师采用make来协助软件开发。

## 起源

目前虽有众多依赖关系检查工具，但是make是应用最广泛的一个。这要归功于它被包含在Unix系统中。斯图亚特·费尔德曼（Stuart Feldman）在1977年在贝尔实验室（Bell Labs）里制作了这个软件。2003年，斯图亚特·费尔德曼因发明了这样一个重要的工具而接受了美国计算机协会（ACM）颁发的软件系统奖。

在make诞生之前，编译工作主要依赖于操作系统里面的类似于“make”、“install”功能的shell脚本。它可以批量执行生成目标的命令，并且可以完成依赖关系的检查。这是向现代编译环境发展的重要一步。

## 不同版本

make程序已被使用者多次重/改写，其中包括几次用相同的文件格式和算法原理重新编写，并且依照不同需要添加了一些不常见的改良。

### GNU make

GNU make仿照make的标准功能（透过clean-room工程）重新改写，并加入作者觉得值得加入的新功能，常和GNU编译系统一起被使用，是大多数GNU Linux安装的一部分。

### BSD make

BSD make是从Adam de Boor的制作的版本上发展成。它编译目标的时候有并行计算的能力。它在FreeBSD，NetBSD和OpenBSD中不同程度的修改下存活了下来。

### Microsoft nmake

广泛应用于微软的Windows，微软的nmake是 Visual Studio 随附的命令行工具，不要与来自AT&T和贝尔实验室的Unix系统nmake混淆。

## 优点和缺点

就像其他和make有着悠久历史的软件一样，make有着很多的拥护者和反对者。它的很多问题因现代大型的软件项目的出现而暴露出来。但是很多人争论说它在常见的情况下可以很好的工作，而且使用非常的简单，功能强大，表达清楚。无论如何，make仍然被用来编译很多完整的操作系统，而且现在替代品们在基本的操作上与它没有太大差别。

随着现代的集成开发环境（IDE）的诞生，特别是非Unix的平台上，很多程序员不再手动管理依靠关系检查，甚至不用去管哪些文件是这个项目的一部分，而是把这些任务交给了他们的开发环境去做。类似的，很多现代的编程语言有自己特别的高效的依赖关系的设置方法。

## Makefile

### 规则

makefile的格式是：

```
   # 用“#”号表明注释。
   target（要生成的文件）: dependencies（被依赖的文件）
   	# 命令前面用的是“tab”而非空格。误用空格是初学者容易犯的错误！
   TAB命令1
   TAB命令2
   TAB命令3
         .
   	  .
   	  .
   TAB命令n
   # 可以使用“\”表示续行。注意，“\”之后不能有空格！
```

- target, dependencies和命令构成了一个makefile里的一个“规则”，规则指示make何时以及如何重新生成target或执行target下的命令
- target通常是我们要生成的文件的名字，摆放的顺序不重要，但第一个target是默认的target。当make不带参数时，自动执行第一个target。target也可以是要求make完成的动作，执行这种target后并不能得到和target同名的文件，因此，也称为伪target(phony target)。
- dependencies是生成target所需的文件名列表。依赖可以为空，常用的“clean”target就常常没有依赖，只有命令。若依赖不为空，则make会先检查依赖的“规则”。依赖规定了何时重新执行target下命令。若任何依赖比target更新 （由于执行了依赖的“规则”的命令或用户修改了依赖），make则会重新执行target下的命令。
- 命令可以是任何一个shell能运行的命令。

### 示例

举例来说明makefile的结构和make如何运作。

```make
editor: main.o text.o
	gcc -o editor main.o text.o
main.o: main.c def.h
	gcc -c main.c
text.o: text.c com.h
	gcc -c text.c
install:editor
	mv editor /usr/local
```

当我们输入：

```
<nowiki>
make
或者
make editor
</nowiki>
```

当editor这个target文件不存在，或者main.o、text.o这两个依赖文件被修改，都会导致make调用其下的命令“gcc -o editor main.o text.o”；接下来，由于引用到main.o和text.o，make会检查main.o的依赖main.c、def.h有无更新，如果有，则执行其下的命令“gcc -c main.c”；同样的道理，也适用于text.o。

于是，可有几种不同的输出：

```
<nowiki>
gcc -c main.c
gcc -c text.c
gcc -o editor main.o text.o
</nowiki>
```

- main.c或/和def.h有修改：

```
<nowiki>
gcc -c main.c
gcc -o editor main.o text.o
</nowiki>
```

- text.c或/和com.h有修改：

```
<nowiki>
gcc -c text.c
gcc -o editor main.o text.o
</nowiki>
```

- main.c和text.c均有修改：

```
<nowiki>
gcc -c main.c
gcc -c text.c
gcc -o editor main.o text.o
</nowiki>
```

当我们输入：

```
<nowiki>
make install
</nowiki>
```

make会检查install的依赖editor是否是最新，如果是，则执行其下的命令“mv editor /usr/local”。由于这个过程并没有产生名为“install”的文件，所以，install是一个假目标。

### 宏

“宏”指的是用一个字符串代替另一个字符串的功能。在makefile中可以使用“=”号来定义宏，使用“$（宏名）”来使用宏；还可以用“+=”追加宏的内容。习惯上，宏名使用大写。承接上面的例子：

```make
OBJECTS = main.o text.o
INSTALL_PATH = /usr/local
editor: $(OBJECTS)
	gcc -o editor $(OBJECTS)
main.o: main.c
	gcc -c main.c
text.o: text.c
	gcc -c text.c
install:editor
	mv editor $(INSTALL_PATH)
```

## 基本资料

- 编程范型：宏, 宣告式编程
- 设计者：斯图亚特·费尔德曼
- 发行日期：1976年
- 实作语言：C 语言
- 操作系统：类 Unix 系统
- 档案格式：Makefile