
在软件开发的浩瀚宇宙中,构建与编译的过程常常是开发者日常工作中不可忽视的一部分。每当项目中加入新的代码或修改现有的代码时,我们不得不再次构建整个程序,确保修改的部分与整个项目的其余部分无缝衔接。这个过程,看似单调、枯燥,却也至关重要。正是这些小小的构建过程,确保了一个庞大软件系统的正常运作。
然而,在每次修改后手动执行一系列繁琐的构建命令,难免让人感到疲惫与困扰。这时,Make 和 Makefile 便应运而生,化繁为简,赋予了开发者极高的效率。
在这篇报告中,我们将深入探讨 Make 和 Makefile,如何在自动化构建的舞台上,成为程序员手中不可或缺的利器。
Make 工具的历史可以追溯到上世纪70年代,源自 Unix 系统中的自动化构建需求。最初,Make 被设计为一个简单的自动化构建工具,用于编译大型项目。随着时间的推移,Make成为了软件开发中自动化构建的标准工具,它能自动分析源文件的依赖关系,决定哪些文件需要重新编译,并自动执行相应的构建命令。
*早期的 Make 仅仅支持简单的命令和文件依赖关系,但随着其不断发展,现代的 Make 版本(尤其是 GNU Make)已经具备了强大的功能,支持变量、条件判断、循环等高级特性,能够满足更加复杂的构建需求。
一个典型的Makefile包含以下部分:
make是⼀条命令,makefile是⼀个⽂件,两个搭配使⽤,完成项⽬⾃动化构建。
为了方便我们下面的讲解,我们这里先带大家看一下如何使用make/Makefile来实现自动化构建的功能
首先,我们要先明白为什么要有这个自动化构建工具,在我们之前的学习中,我们在编写代码的时候会经常用到gcc和删除相关的指令,每次都需要我们重新创建并删除可执行文件,这个操作比较冗余,且当工程比较大时,这种操作就会显得非常麻烦,所以就有了自动化构建工具
下面我们来看一下如何简单的使用make/Makefile
首先,我们要先在当前目录下创建一个Makefile文件
touch Makefile
然后进入这个文件中,将我们的源文件和目标文件建立依赖关系,和相关的清除语句
先来看一下我们的源文件test.c中的内容
#include<stdio.h>
2 int main()
3 {
4 printf("Hello Linux\n");
5 return 0;
6 } 在之前我们只能通过gcc来编译得到可执行文件,运行可执行文件才能得到结果,如果test.c中的内容进行了改动,就需要重新执行上面的步骤,比较繁琐.
但现在我们可以通过以下操作,在Makefile文件中写入以下内容:
mytest:test.c
gcc test.c -o mytest
clean:
rm -f mytest 
写入后保存并退出,然后执行make命令

执行后我们就可以发现我们执行了Makefile文件中的编译命令,生成了可执行文件,运行可执行文件后就可以得到我们想要的结果
当我们要删除我们得到的这个可执行文件时,需要下面的指令即可:
make clean
上面有几个小的知识点值得思考:
1、如果有多层依赖关系怎样处理? |
|---|
这里的多层依赖关系指的是互相依赖,就比如在上篇我们已经讲过了可执行文件是由.o文件得来,而.o文件又依赖于.s文件,.s文件依赖于.i文件,.i文件依赖于.c文件,就这样层层依赖,才得到了最终的可执行文件,如果将这几个依赖关系都写入Makefile文件中去,其实我们可以发现它会自己处理这种多层依赖关系,即使我们的顺序写的不对
2、为什么make命令的执行结果是gcc编译? |
|---|
这个取决于makefile的内容,make命令的功能是执行Makefile中的第一条命令,因为我们将编译的指令放在最上面,所以执行结果就是gcc编译,如果我们以下面的这种顺序写入Makefile文件:
3.当源文件不变时,只能编译一次: |
|---|

当我们的源文件没有改动时,我们只能make编译一次,之后就无法再编译了,这样的原因其实是为了提高编译效率,那么make指令具体是怎么做的呢?这就牵扯到
文件时间戳的问题了,下面我们详细讲解一下
make实现高效编译的原理其实就是通过比较源文件和可执行文件的修改时间,来判断是否可以再次执行,从而避免无效的执行
具体点来说就是源文件的修改时间新于可执行文件的修改时间时,就能够再次执行make命令,生成新的可执行文件
我们可以用stat指令来查看文件的时间的相关的信息
stat 文件名
在本篇我们需要关注到的就是这三个与时间相关的信息:
其中我们判断是否可以再次执行比较的是源文件与可执行文件的Modify时间
就比如我们上面的test.c和可执行文件mytest

我们可以发现可执行文件mytest的Modify时间新于源文件test.c的Modify时间,所以无法执行make命令
此时我们更新一下test.c文件中的内容
#include<stdio.h>
int main()
{
printf("Hello Linux\n");
printf("更新内容\n");
return 0;
} 更新之后再来查看一下test.c和mytest的时间:

此时源文件的最新修改时间就晚于可执行文件的最新修改时间,所以make就可以执行
以上就是make实现高效编译的方式
myproc:myproc.o
gcc myproc.o -o myproc
myproc.o:myproc.s
gcc -c myproc.s -o myproc.o
myproc.s:myproc.i
gcc -S myproc.i -o myproc.s
myproc.i:myproc.c
gcc -E myproc.c -o myproc.i
.PHONY:clean
clean:
rm -f *.i *.s *.o myproc⼀般我们这种clean的⽬标⽂件,我们将它设置为伪⽬标,⽤.PHONY修饰,
伪⽬标的特性是,总是被执⾏的。
因此可得出该结论:
.PHONY:让make忽略源⽂件和可执⾏⽬标⽂件的M时间对⽐
在计算机世界的广袤天地里,Makefile或许只是众多工具中的一员。然而,它所代表的自动化构建理念,却深刻地影响了整个软件开发的进程。从初学者到资深开发者,每一位接触Makefile的人,都在这个过程中找到了高效与优雅的平衡。正如那位隐形的指挥家,Makefile将开发者的工作引领至一个个完美的高峰。
编写Makefile,也许只是一项技术任务,但它却以其简洁、高效与诗意的魅力,永远镶嵌在开发者心中,成为一段难忘的记忆。
本篇关于Make和Makefile的介绍就暂告段落啦,希望能对大家的学习产生帮助,欢迎各位佬前来支持斧正!!!