Linux 进阶:一文搞懂 make 工具与 Makefile 编写
目录
前言:
一、先搞懂:make 是什么?Makefile 又是什么?
1.1 背景
1.2 make:Linux 下的自动化构建工具
1.3 Makefile:make 的 “操作手册”
二、最佳实践:先见一下,如何使用
2.1 构建项目
2.2 清理项目
2.3 理解Makefile/make,编译工作的推导过程,依赖关系和依赖方法
2.4 PHONY:伪目标——总被执行
三、ACM时间管理
3.1 重复make,竟然不让我make?
3.2 Access Time(访问时间)
3.3 Modify Time(修改时间)
3.4 Change Time(状态改变时间)
3.5 详解make如何知道code.c是否需要被重新编译
四、makefile最佳实践和实用语法
4.1 $@与$^
4.2 定义变量
4.3 $<和$^
五、总结:为什么一定要学 Makefile?
前言:
如果你在 Linux 下编译过 C/C++ 项目,一定遇到过这样的场景:
修改一个文件后,需要手动输入gcc main.c func.c -o app重新编译,文件多了还容易漏写;如果只改了一个小模块,却要重新编译所有文件 —— 既浪费时间又麻烦。
而make工具和Makefile,就是为解决这个问题而生的自动化构建方案。今天我们就从基础到实战,彻底掌握它们。
一、先搞懂:make 是什么?Makefile 又是什么?
1.1 背景
会不会写 makefile,从一个侧面说明了一个人是否具备完成大型工程的能力
一个工程中的源文件不计其数,其按类型、功能、模块分别放在若干个目录中,makefile 定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作
makefile 带来的好处就是 ——“自动化编译”,一旦写好,只需要一个 make 命令,整个工程完全自动编译,极大的提高了软件开发的效率。
make 是一个命令工具,是一个解释 makefile 中指令的命令工具,一般来说,大多数的 IDE 都有这个命令,比如:Delphi 的 make,Visual C++ 的 nmake,Linux 下 GNU 的 make。可见,makefile 都成为了一种在工程方面的编译方法。
make 是一条命令,makefile 是一个文件,两个搭配使用,完成项目自动化构建。
1.2 make:Linux 下的自动化构建工具
make是一个命令行工具,核心能力是:
检查文件的修改时间(对比目标文件与依赖文件)
只重新编译 “被修改过的依赖文件” 对应的目标
按预定规则(写在 Makefile 里)逐步执行构建步骤
简单说:它能帮你 “智能编译”,避免重复劳动。
1.3 Makefile:make 的 “操作手册”
Makefile 是一个文本文件,没有后缀名,核心作用是定义构建规则—— 告诉 make:
最终要生成的目标文件(比如可执行程序app)
生成目标需要依赖哪些文件(比如main.o、func.o)
具体用什么命令生成目标(比如gcc -c main.c)
没有 Makefile,make 命令就会报错(不知道该做什么)。
二、最佳实践:先见一下,如何使用
2.1 构建项目
1. 创建Makefile文件
touch Makefile //创建Makefile文件
2. vim编辑Makefile
3. make
2.2 清理项目
如果要清理项目时,可能会不小心将程序搞崩溃,所以这时候就需要Makefile自动化清理
2.3 理解Makefile/make,编译工作的推导过程,依赖关系和依赖方法
我们现在将clean换到前面,我们再make试一试:
我们发现make变成了进行clean的操作,那么我如果想编译code.c呢?—接着往下看:
我们只需在make后面加上目标文件即可!
那么make的逻辑是什么呢?
Makefile、make
默认形成的是从上向下遇到的第一个目标文件!!
且执行一组依赖关系和依赖方法
如图:
思考:make / Makefile:具体是如何形成可执行程序的呢???推导过程又是如何呢?
我们打开vim编辑Makefile
我们make一下:
make 是如何工作的,在默认的方式下,也就是我们只输入 make 命令。那么:
make 会在当前目录下找名字叫 “Makefile” 或 “makefile” 的文件。
如果找到,它会找文件中的第一个目标文件 (target),在上面的例子中,他会找到 myproc 这个文件,并把这个文件作为最终的目标文件。
如果 myproc 文件不存在,或是 myproc 所依赖的后面的 myproc.o 文件的文件修改时间要比 myproc 这个文件新(可以用 touch 测试),那么,他就会执行后面所定义的命令来生成 myproc 这个文件。
如果 myproc 所依赖的 myproc.o 文件不存在,那么 make 会在当前文件中找目标为 myproc.o 文件的依赖性,如果找到则再根据那一个规则生成 myproc.o 文件。(这有点像一个堆栈的过程)
当然,你的 C 文件和 H 文件是存在的啦,于是 make 会生成 myproc.o 文件,然后再用 myproc.o 文件声明 make 的终极任务,也就是执行文件 hello 了。
这就是整个 make 的依赖性,make 会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件。
在找寻的过程中,如果出现错误,比如最后被依赖的文件找不到,那么 make 就会直接退出,并报错,而对于所定义的命令的错误,或是编译不成功,make 根本不理。
make 只管文件的依赖性,即,如果在我找了依赖关系之后,冒号后面的文件还是不在,那么对不起,我就不工作啦
清理工程:
2.4 PHONY:伪目标——总被执行
三、ACM时间管理
3.1 重复make,竟然不让我make?
这里我们重复make发现不让我们make了,这里显示的意思是code文件没有进行更新
为什么我没有更新就不让我make呢?
答:gcc编译代码的时候,代码如果没有被编译过,或者曾经被修改过,make+gcc才会进行重新编译!
那么make如何知道code.c是否需要被重新编译呢?
这里我们补充一个指令:
查看文件时间:stat code.c
3.2 Access Time(访问时间)
缩写:atime
含义:文件内容被读取 / 访问的时间(仅指查看内容,不包含修改)。
触发场景:用 cat/less/more 打开文件查看内容、用 grep 匹配文件内容等。
注意:部分系统为优化性能,会通过 noatime 挂载选项关闭 atime 的自动更新(避免频繁写磁盘)。
3.3 Modify Time(修改时间)
缩写:mtime
含义:文件内容被修改并保存的时间(仅与文件内容变更相关)。
触发场景:用编辑器(如 vim)修改文件内容后保存、用 echo "内容" >> 文件 追加内容等。
关联:mtime 变更时,文件的元数据也会变化,因此 ctime 会同步更新。
3.4 Change Time(状态改变时间)
缩写:ctime
含义:文件元数据(非内容)被修改的时间(元数据包括权限、所有者、文件大小、文件名、链接数等)。
触发场景:用 chmod 修改权限、chown 变更所有者、mv 重命名文件、rm 删除文件(删除前会改元数据)等。
3.5 详解make如何知道code.c是否需要被重新编译
我们再来回顾一下:
文件 = 内容 + 属性
先说结论:文件内容改变影响的是Modify时间,文件属性改变影响的是Change时间
这里我们改变文件属性,来验证哪个时间会变化:
说明:改变文件属性影响的是文件的Change时间,而其他时间不变
我们再来改变文件内容,来验证哪个时间会改变:
这里我们改变code.c的内容来验证一下
从图中发现我们改变内容,ACM时间都改了!!!
在Linux系统中,修改文件内容,文件属性就会因此而变化,这是正常的!!!
我们再来读取一下文件信息,来观察时间变化情况:
先说结论这里读取文件信息会影响的是Access时间
但是你图片上展示的Access、Modify、Change时间都是一样的啊?我怎么知道你改变的是哪个?
说明:
我们读取文件会导致大量IO操作,Linux系统不会傻傻的你用一次就给你更新一下Access时间,这样会导致太大的IO操作,Linux对其进行特殊处理,它有一个策略,就是在你访问7、8次识别出来,他才会更新一下,最后总结一下:Access时间更新最频繁,但是他不是常更新,他有自己的策略,和系统有关
那你还没有给我解决前一个问题啊:make怎么知道我的文件是否需要被重新编译呢?
我们再来make一下:
永远都是先有源文件才有目标可执行文件!所以我们可以来画个图:
code文件时间比源文件时间新,此时我们再make一下看看结果
果然如此,所以我们得到结论:make是如何知道code.c需要被重新编译?
解析:
那么我可执行程序的时间比源文件新,说明源文件没有更新,也就说明Modify时间没有变!,如果源文件比可执行程序文件新,说明我们源文件被改了,Modify时间变了!此时make就知道code.c需要被重新编译了!
答:只要code.c的Modify时间比code可执行程序文件新,就需要重新编译!
那么我们将.PHONY修饰一下gcc编译试一下:
我们再来make一下:
此时我们发现make竟然可以一直执行,那么我想问一下大家知道.PHONY的本质了嘛?
.PHONY的本质:忽略实践对比!
但是我们不建议用.PHONY进行修饰!
四、makefile最佳实践和实用语法
在编译过程中,我们都倾向于将源文件 .c 先编译为 .o 文件
分两步是比较推荐的做法!
4.1 $@与$^
code:code.o
gcc -o $@ $^ // $@代表code $^代表code.o
code.o:code.c
gcc -c code.c
.PHONY:clean
clean:
rm -rf code.o code.exe
AI写代码
bash
4.2 定义变量
我们再来make一下看是否可以执行
我们可以观察到都是可以执行的!
我们再来打印一下要进行的操作!
有没有发现这样很挫,能不能不让它回显:有的!
我们只需要在前面加上@符号即可,我们再来make一下:
这样他就不会回显出来了!
那么如果不想让gcc -c code.c和gcc -o code.exe code.o回显,同样可以在他们前面加上@
查看文件:
4.3 $<和$^
$^ 表示所有的依赖文件
$< 代表第一个依赖文件
如果我们创建了10个文件都进行编译,我们来看一下make结果:
我们创建了10个.c文件,但为什么就给我一个.o编译为.exe?
这是因为我们在声明.o文件时只声明一个,那么向编译10个就要全写上嘛?那如果100个那岂不是很麻烦?
那么接下来有两种方法来进行展开:
做法一:
这时候就可以观察到全部展开了!
我们发现没有.o文件啊,这里有个语法make自动识别:
我们再来make一下
这样.o文件也展开了
我们再对Makefile进一步优化:
这样就可以全部删除了
再进行优化:
如果我们创建上1000个.c文件进行编译,我们来看一下它的编译时间:
这里大家可能看不到过程,其实是编译了很长时间的,我们在make一下发现他不让我百衲衣,因为会花费大量时间,我们再来code.c把源文件修改一下,如何发现再make它编译一下就出来了,这是为什么呢?
因为他只编译了被修改的内容!!!
我们之前说到的展开文件还有一个做法也是比较推荐的:
五、总结:为什么一定要学 Makefile?
效率提升:只编译修改过的文件,大型项目节省大量时间;
统一流程:团队协作时,所有人用同一个 Makefile,避免 “本地能跑,别人跑不了”;
进阶基础:后续学习 CMake(生成 Makefile 的工具)、自动化部署,都需要理解 Makefile 的核心逻辑。
如果你是 Linux 开发新手,建议从 “多文件案例” 开始动手实践,重点注意Tab 键和变量简化这两个细节 —— 上手后会发现,Makefile 其实比想象中简单!
结语:掌握Makefile能显著提升Linux开发效率,特别适合需要管理多文件项目的开发者,讲解了大量实用案例和底层原理分析,帮你彻底理解这个工程化开发必备工具
————————————————
版权声明:本文为CSDN博主「Cx330❀」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/yhrxh_ymq/article/details/155357221
云服务器爆款直降90%
新客首单¥68起 | 人人可享99元套餐,续费同价 | u2a指定配置低至2.5折1年,立即选购享更多福利!