【Linux】自动化构建—make/Makefile

目录

一、Makefile的简单介绍

二、初步使用make/Makefile

三、PHONY伪目标

1.PHONY简单介绍

2.简单小结

[3. .PHONY可以总是被执行,这是如何做到的?](#3. .PHONY可以总是被执行,这是如何做到的?)

4.为什么每次访问(cat)未必都会改变Access时间呢?

四、适度扩展语法

1.同时创建多个文件

2.隐藏不需要显现的语句

3.变量替换


Makefile是强大的"自动化编译"助手,是否会写好并运用好它,往往能侧面说明一个人是否具备完成大型工程的能力。

一、Makefile的简单介绍

1.首先一个工程的源文件不计其数,其按类型、功能、模块被分类到若干个目录中,makefile能够定义一系列规则来指定哪些文件需要先编译、哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作。

2.其次makefile带来的好处就是"自动化编译",一旦写好,只需要一个make指令,整个工程完全自动编译,极大的提高了软件开发的效率。

3.makefile是一个命令工具,是一个解释makefile中指令的命令工具,一般来说,大多数的IDE都有这个命令,比如:Delphi的make、Visual C++的nmake、linux下GNU的make。可见,makefile都成为了一种在工程方面的编译方法。

4.最后说明一下make是一条命令,Makefile是一个文件,两者搭配使用,才能完成项目自动化构建。

二、初步使用make/Makefile

先touch一个test.c文件和一个Makefile文件(尽量不要写成makefile,更不能写成MAKEFILE,开头的M大写即可),然后vim test.c写一个简单hello world的程序。保存退出并vim进Makefile开始编写如下两行代码:

输入结束后保存并退出,然后输入make命令,便开始解析Makefile文件,即将test.c编译为可执行文件。

直接运行test.exe可以发现我们是可以直接运行成功的:

演示完毕后接下来要解释的是Makefile文件里的两行代码到底是什么意思?首先test.exe:test.c我们通常称为是一对依赖关系,第二行的gcc -o test.exe test.c称作依赖方法,即text.exe是通过gcc编译text.c得来的。依赖关系与依赖方法需要共同作用,构成一条完整的逻辑线,才可以被外部make指令解析。

我们可以更加详细地剖析这一过程,来学习makefile的基本语法(只利于理解,实际工程基本不会用到下面这一过程)

保存退出并输出make指令后,依旧可以达到同样的结果:

这里相当于一个推导线。属于依赖方法的集合,根据依赖关系从上找到下,最后找到test.c文件并开始编译,过程从下到上,相当于一个递归的过程,由于test.exe到test.c同样具有逻辑传递的关系,因此这几条语句仍属于一整个完整的逻辑线,而非多条逻辑线(多条逻辑线的情况下make只会默认推导makefile中的第一条逻辑链路)。

同样的,也可以把这几条语句看作元素,进行一个出栈入栈的过程,比如"gcc test.o -o test.exe"需要text.o文件,但此时并未存在该文件,因此这条语句暂未执行,先压入特定的栈中,继续执行下一条语句"gcc -c test.s -o test.o",此时仍不存在test.s文件,则该条语句继续压入栈中,以此类推,最后到语句"gcc -E test.c -o test.i",发现此时有test.c文件了,开始执行,生成test.i文件,然后通过栈后进先出的原则,语句依次出栈并实行"gcc -S test.i -o test.s"、"gcc -c test.s -o test.o"、"gcc test.o -o test.exe"的操作,如上输出结果。

三、PHONY伪目标

PHONY顾名思义就是伪造的意思,用于在makefile文件中创建伪目标,以下是对PHONY的简单介绍以及使用方法和场景。

1.PHONY简单介绍

PHONY用来修饰一个文件为一个伪目标,上面讲test.c编译为test.exe文件的过程中,过程会产生许多文件,此时,我们就可以写一个伪目标clean,来清理掉这些文件:

这里的clean不需要依赖关系,因为它只需要执行这条删除命令的语句,但同时,clean也不会自动执行,而是需要外部手动make clean才会执行,因为make只会默认执行一条完整的推导链路,make后面可以跟"目标名",跟谁就解析谁的依赖关系和依赖方法,如下:

重点特征:.PHONY总是能被执行的,什么意思呢?我们来看下面实操分析:

我们看,当重复使用make指令的时候,第二次就会提示"is up to date",意思就是目前已经是更新的最新内容了,无法重复编译。但是,如果我们给test.exe设成伪目标,再观察是否还会出现相同情况:

很显然,此时重复使用make也不会出现无法重复编译的情况,因此被.PHONY修饰的伪目标是可以重复被执行的。

2.简单小结

以上的内容看起来是十分零散的,以下我先做一个小总结,然后再继续探讨,主要总结为五个细节。

细节1:依赖关系必须存在,依赖文件列表可以为空,比如上面的clean依赖文件列表就为空。

细节2:依赖方法可以是任何的shell命令,比如上面编译test.c的gcc命令和clean的rm -f强制删除命令。

细节3:clean目标,只是利用make的自动推导的能力,让它执行了rm命令,在构建工程的视角,看起来就是清理项目,本质就是删除不需要的临时创建的文件。

细节4:make后面可以跟"目标名",后面跟谁,就解析谁的依赖关系和依赖方法。

细节5:make默认只会推导一条完整的推导链路,即只会推导第一个依赖关系对应的推导链。

3. .PHONY可以总是被执行,这是如何做到的?

首先,先了解输入stat指令是可以查看一个文件的时间信息的,如下:

其中,主要的时间信息就是Access(访问时间)、Modify(内容修改时间)、Change(属性修改时间),按照其首字母,我们一般称为ACM时间,而之前的make指令主要是根据Modify时间是否是最新时间来判断是否执行的,如果进行两次make操作,但是Modify时间是不变的(即文件内容不发生改变),则第二次重复操作无效。当我们修改test.c的内容后再试试看,会发现Modify时间不一样了,演示如下:

会发现不只是Modify时间改变了,甚至整个ACM时间都改变了,因为Modify改变后,本质内容也就发生了改变,内容改变意味着文件大小发生了改变,而文件大小也属于一种属性,它改变后那么属性时间Change也会跟着改变,而Access访问时间同样也会发生改变,因为进去修改就需要访问该文件,但需要注意的是,并非每次访问(cat)都会改变Access的时间, 这个后面单独讨论为什么。言归正传,开始解释为什么.PHONY修饰的目标文件可以总是被执行,因为伪目标文件可以强制忽略掉Modify时间,不被该规则限制,即不需要根据Modify时间是否是最新时间来判断是否执行

4.为什么每次访问(cat)未必都会改变Access时间呢?

因为更新Access时间就相当于修改文件属性,而文件属性信息变化需要同步刷新到磁盘,而对于一个文件,我们访问的次数肯定要远大于修改的,如果每次访问都要改变属性并刷新一次磁盘,势必会产生大量的额外磁盘IO,导致系统整体效率降低,因此一般访问文件内容达到一个特定次数后才会更新一次时间。

四、适度扩展语法

以上都是一个源文件解析为可编译程序的例子,那如果有10个乃至100个甚至1000个源文件又该如何去做呢?我们以100个源文件为例子,首先先创建100个.c文件,如下:

1.同时创建多个文件

想要创建src1、src2......src100这么多文件,不需要一个一个touch出来,只需要再src后面加一个大括号然后填写"1..100",最后写后缀.c即可一次性创建多个文件。

2.隐藏不需要显现的语句

按照这样进行make指令,下划红线的语句是会在操作页面显现出来的,如下:

这里给出隐藏这些不必要语句的方法,就是在这些语句前面加上"@",具体情况是否隐藏根据实际来定,这里仅做演示:

我们再进行相同的操作会发现之前显现出来的语句隐藏了:

3.变量替换

这里的变量替换与我们熟知的宏替换相似,大型工程往往文件多、模块杂,编译器路径、编译参数、输出目录等常需统一调整。而用变量替换后,只需改一处变量值,所有引用该变量的地方会自动同步,避免手动修改成百上千出疏漏。此外,变量替换让脚本更易读,能清晰体现逻辑。

接下来我们尝试把100个.c文件变为可编译文件:

详细如上所示,额外补充的是这里的注释是用#

上面变量可以根据需求随意变换,在大型工程中可以避免大量重复修改操作,大大提高了工作效率,同时,一个make就可以直接运行。

第10行到第12行是将所有的.o文件都链接为一个exe文件,但此时还没有.o文件,于是继续向下看,第14行到第16行,是将所有的.c文件编译成.o文件,这两处构成一条完整的逻辑链路,下面展示部分编译过程:

最后得到如下文件,最终生成src.exe文件:

同样可以得到正确结果

要一口气删除所有的.o文件以及.exe文件,在clean中已经写好了,直接make clean即可

test目标则是输出所有的.c文件和.o文件,此处不再赘述。

好了,对make/Makefile的介绍到此为止,到目前为止的知识点均掌握即可,基本暂时足够使用了,对于额外的语法知识,需要一个学一个然后见招拆招即可。

相关推荐
脑子进水养啥鱼?2 小时前
Linux find 命令
linux·运维
梓䈑2 小时前
【Linux系统】实现线程池项目(含日志类的设计)
linux·服务器·c++
EverydayJoy^v^2 小时前
RH124简单知识点——第8章——配置和保护SSH
linux·运维·ssh
取加若则_3 小时前
Linux环境变量与命令行参数全解析
linux
淮北4943 小时前
GDB在ubuntu上的安装与使用
linux·运维·ubuntu
shhpeng3 小时前
在 Ubuntu 24.04 上安装 Go1.24.2
linux·运维·ubuntu
0思必得03 小时前
[Web自动化] Selenium中Select元素操作方法
前端·python·selenium·自动化·html
颖风船3 小时前
vscode连接vmware中的deepin虚拟机,deepin端配置
linux·ide·vscode
苏宸啊3 小时前
Linux工具
linux