> For the complete documentation index, see [llms.txt](https://inityun.gitbook.io/niao-ge-de-linux-si-fang-cai-1/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://inityun.gitbook.io/niao-ge-de-linux-si-fang-cai-1/chapter_21/21.3-yong-make-jin-hang-hong-bian-yi.md).

# 21.3 用make进行宏编译

在本章一开始我们提到过 make 的功能是可以简化编译过程里面所下达的指令，同时还具有很多很方便的功能！那么下面咱们就来试看看使用 make 简化下达编译指令的流程吧！

## 21.3.1 为什么要用 make

先来想像一个案例，假设我的可执行文件里面包含了四个源代码文件，分别是 main.c haha.c sin\_value.c cos\_value.c 这四个文件，这四个文件的目的是：

* main.c ：主要的目的是让使用者输入角度数据与调用其他三支副程序；
* haha.c ：输出一堆有的没有的讯息而已；
* sin\_value.c ：计算使用者输入的角度（360） sin 数值；
* cos\_value.c ：计算使用者输入的角度（360） cos 数值。

这四个文件你可以到 <http://linux.vbird.org/linux_basic/0520source/main.tgz> 来下载。由于这四个文件里面包含了相关性，并且还用到数学函数在里面，所以如果你想要让这个程序可以跑， 那么就需要这样编译：

```shell
# 1\. 先进行目标文件的编译，最终会有四个 *.o 的文件名出现：
[root@study ~]# gcc -c main.c
[root@study ~]# gcc -c haha.c
[root@study ~]# gcc -c sin_value.c
[root@study ~]# gcc -c cos_value.c

# 2\. 再进行链接成为可执行文件，并加入 libm 的数学函数，以产生 main 可执行文件：
[root@study ~]# gcc -o main main.o haha.o sin_value.o cos_value.o -lm

# 3\. 本程序的执行结果，必须输入姓名、360 度角的角度值来计算：
[root@study ~]# ./main 
Please input your name: VBird  <==这里先输入名字
Please enter the degree angle （ex> 90）: 30   <==输入以 360 度角为主的角度
Hi, Dear VBird, nice to meet you.    <==这三行为输出的结果喔！
The Sin is:  0.50
The Cos is:  0.87
```

编译的过程需要进行好多动作啊！而且如果要重新编译，则上述的流程得要重新来一遍，光是找出这些指令就够烦人的了！ 如果可以的话，能不能一个步骤就给他完成上面所有的动作呢？那就利用 make 这个工具吧！ 先试看看在这个目录下创建一个名为 makefile 的文件，内容如下：

```shell
# 1\. 先编辑 makefile 这个规则档，内容只要作出 main 这个可执行文件
[root@study ~]# vim makefile
main: main.o haha.o sin_value.o cos_value.o
    gcc -o main main.o haha.o sin_value.o cos_value.o -lm
# 注意：第二行的 gcc 之前是 <tab> 按键产生的空格喔！

# 2\. 尝试使用 makefile 制订的规则进行编译的行为：
[root@study ~]# rm -f main *.o   <==先将之前的目标文件去除
[root@study ~]# make
cc    -c -o main.o main.c
cc    -c -o haha.o haha.c
cc    -c -o sin_value.o sin_value.c
cc    -c -o cos_value.o cos_value.c
gcc -o main main.o haha.o sin_value.o cos_value.o -lm
# 此时 make 会去读取 makefile 的内容，并根据内容直接去给他编译相关的文件啰！

# 3\. 在不删除任何文件的情况下，重新执行一次编译的动作：
[root@study ~]# make
make: `main' is up to date.
# 看到了吧！是否很方便呢！只会进行更新 （update） 的动作而已。
```

或许你会说：“如果我创建一个 shell script 来将上面的所有动作都集结在一起，不是具有同样的效果吗？”呵呵！ 效果当然不一样，以上面的测试为例，我们仅写出 main 需要的目标文件，结果 make 会主动的去判断每个目标文件相关的源代码文件，并直接予以编译，最后再直接进行链接的动作！ 真的是很方便啊！此外，如果我们更动过某些源代码文件，则 make 也可以主动的判断哪一个源代码与相关的目标文件文件有更新过， 并仅更新该文件，如此一来，将可大大的节省很多编译的时间呢！要知道，某些程序在进行编译的行为时，会消耗很多的 CPU 资源呢！所以说， make 有这些好处：

* 简化编译时所需要下达的指令；
* 若在编译完成之后，修改了某个源代码文件，则 make 仅会针对被修改了的文件进行编译，其他的 object file 不会被更动；
* 最后可以依照相依性来更新 （update） 可执行文件。

既然 make 有这么多的优点，那么我们当然就得好好的了解一下 make 这个令人关心的家伙啦！而 make 里面最需要注意的大概就是那个规则文件，也就是 makefile 这个文件的语法啦！所以下面我们就针对 makefile 的语法来加以介绍啰。

## 21.3.2 makefile 的基本语法与变量

make 的语法可是相当的多而复杂的，有兴趣的话可以到 GNU 去查阅相关的说明，鸟哥这里仅列出一些基本的规则，重点在于让读者们未来在接触源代码时，不会太紧张啊！ 好了，基本的 makefile 规则是这样的：

```shell
标的（target）: 目标文件1 目标文件2
<tab>   gcc -o 欲创建的可执行文件 目标文件1 目标文件2
```

那个标的 （target） 就是我们想要创建的信息，而目标文件就是具有相关性的 object files ，那创建可执行文件的语法就是以 \<tab> 按键开头的那一行！特别给他留意喔，“命令列必须要以 tab 按键作为开头”才行！他的规则基本上是这样的：

* 在 makefile 当中的 # 代表注解；
* \<tab> 需要在命令行 （例如 gcc 这个编译器指令） 的第一个字符；
* 标的 （target） 与相依文件（就是目标文件）之间需以“:”隔开。

同样的，我们以刚刚上一个小节的范例进一步说明，如果我想要有两个以上的执行动作时， 例如下达一个指令就直接清除掉所有的目标文件与可执行文件，该如何制作呢？

```shell
# 1\. 先编辑 makefile 来创建新的规则，此规则的标的名称为 clean ：
[root@study ~]# vi makefile
main: main.o haha.o sin_value.o cos_value.o
    gcc -o main main.o haha.o sin_value.o cos_value.o -lm
clean:
    rm -f main main.o haha.o sin_value.o cos_value.o

# 2\. 以新的标的 （clean） 测试看看执行 make 的结果：
[root@study ~]# make clean  <==就是这里！通过 make 以 clean 为标的
rm -rf main main.o haha.o sin_value.o cos_value.o
```

如此一来，我们的 makefile 里面就具有至少两个标的，分别是 main 与 clean ，如果我们想要创建 main 的话，输入“make main”，如果想要清除有的没的，输入“make clean”即可啊！而如果想要先清除目标文件再编译 main 这个程序的话，就可以这样输入：“make clean main”，如下所示：

```shell
[root@study ~]# make clean main
rm -rf main main.o haha.o sin_value.o cos_value.o
cc    -c -o main.o main.c
cc    -c -o haha.o haha.c
cc    -c -o sin_value.o sin_value.c
cc    -c -o cos_value.o cos_value.c
gcc -o main main.o haha.o sin_value.o cos_value.o -lm
```

这样就很清楚了吧！但是，你是否会觉得，咦！ makefile 里面怎么重复的数据这么多啊！没错！所以我们可以再借由 shell script 那时学到的“变量”来更简化 makefile 喔：

```shell
[root@study ~]# vi makefile
LIBS = -lm
OBJS = main.o haha.o sin_value.o cos_value.o
main: ${OBJS}
        gcc -o main ${OBJS} ${LIBS}
clean:
        rm -f main ${OBJS}
```

与 \[bash shell script] 的语法有点不太相同，变量的基本语法为：

1. 变量与变量内容以“=”隔开，同时两边可以具有空格；
2. 变量左边不可以有 \<tab> ，例如上面范例的第一行 LIBS 左边不可以是 \<tab>；
3. 变量与变量内容在“=”两边不能具有“:”；
4. 在习惯上，变量最好是以“大写字母”为主；
5. 运用变量时，以 ${变量} 或 $（变量） 使用；
6. 在该 shell 的环境变量是可以被套用的，例如提到的 CFLAGS 这个变量！
7. 在命令行界面也可以给予变量。

由于 gcc 在进行编译的行为时，会主动的去读取 CFLAGS 这个环境变量，所以，你可以直接在 shell 定义出这个环境变量，也可以在 makefile 文件里面去定义，更可以在命令行当中给予这个咚咚呢！例如：

```shell
[root@study ~]# CFLAGS="-Wall" make clean main
# 这个动作在上 make 进行编译时，会去取用 CFLAGS 的变量内容！
```

也可以这样：

```shell
[root@study ~]# vi makefile
LIBS = -lm
OBJS = main.o haha.o sin_value.o cos_value.o
CFLAGS = -Wall
main: ${OBJS}
    gcc -o main ${OBJS} ${LIBS}
clean:
    rm -f main ${OBJS}
```

咦！我可以利用命令行进行环境变量的输入，也可以在文件内直接指定环境变量，那万一这个 CFLAGS 的内容在命令行与 makefile 里面并不相同时，以那个方式输入的为主？呵呵！问了个好问题啊！ 环境变量取用的规则是这样的：

1. make 命令行后面加上的环境变量为优先；
2. makefile 里面指定的环境变量第二；
3. shell 原本具有的环境变量第三。

此外，还有一些特殊的变量需要了解的喔：

* $@：代表目前的标的（target）

所以我也可以将 makefile 改成：

```shell
[root@study ~]# vi makefile
LIBS = -lm
OBJS = main.o haha.o sin_value.o cos_value.o
CFLAGS = -Wall
main: ${OBJS}
    gcc -o $@ ${OBJS} ${LIBS}   <==那个 $@ 就是 main ！
clean:
    rm -f main ${OBJS}
```

这样是否稍微了解了 makefile （也可能是 Makefile） 的基本语法？这对于你未来自行修改源代码的编译规则时，是很有帮助的喔！^\_^！


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://inityun.gitbook.io/niao-ge-de-linux-si-fang-cai-1/chapter_21/21.3-yong-make-jin-hang-hong-bian-yi.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
