【linux】linux项目自动化构建工具-make/Makefile
小编个人主页详情<—请点击
小编个人gitee代码仓库<—请点击
linux系列专栏<—请点击
倘若命中无此运,孤身亦可登昆仑,送给屏幕面前的读者朋友们和小编自己!
目录
- 前言
- 一、如何简单使用make/Makefile
- 二、细节介绍
- 1. 依赖关系和依赖方法
- 2. make和make clean
- 3.回显问题
- 4. 特殊符号$@和$^
- 5. 注释
- 三、为什么通常情况下make不能连续执行,那么又是如何做到的呢
- 1. 为什么通常情况下make不能连续执行
- 2. 那么又是如何做到的
- 总结
前言
【linux】linux编译器-gcc/g++的使用、如何才能让普通用户进行sudo提权—书接上文详情请点击<—
本文由小编为大家讲解—【linux】linux项目自动化构建工具-make/Makefile
一、如何简单使用make/Makefile
make是一个命令工具
Makefile是一个在当前目录下的文件,同时这个Makefile也可以使用makefile来替换,可以使用Makefile也可以使用makefile
那么下面由小编教一下大家如何简单的使用make/Makefile
-
确保自己当前有文件需要被编译生成可执行程序,这里小编以一个简单的c语言的打印程序为例进行讲解
-
在当前目录下使用touch指令创建一个名为Makefile普通文件
-
使用vim打开文件进行编辑,关于vim的使用请点击,按照小编如下格式进行编写,比葫芦画瓢,注意这里的红色字体的那一行一定要以一个tab键开头,编写完成后底行模式 wq 保存并退出即可
-
在bash命令行输入 make 即可直接完成文件自动编译生成可执行程序,查看当前目录下出现了一个名为 test 的可执行程序,我们 ./test 运行一下,运行成功,运行成功之后我们不想使用了,那么在bash命令行中输入 make clean 即可完成对可执行程序的删除(清理)
二、细节介绍
1. 依赖关系和依赖方法
- 我们的依赖关系的是以:进行分隔的,冒号的左侧是目标文件,冒号的右侧是依赖文件列表,这个依赖文件列表中可以有多个依赖文件也可以没有依赖文件,即目标目标依赖于依赖文件才能够生成
- 依赖方法:观察小编编写的依赖方法其实就是一行指令,以小编的编写的指令为例,即使用了gcc编译器去编译我们的源文件 test.c gcc编译器的使用
- 同时clean目标文件的依赖关系中不需要依赖文件,因为clean是对我们生成的临时文件或可执行文件进行清理删除,其不需要依赖文件,直接使用依赖方法中的 rm 指令直接对临时文件或可执行文件进行清理删除即可
2. make和make clean
为什么我们使用make完成的是对代码的编译,而我们使用make clean完成的是对代码的清理呢,那么可以让make完成的是对代码的清理,使用make test完成的是对代码的编译吗?答案是可以的
- 因为make执行规则是在Makefile文件查找并只生成所有目标文件中的第一个,如果第一个目标文件的依赖文件列表中存在的依赖文件不存在,那么make会继续向下递归式的寻找生成依赖文件的依赖关系,并且会递归式的生成目标文件所需要的所有依赖文件,类似于栈结构
- 对于其它并不是处于所有目标文件中的第一个的其它目标文件的生成需要我们使用 make 目标文件名 去显示调用其对应的依赖方法才可以生成目标文件
例如小编对Makefile进行更改一下顺序,那么我们执行make会去执行什么呢?
- 观察一下当小编修改了clean和test目标文件的依赖关系和依赖方法的前后顺序之后,不同的是,输入make命令之后成了调用clean目标文件依赖方法,即对临时文件或可执行程序的清理删除,这时候我们想要编译源文件只能显示使用make test去调用其对应的依赖方法
- 从使用习惯上来讲,这样的编写顺序不符合习惯,通常默认将对源文件的编译生成可执行程序的依赖对象和其依赖方法放在第一个位置
- 那么我们接下来看一下递归式生成目标文件,以源文件编译链接形成可执行程序的分过程为例进行讲解预处理 编译 汇编 链接详情请点击<—,这里我们使用vim修改一下在当前目录下的Makefile文件,使要生成第一个目标文件test就要先一次生成它的依赖文件
- 依赖文件的生成查找方案是先查找test目标文件的依赖文件test.o没有,再去查找test.o的依赖关系中的依赖文件test.s没有,再去查找test.s的依赖关系中的依赖文件test.i没有,再去查找test.i的依赖关系中的依赖文件test.c有,那么先生成test.i,再根据test.i生成test.s,再根据test.s生成test.o,再根据test.o生成我们真正所需要的所有目标文件中的第一个的test目标文件,这个过程不就类似于我们递归调用函数的过程,进行函数调用不断建立栈帧,当遇到返回条件(这里指遇到存在的依赖文件)的时候进行返回,类似于栈的先进后出的结构,最后遇到的先执行,先遇到的后执行
- 同时这个递归生成的过程中临时文件都会生成出来,不要忘记使用 rm 指令进行清理的时候要连同临时文件和可执行文件一并清理
注意:在实际的应用场景中通常是直接将源文件进行使用 gcc test.c -o test 一步到位进行编译链接生成可执行文件,这里小编将这个过程写的繁琐主要是为了便于演示过程,引出递归逐个生成依赖文件,并且最终生成第一个目标文件的过程
3.回显问题
-
为了验证可以使用makefile进行替换Makefile,这里小编使用mv指令修改一下文件名
-
我们观察,对于我们输入的make和make clean在屏幕上都有回显的其对应的依赖方法指令,如果我们不想要回显,那么可以在makefile文件中的依赖方法的指令前加上@ 即可
-
这时候我们再进行输入的make和make clean之后在屏幕上的其对应的依赖方法指令的回显已经消失了
4. 特殊符号$@和$^
- 特殊符号$@代表依赖关系中的冒号前面的目标文件,$^代表的是依赖关系中冒号后面的依赖文件列表中的依赖文件,在进行编写的时候,我们可以使用这两个特殊符号来进行一定的替代,所达成的效果和普通编写方式并无不同
- 进行修改后,执行一下 make 和 make clean 之后结果和普通书写方式相同
5. 注释
- makefile文件中我们也可以加注释,不同于windows操作系统中的vs的注释 // 或 /* */ ,在linux中这个makefile文件中的注释一律使用 # 的方式进行注释
三、为什么通常情况下make不能连续执行,那么又是如何做到的呢
1. 为什么通常情况下make不能连续执行
当一个文件需要进行编译链接形成可执行文件的时候,我们使用make命令来执行,执行完一次之后,再去执行make后编译器就不让我们执行,提示我们我们的test文件已经是最新的了,这是为什么呢?
- 因为没有必要,我们已经使用了make生成了可执行程序,再使用make去生成可执行有什么用呢?并且有的文件进行编译链接形成可执行程序占用的内存很大,如果多次形成可执行程序,那么操作系统的效率就会很低,为了在一定程度上提高效率,所以编译器默认只允许make生成了可执行程序的操作执行一次
2. 那么又是如何做到的
make通过判断源文件(依赖文件)和目标文件(可执行程序/文件)的新旧,进而来判断是否需要重新执行依赖方法进行编译形成目标文件
- 一定是先有源文件,再有的可执行文件,因为是源文件编译链接形成可执行文件,所以一般而言,源文件要比可执行文件老,这时候不允许使用make重新执行依赖方法形成可执行文件
- 如果我们更改了源文件,历史上还有可执行文件,那么源文件要比可执行要新,这时候可以使用make重新执行依赖方法形成可执行文件
那么如何进行进行具体的比较呢?那么自然是使用时间进行比较,时间又可以转化为时间戳,底层实际上是采用时间戳进行比较的
- stat 要查看三种时间,Access访问时间,Modify修改时间(文件内容的修改),Change改变时间(文件属性的改变)
- 文件=文件内容(文件中的文字内容)+文件属性(文件名,占用内存大小等),用于进行比较的是文件的内容的修改时间
- 通常对文件内容的增删改都会引起文件占用内存大小的更改,自然也就会引起文件属性的更改(即Modify时间(文件内容的修改)的改变会引起Change时间(文件属性的改变)的改变)
- 对文件属性的更改不一定会引起文件内容的更改,例如你修改文件名,但是文件内容未进行更改(Change时间(文件属性的改变)的改变不一定会影响Modify时间(文件内容的修改)的改变)
- 通常来讲,访问时间会进行频次最高的修改,但是编译器进行了优化,试想一下,通常都是存储在磁盘上,磁盘上的数据访问修改比较慢,如果存在多人1000个人协作进行修改或访问文件,那么这个Accsee访问时间就会被不断的进行修改,而磁盘上的数据访问修改比较慢,那么操作系统的性能就变差了,所以进行了优化,会有一个计数器用于统计访问时间的修改次数,当次数达到设定的临界次数后Access访问时间才会进行修改
- 观察下图,test.c的文件内容的修改时间要老于可执行文件的修改时间,所以这时候不能使用make再次形成可执行文件
使用make再次形成可执行文件失败
- 同时我们还可以使用touch指令更新时间,当touch后是一个不存在的文件的时候,touch的作用是创建此文件,当touch后是一个已经存在的文件后,那么touch的作用是将此文件的三种时间Access,Modify,Change都进行更新到当前时间的最新时间,所以我们就可以使用touch指令的方式人为更改文件的时间
-
当我们更改源文件的时间后,历史上已经存在了可执行文件,此时源文件的时间比可执行文件的时间要新,使用make可以重新调用依赖方法重新编译链接形成可执行文件
-
此时重新形成的可执行文件的Modify修改时间要新于源文件的Modify修改时间,此时我们再执行一次make自然会显示失败
-
当然我们还是有办法让每次这个目标文件都被执行的,那就是.PHONY进行修饰目标文件,修饰后,每次make指令就可以被执行了
- 被.PHONY修饰的目标文件我们通常称为伪目标,伪目标的文件名并不代表真正的文件名,与实际存在的同名文件名没有任何关系,伪目标特点的是总是被执行,我们通常使用.PHONY来修饰clean,使清理删除临时文件或目标文件的工作每次都被执行,以便达到可以进行重新编译的效果
- touch 选项 要修改时间的文件名,补充touch还可以有 -m 和 -a 选项,分别是更新Modify修改时间至当前最新时间和更新Access访问时间至当前最新时间,注意这里的时间更新还有可能还会引起其它两个时间的改变
总结
以上就是今天的博客内容啦,希望对读者朋友们有帮助
水滴石穿,坚持就是胜利,读者朋友们可以点个关注
点赞收藏加关注,找到小编不迷路!