> For the complete documentation index, see [llms.txt](https://oscourse-tsinghua.gitbook.io/loongsoncsprj2020-manual/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://oscourse-tsinghua.gitbook.io/loongsoncsprj2020-manual/ucore/make.md).

# 3.1 make 命令的使用

[make](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/make.html)为UNIX系统下的一个工具程序，可以根据预先准备的makefile计算文件间的依赖关系，由源文件生成目标并保证目标的版本不落后与源文件，大量用于软件构建。[GNU make](https://www.gnu.org/software/make/)为make的一种实现，并在标准之上增加了许多额外功能。 [文档](https://www.gnu.org/software/make/manual/html_node/index.html)

## 必要知识

Makefile中文件的生成规则由运行环境的命令行解释器执行。在多数UNIX系统上，该解释器为`/bin/sh`。除此之外，生成中使用到的命令也需要分别指定参数。因此，首先需要对Shell以及所使用的构建工具有基本的了解。

## 基础

### make与makefile

Makefile中包括文件间的相互关系，说明了如何使用工具从一个文件生成另一个文件。在目录下执行`make`命令并不指定`makefile`时，make将会搜索合适的文件（文件名为`makefile`或`Makefile`）并作为makefile读取。因此，一般把它取名为`Makefile`，置于需构建的项目目录。

make执行时，首先读取makefile，解析其中的规则。若在命令行上指定了构建的目标，make则会尝试生成该目标；否则，make一般会选择文件中的第一个目标进行生成。

通过传给make `-j <任务数>`参数，可以使make同时构建多个目标，加快构建速度。

### 目标规则

目标规则指定了生成一个文件时所需要的其他文件和所运行的命令。生成规则的一般结构为：

```
target: [prereq...]
[<tab>    command...]
```

其中，`target`为该规则的“目标”，亦即所需要生成的文件名；`prereq`为生成`target`的所有依赖，可以为其他目标或文件；`command`为生成`target`所要执行的一系列命令（在所有的命令左侧都必须有一个Tab）。

make生成一个目标时，首先会生成它依赖中的所有目标，然后执行命令进行该目标的生成。但若一规则的目标文件存在并且其修改时间晚于任何一个依赖，则该规则未过期，生成该目标时make就会跳过命令执行阶段。生成依赖目标时，make一般会同时进行多个目标的生成。

### 特殊目标

在makefile中，有一部分特殊的目标并不对应生成的文件，而是用来进行配置。这类目标一般以`.`开头。

* `.PHONY`：该目标声明其所有依赖均不对应生成的文件
* `.MAIN`：该目标存在时，make将其作为默认目标
* `.SUFFIXES`: 见后缀规则

### 变量

变量可以出现在makefile的各处。当make遇到变量时，会首先将其展开，然后再继续解析。使用变量的方式为： `$(varname)`或`${varname}`。当变量名仅有一个字符时，括号可以省略。

通过设定变量，Makefile可以将重复的内容省略至一处，也可以在运行时通过执行命令确定规则的细节。设定变量的一般方式为：

```
var <mod>= value
```

其中，`var`为变量名，`value`为需要设定的值。根据符号`mod`的不同，`var`的赋值效果不同。常见的赋值符号有：

* `mod`为空，即`var = value`：以`value`替换`var`的值
* `mod`为`+`，即`var += value`：在`var`后附加`value`
* `mod`为`?`，即`var ?= value`：若`var`之前未定义，则以`value`替换
* `mod`为`!`，即`var != value`：将`value`作为命令执行，然后取其输出替换`var`的值

在规则中，有一部分变量为预定义：

* `$@`：目标的文件名
* `$*`：不含后缀的目标文件名

### 后缀规则

后缀规则用来定义不同类型文件之间的转换规则，以避免针对每一个文件都要明确制定规则的麻烦。使用后缀规则时，首先需要将需要写入规则的文件类型的后缀列在`.SUFFIXES`特殊目标的依赖中，例如：

```
.SUFFIXES: .c .o
```

后缀规则的结构为：

```
sfxrule:
[<tab>    command...]
```

其中，`sfxrule`为转换的两种类型的后缀名的连接，若目标类型无后缀则只用一个类型的后缀。在后缀规则的命令中，可以使用预定义的变量`$<`用来表示该规则的依赖。例如:

```
.c.o:
    $(CC) $(CCFLAGS) -c -o $@ $<

.o:
    $(CC) $(LDFLAGS) -o $@ $<
```
