大家好,欢迎来到IT知识分享网。

💭 写在前面:本章我们要学习的是 makefile。会不会写 makefile,从一个侧面说明一个人是否具备完成大型工程的能力。一个工程中的源文件不计其数,按类型、功能、模块分别放在若干个目录中,makefile 定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译 ,甚至于更复杂的功能操作。话不多说,让我们开始吧!

Ⅰ. 初识 Makefile
0x00 引入:聊一聊项目构建的话题

对于项目构建的话题,一直用 VS 的同学可能会感叹道:
” 诶……我就从来就没这样的困扰,我在 VS 下创建多文件,运行直接特喵的 Ctrl + F5 就完事了 “
确实,编译时你需要按个键就能做到编译调试运行,编译个项目真的轻轻松松。

❓ 思考:你可以试着思考一下下面的问题
- 多文件
我们先编译哪一个程序?
- 链接需要哪些库?
- 库和头文件等在哪里找?
- 整个项目结构,该如何维护?
上述问题你在 VS 下压根就没关心过,因为 VS 下都帮我们屁股擦得干干净净 ~
你只需要在你的 VS 下需要



需要包含头文件就 #include 头文件,因为 VS 自动帮你维护了项目的结构!
也不需要牵扯先编哪个后编哪个的话题,但是在 Linux 下这些东西该如何去弄呢?
💭 举个例子:
没有请问你要先编哪个后编哪个呢?最后怎么形成可执行程序呢?
若尚未学习 makefile,你怕不是会用 gcc 命令一个个去编,编完之后还要把它们链接起来。
” 啊这,这样成本也太高了,太烦了吧……”
如果你项目中存在 100 个 

但是如果这是在 Linux 中,有这么多的头文件和源文件,你就得把这些源文件一个个变成 
然后再一个个把 
这些事在 Linux 中就需要我们去做了,为了能让这样的工作更适合地球人,于是就有了 makefile。
不是集成环境也没关系,我们可以使用 Makefile 来统一解决这样的问题。
0x01 什么是 Makefile
什么是 Makefile?
在我们 Linux 当中就是 make / makefile。make 是一个命令,makefile 是一个文件。
(为了不增加学习的成本,前期我们只用一个头文件给大家演示,并且会讲的浅一些)
💭 演示:如何使用 Makefile
如果放到之前,我们如果想编代码会自己 gcc,但实际上写 Makefile 也要 gcc。
但 Makefile 能让你不在命令行中打 gcc 命令操作,这能避免一些比较尴尬的情况。
比如下面这种 mytest.c 和 mytest 的顺序写反了的情况:
$ gcc mytest -o mytest.c
” 这……尬的我徒手抠出一个三室一厅 “
写选项时如果把生成可执行程序与源代码搞反了,又恰巧存在一个可执行,那源代码可能就寄了:
目录下存在一个叫 mytest 的可执行,此时如果不小心把选项写反,后果就是源代码直接消失:
$ gcc mytest -o mytest.c ❌ 写反了!!!
这种场景一不小心写反,可执行程序就覆盖掉了原文件,最后导致你的源代码都没了……
由此可见,在命令行操作时如果出现误操作,就会翻车。
” 毕竟人被杀就会死,道理大家懂得都懂。”
正是因为这些悲剧的存在,使得 Makefile 的光芒愈发温暖!
” Makefile,我滴神!我好喜欢你,为了你,我要疯狂的写bug!”
0x02 依赖关系与依赖方法
下面我们来正式介绍一下 Makefile,Makefile 需要两个东西,我们先介绍第一个东西。
- 需要在你当前路径或代码路径下创建一个 “Makefile” 或 “makefile” 文件(首字母大小写均可)
我们先做一些准备工作,然后创建一个名为 makefile 的文件:
现在有了 makefile 文件,下一步就是编写这个文件!
即在这个文件中添加对应的 “依赖关系” 和 “依赖方法”。
📚 makefile:是在当前路径下的一个普通文件,它会包含两个东西
- 依赖关系(Dependency Relationship)
- 依赖方法(Dependent Method)

不如这样,我来举一个生活当中的例子帮助大家去理解它们的意思:
我们首先弄清楚一个概念,依赖是什么意思?什么是依赖?
假如你是个在校大学生,快要到月底了,这时候你可能就要打电话给你爸要生活费了。你打电话给你爸爸,说 “爸,我是你儿子。”,这就是表明依赖关系。你打电话告诉你爸你是他儿子的时候,实际上你的潜台词就是 “我要依赖你”。你给你爸打电话说:”爸我是你儿子”,说完就把电话一挂,对于你爸来说会一脸懵逼 —— “这孩子今天怎么了,这是被绑架了?”,你爸就不太清楚了。也就是说,你在打电话时只是表明了依赖关系,但你并没有达到你想要做的目的(要下个月的生活费),所以正确的方法应该是:”爸,我是你儿子,我要下个月的生活费。”,你表达了你是谁,并且要求给你打钱。
我是你儿子 —— 表明了 “依赖关系”,因为依赖关系的存在,所以才能给你打钱。
打钱 —— 就是 “依赖方法”,当你把依赖关系和依赖方法表明时,你就能达到要钱的目的。
依赖关系不对,依赖方法再对也没有用,比如你的舍友给你爸打电话,说:”我是你儿子的舍友,给我打钱!”,你爸绝对不会打钱的。
依赖方法表明了,依赖方法不正确同样没有用,比如你打电话给你爸:说:”我是你儿子,给我打钱我要充游戏!”,你爸也不会给你打钱的!
通过上面的比喻,相信你已经知道什么是依赖关系和依赖方法了,他们必须都为真。
依赖关系和依赖方法都要为真,才能达成要钱的目的!
makefile 里面表明的就是依赖关系和依赖方法,按照现阶段的认识我们可以倒推一下:
Ⅱ. 实现简单的 Makefile
0x00 写一个最简单的 Makefile
💭 演示:写一个最基本的 Makefile
mytest:mytest.c
gcc mytest.c -o mytest
至此,我们就把一个最基本的 Makefile 写完了。
我们来 cat 看一下我们写的 Makefile,第一行是依赖关系,紧接着第二行是依赖方法。
📌 注意:依赖关系后面紧跟依赖方法时前面要空一个 
此时我们就有了一个最基本的 Makefile 了,我们编译 mytest.c 文件就可以不用再敲 gcc 命令了!
直接在命令行中输入:
$ make
🚩 输入效果:
输入 make,这里就自动形成了可执行程序,会帮我们从 Makefile 去找依赖关系和依赖方法。
我们来看看这小B窄汁都做了些什么 ——
(这个 3,我就是故意写这么大的,你就说你能把我怎么办吧)
看到这里有的人可能会觉得 ——
“你这不是脱裤子放屁么,这不还是要输 gcc 命令吗?这和我在命令行有什么区别?”
当前让你产生这种感觉,主要是因为:
① 上面我们写的 makefile 是最简单的 makefile 了,自然和命令行没什么差别。
② 我们目前的项目结构简单,如果后面遇到大的项目结构你就知道 Makefile 有多香了。
” 哈哈,毕竟谁会想敲一百多行 gcc 呢……”

以后我们在 Linux 下编译代码就不需要敲 gcc 命令了,直接 make 就可以了。
它会给我们带来很多便捷,如果是第一次接触,现在可能还体会不到,后面慢慢就能体会到了。
0x01 项目的清理
刚才我们说的 make 就相当于 VS 下的 “生成解决方案” :
但是 VS 下好像还有 “清理解决方案” 这样的功能,那我在 Linux 下也想拥有,怎么办?
” 得不到就毁掉?不,我们得不到还可以去 Makefile 嘛!”
💭 举个例子:
我们现在不想要这个可执行程序了,放在之前我们会直接 rm 掉这个文件。
但是假设有这样的一个场景:一个程序生产了大量的临时文件,你岂不是要疯狂的 rm?
无脑删又很容易把源代码删掉,这个时候我们就可以在 Makefile 里实现 “清理解决方案” 的功能!
.PHONY:clean
clean:
clean 没有依赖文件,也是存在依赖关系的,只不过这个 clean 没有依赖列表。
它后面的相当于是个孤儿,而 clean 被 
后面的 rm -f mytest 为依赖方法,我们现在来用一下看看效果如何!
刚才我们编译用的是 make,清理我们用 make clean:
🚩 输入效果如下:
此时如果想清理代码,在命令行直接输入 $make clean 即可。
📌 注意事项:再次强调,依赖方法前面必以 tab 键开头(红色框标记处)
0x02 多文件的 makefile
我们来演示一下多文件的 makefile。
我们先创建 3 个文件,分别是 main.c,test.c 和 test.h :
vim 分别打开这三个文件,我们写一点东西进去:
此时如果我们 gcc,我们可以:
gcc -o hello main.c test.c
我们把 test.c 跟在 main.c 后面,这里不放 test.h 的原因是因为头文件在预处理阶段就已经展开到源文件中了。
现在我们开始写 makefile:
我们想生成的 hello 文件需要依赖于 main.o 和 test.o
但我们没有 .o,所以我们还需要进一步解释一下 main.o 和 test.o 是依赖谁的。
值得一提的是,下面两种写法都是可以的:
# 写全是这样的
gcc -c main.c -o main.o
# 实际上,如果你不写后面的,makefile也会自动给你形成同名.o
gcc -c main.c
制作好 makefile 之后,我们 make 一下看看效果如何:
效果很好,我们再把清理功能写一下:
这里我们需要删除 

只要它以 


看看效果如何:
make 就相当于 VS 中的构建项目,而 make clean 就相当于是清理项目。
Ⅲ. PHONY 伪目标
0x00 .PHONY 定义伪目标
目标文件和伪目标,给我的一种感觉就是好像都是目标文件啊?这就像正规军和伪军,都是军队。
不管是目标文件还是伪目标,最终目的都是要根据自己的依赖关系执行依赖方法。
不知道你有没有观察到,我们的 makefile 有两个目标文件:
我们在 make 的时候,默认只帮我们生成 makefile 中的 mytest.c 目标文件。
而 make clean 是制作指定名称的目标文件,做清理的工作,我们首先来思考一个问题:
❓ 思考:为什么 make 的时候它总是执行第一个呢?
makefile 在自顶而下进行形成目标文件时,它可以根据你的需求形成多个目标文件。
我们这里有两个目标文件,一个是 mytest 一个是 clean,凭什么我 make 执行的是 mytest 而不是 clean?答案很简单,就凭我 mytest 是在前面写的!
如果我们把它们两的顺序换一下:
然后我们再来进行 make 看看:
所以 makefile 在形成文件时会自顶而下扫描,默认只会形式第一个目标文件,执行该依赖关系的依赖方法。
但是我们一般还是喜欢把形成可执行程序放在前面,也就是让 makefile 默认去形成:
那这个 

- 比如 clean 被
修饰时,表明 clean 是总是被执行的。
这个 “总是被执行” 这句话真的非常的莫名其妙,我们先通过代码来看看什么是 “总是被执行” !
0x01 总是不被执行
“总是被执行” 就意味着还有 “总是不被执行” ,我们先来看看什么是 “总是不被执行” ?
💬 现象演示:总是不被执行
我们多次 make 之后它仍然是会提示 “make: `mytest` is up to date.” 这段话。
貌似只有第一次 make 的时候才会才会帮我们形成一个全新的 mytest ,再之后多次进行 make,
就会告知你 mytest 已经是一个可执行程序了,不能再生成了。
这种现象就叫做 “总是不被执行的” ,你已经是最新的可执行程序了,干嘛要再去形成呢。
如果自己的源代码已经用最新的源代码编译链接形成可执行文件了,那么编译器就不会再帮我们再重新根据你的 makefile 再重新给你生成了。因为既然变都没变,还给你生成那岂不是既浪费时间又浪费资源?
现在你可以不用去想它是怎么做到的,我们已经演示了 “总是不被执行” 的情况了。
0x02 总是被执行
我们刚才看了 “总是不被执行” 的现象,我们试试给我们的 mytest 也用 
现在 mytest 也变成了 “总是被执行” 了,现在我们来看看 “总是被执行” 是个什么现象:
💬 现象演示:总是被执行
被 
📚 总是被执行:无论目标时间是否新旧,照样执行依赖关系。
而我们一般不太建议、喜欢把我们形成的目标文件定义成伪目标,而是喜欢把清理定义成伪目标。
这也正是为什么我们每次 make clean 都能帮我们执行清理的根本原因。
❓ 思考:那 makefile 是如何识别我的 exe/bin 文件是新的还是旧的呢?
ACM 时间包含 Access 时间、Modify 时间 与 Change 时间。
这个 Modify 和 Change 有什么区别呢?
我们知道,”文件 = 内容 + 属性”
如果是内容发生改变,就是 Modify;如果是属性发生改变,就是 Change。
修改内容也有可能引发 Change 的改变,因为修改内容可能会引起 change time 的变化
我们打开文件修改,Access 应不应该改变呢?我们读取 Access 变不变?
要变的!但是现在不会变!因为访问文件的频率是最高的,Modify 和 Change 是不得不变的,不变的化文件就不对了。但是我们大多数情况修改文件属性和修改文件内容是很低频的事情,但打开文件是非常高平的事情,Linux 后期内核对 Access 进行了优化,将文件打开访问,打开时间不会变化,累计一段时间后他才会变化。如果不这样,打开文件这种高频率的事情,一旦更新 Access 时间,就要将数据刷新到磁盘上,这实际上一个很没效率的事情。
具体 Access 的调整策略取决于 Linux 的版本。
💡 答案:通过对比你的源文件和可执行程序的更改时间 (modify time) 识别的新旧。 根据原文件和可执行程序的最近修改时间,评估要不要重新生成。
现在我们再回头看刚才的问题:什么是 “总是被执行呢” ?
“总是被执行” 就是 忽略对比时间 (modify time),不看新旧,我让你执行你就给我执行。
📌 [ 笔者 ] 王亦优
📃 [ 更新 ] 2023.1.3
❌ [ 勘误 ] /* 暂无 */
📜 [ 声明 ] 由于作者水平有限,本文有错误和不准确之处在所难免,
本人也很想知道这些错误,恳望读者批评指正!
|
📜 参考资料 C++reference[EB/OL]. []. http://www.cplusplus.com/reference/. Microsoft. MSDN(Microsoft Developer Network)[EB/OL]. []. . 百度百科[EB/OL]. []. https://baike.baidu.com/. 比特科技. Linux[EB/OL]. 2021[2021.8.31 xi |
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/23947.html































