Linux操作系统从入门到实战(九)Linux开发工具(中)自动化构建-make/Makefile
- 前言
- 一、 make/Makefile是什么?
- 1. 我们先想个问题:手动编译代码有多麻烦?
- 2. 为了解决麻烦,才有了自动化工具
- 3. 什么是 Makefile ?
- 4. 什么是 make ?
- 二、如何使用 make/Makefile
- 1. 如何创建第一个Makefile?
- (1) 创建源文件
- (2) 创建Makefile文件
- (3) 编写Makefile规则
- (4) 执行make命令
- 2. 依赖关系与命令
- (1) 依赖关系
- (2) make的执行逻辑
- (3) 为什么需要依赖关系?
- 3. 如何清理编译结果?
- (1) 添加clean目标
- (2) 执行清理命令
- (3) .PHONY 标签的作用
- 4. Makefile的执行规则
- (1) 默认执行第一个目标
- (2) 如何执行其他目标?
- (3) 多个目标的执行顺序
- 5. 常见错误与注意事项
- (1) 命令行必须以Tab键开头
- (2) 文件名大小写敏感
- 三、伪目标是什么,为什么伪目标总被执行?
- 1. make如何判断是否需要重新编译?
- 2. 什么是伪目标(.PHONY)?
- (1)定义:
- (2) 常见用途:
- 3. 为什么伪目标总是被执行?
- (1)核心原因:
- (2) 对比实验:
- 4. 为什么需要伪目标?
- (1) 避免与文件冲突
- (2) 强制执行命令
- 5. 常见伪目标示例
- 1. clean目标(最常见)
- 2. all目标(批量编译多个程序)
- 3. install目标(安装程序到系统)
前言
- 前面的博客里我们讲解了vim编译,g++/gcc编译的步骤和动静态链接与库的相关知识
- 接下来我们继续讲解Linux开发工具自动化构建-make/Makefile
我的个人主页,欢迎来阅读我的其他文章
https://blog.csdn.net/2402_83322742?spm=1011.2415.3001.5343
我的Linux知识文章专栏
欢迎来阅读指出不足
https://blog.csdn.net/2402_83322742/category_12879535.html?spm=1001.2014.3001.5482
一、 make/Makefile是什么?
1. 我们先想个问题:手动编译代码有多麻烦?
- 假设你写了一个小项目,里面有好几个
.c
源文件(比如a.c
、b.c
、main.c
)。 - 要让它们变成能运行的程序,得用
gcc
编译,比如:
gcc a.c b.c main.c -o myprogram
这看起来简单,但如果项目变大呢?
-
假设有 100 个源文件,每次都手动敲这串命令,累不说,还容易输错;
-
如果只改了
a.c
,手动编译时还得重新编译所有文件,浪费时间; -
不同文件有依赖关系(比如
main.c
要用a.c
的结果),手动编译得记清顺序,错一步就失败。
2. 为了解决麻烦,才有了自动化工具
就像做复杂的菜时,你需要一份「步骤清单」(先切菜、再炒肉、最后炖),再找个「帮手」按清单一步步做。
在编程里,「步骤清单」就是 Makefile
,「帮手」就是 make
。
3. 什么是 Makefile ?
Makefile
是一个我们自己创建的文件(名字就叫 Makefile
,没有后缀),里面写的是「编译规则」。
比如在一个 Makefile 里,你可以在里面写下:
现在看一下代码,后面会详细讲
# 规则1:最终要生成的程序叫 myprogram,它依赖 a.o、b.o、main.o
myprogram: a.o b.o main.ogcc a.o b.o main.o -o myprogram # 生成 myprogram 的命令# 规则2:a.o 依赖 a.c
a.o: a.cgcc -c a.c # 把 a.c 编译成 a.o(中间文件)# 规则3:b.o 依赖 b.c
b.o: b.cgcc -c b.c# 规则4:main.o 依赖 main.c
main.o: main.cgcc -c main.c
简单说,Makefile
就像一份「菜谱」:
- 明确最终要做什么(比如
myprogram
这个程序); - 明确需要哪些「原材料」(比如
a.o
这些中间文件); - 明确每一步怎么做(比如用什么命令编译,先编译哪个、后编译哪个)。
4. 什么是 make ?
make
是 Linux 自带的一个命令工具(就像 ls
、cd
一样,输入 make
就能运行)。
- 它的作用很单纯:读
Makefile
里的规则,然后自动执行命令。
比如刚才写好了 Makefile
,你在终端敲一句 make
,它就会:
- 先看最终要生成
myprogram
,发现需要a.o
、b.o
、main.o
; - 检查这些
.o
文件有没有,如果没有,就按规则编译a.c
生成a.o
,以此类推; - 最后把所有
.o
文件拼起来,生成myprogram
。
如果之后我们只改了 a.c
,再敲 make
时,它会聪明地只重新编译 a.o
和 myprogram
,其他没改的文件不碰——省时间!
二、如何使用 make/Makefile
1. 如何创建第一个Makefile?
(1) 创建源文件
首先,创建一个简单的C语言程序code.c
:
// code.c
#include <stdio.h>
int main() {printf("Hello, Makefile!\n");return 0;
}
(2) 创建Makefile文件
在相同目录下创建Makefile
文件(注意大小写和文件名):
touch Makefile
vim Makefile
(3) 编写Makefile规则
在Makefile
中写入以下内容:
# 目标: 依赖文件
code: code.cgcc -o code code.c # 注意这行必须以Tab键开头
(4) 执行make命令
在终端输入make
,它会自动读取Makefile
并执行编译:
$ make
gcc -o code code.c
此时,当前目录下会生成可执行文件code
,运行它:
$ ./code
2. 依赖关系与命令
(1) 依赖关系
在Makefile中,每行规则的基本格式是:
目标文件: 依赖文件列表命令1命令2...
- 目标文件:要生成的文件(如
code
) - 依赖文件:生成目标所需要的文件(如
code.c
) - 命令:生成目标的具体操作(如
gcc -o code code.c
)
(2) make的执行逻辑
当你执行make
时,它会:
- 检查目标文件是否存在
- 如果不存在,或依赖文件比目标文件更新(即依赖文件被修改过)
- 则执行对应的命令来生成/更新目标文件
(3) 为什么需要依赖关系?
假设你修改了code.c
,再次执行make
时:
$ make
make: 'code' is up to date.
make
会自动判断:由于code
已存在且code.c
未修改,因此无需重新编译——这就是Makefile
的智能编译能力,大大节省时间!
3. 如何清理编译结果?
(1) 添加clean目标
在Makefile
中新增一个clean
目标:
code: code.cgcc -o code code.c.PHONY: clean
clean:rm -f code # 删除生成的可执行文件
(2) 执行清理命令
当需要删除编译结果时,执行:
$ make clean
rm -f code
(3) .PHONY 标签的作用
.PHONY
用于声明clean
是一个伪目标(即它不是真正的文件名),防止目录中真的存在名为clean
的文件导致冲突。
4. Makefile的执行规则
(1) 默认执行第一个目标
当你直接输入make
时,它会执行Makefile中的第一个目标(如上面的code
)。
(2) 如何执行其他目标?
通过指定目标名来执行特定规则,例如:
$ make clean # 只执行clean目标下的命令
$ make code # 只执行code目标下的命令(等同于直接make)
(3) 多个目标的执行顺序
Makefile会按照依赖关系自动处理执行顺序。例如:
all: program1 program2program1: file1.cgcc -o program1 file1.cprogram2: file2.cgcc -o program2 file2.c
执行make all
时,会先编译program1
,再编译program2
。
5. 常见错误与注意事项
(1) 命令行必须以Tab键开头
在Makefile中,命令行必须以Tab键开头,否则会报错:
# 错误示例(使用空格缩进)
code: code.cgcc -o code code.c # 错误!会提示"*** missing separator. Stop."# 正确示例(使用Tab键缩进)
code: code.cgcc -o code code.c
(2) 文件名大小写敏感
Makefile
和makefile
是不同的文件,GNU make默认优先读取GNUmakefile
,然后是makefile
,最后是Makefile
。建议统一使用Makefile
。
三、伪目标是什么,为什么伪目标总被执行?
1. make如何判断是否需要重新编译?
当我们执行make
时,它会检查两件事:
- 目标文件(如
code
)是否存在 - 依赖文件(如
code.c
)的修改时间是否比目标文件更新
如果目标文件不存在,或依赖文件被修改过(修改时间比目标文件晚),make
就会执行命令重新生成目标文件。
举个例子
# Makefile
code: code.cgcc -o code code.c
- 第一次执行
make
:生成code
文件 - 第二次执行
make
:make
发现code
已存在,且code.c
没修改,==“code”已是最新 ==
$ make
gcc -o code code.c # 第一次执行,生成code
$ make
make: “code”已是最新
2. 什么是伪目标(.PHONY)?
(1)定义:
伪目标是用.PHONY
声明的目标,它不代表一个真实的文件,而是一个动作名称。
(2) 常见用途:
- 清理编译结果(如
clean
目标) - 执行测试(如
test
目标) - 批量操作(如
all
目标)
示例:
.PHONY: clean # 声明clean是伪目标
clean:rm -f code # 删除生成的可执行文件
3. 为什么伪目标总是被执行?
(1)核心原因:
伪目标不对应真实文件,因此make
无法检查它的修改时间。
当你执行make 伪目标名
时,make
会直接执行对应的命令,而不做任何检查。
(2) 对比实验:
情况1:普通目标(无.PHONY)
hello:echo "Hello, World!"
执行效果:
$ make hello
echo "Hello, World!"
Hello, World!
$ make hello
make: 'hello' is up to date. # 第二次不会执行,因为默认认为hello是文件且已存在
情况2:伪目标(有.PHONY)
.PHONY: hello
hello:echo "Hello, World!"
执行效果:
$ make hello
echo "Hello, World!"
Hello, World!
$ make hello
echo "Hello, World!" # 每次都会执行!因为hello是伪目标
Hello, World!
4. 为什么需要伪目标?
(1) 避免与文件冲突
如果目录中真的有一个名为clean
的文件,会发生什么?
# 错误示例:没有.PHONY的clean目标
clean:rm -f code
此时执行make clean
,make
会认为:“clean
文件已存在,无需执行任何命令”——这显然不是你想要的!
(2) 强制执行命令
对于clean
、test
这类目标,你总是希望它们无条件执行,而不是根据文件时间判断。
5. 常见伪目标示例
1. clean目标(最常见)
.PHONY: clean
clean:rm -f *.o # 删除所有.o文件rm -f executable # 删除可执行文件
2. all目标(批量编译多个程序)
.PHONY: all
all: program1 program2 # 先编译program1,再编译program2program1: file1.cgcc -o program1 file1.cprogram2: file2.cgcc -o program2 file2.c
3. install目标(安装程序到系统)
.PHONY: install
install:cp program /usr/bin/ # 复制程序到系统目录
以上就是这篇博客的全部内容,下一篇我们将继续探索Linux的更多精彩内容。
我的个人主页
欢迎来阅读我的其他文章
https://blog.csdn.net/2402_83322742?spm=1011.2415.3001.5343
我的Linux知识文章专栏
欢迎来阅读指出不足
https://blog.csdn.net/2402_83322742/category_12879535.html?spm=1001.2014.3001.5482
非常感谢您的阅读,喜欢的话记得三连哦 |