【Linux】Linux 项目自动化构建工具 -- make/makefile

news2024/11/28 23:34:38

👑作者主页:@进击的安度因
🏠学习社区:进击的安度因(个人社区)
📖专栏链接:Linux

文章目录

  • 一、前言
  • 二、概念
  • 三、demo 实现
  • 四、原理与规则
    • 1、依赖关系和依赖方法
      • ① 感性理解
      • ② 深层理解
    • 2、清理
      • ① .PHONY 伪目标
      • ② .PHONY 的取舍
    • 3、make 确定是否编译的方法
    • 4、完整代码
    • 5、规则总结
  • 五、结语

一、前言

请添加图片描述

上篇博客,我们学习了 gcc 编译器。学会了如何在 Linux 上编译 C语言 代码。

对于我们平常练习是没问题的,但是如果有上百个源文件,该怎么办?难道还是一个个都用 gcc 编译为 .o 文件,最后将它们一起链接起来?

这肯定是不实际的,这使得编译成为了一个很麻烦的事情。

之前我们在 vs 中写代码时,使用快捷键就可以很快地进行程序的编译,或者直接执行程序,那么在 Linux 下能否也能实现这个功能?

能否减少编译代码时的风险,使编译更加快捷,一定程度实现自动化编译?

当然有,这就是我们 今天的目标之一:使用 make/makefile 构建一个简单的自动化工具。

另外 a n d u i n anduin anduin 还会讲解 make/makefile 的概念、原理和规则,从多层面理解透彻它们。

二、概念

makefile

makefile 是一个文件。它是一个工程文件的编译规则,描述了整个工程的编译链接等规则。

好的 makefile 文件可以使用一行命令来完成 “自动化编译” ,一旦写好 makefile ,就只要使用在 shell 提示符下输入 make 命令,从而完成对工程的编译,极大提高效率。

make

make 是一个命令工具,用来一个解释 makefile 中文件中的指令。

当已经编写好 makefile 文件后,只需要使用 make ,就可以执行 makefile 中的内容。

会不会写makefile,也从侧面说明了一个程序员是否具备完成大型工程的能力。

一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作。所以使用好 make/makefile ,可以使得开发更加得心应手。

一句话总结:make 是一条命令,makefile 是一个文件,两个搭配使用,完成项目自动化构建。

三、demo 实现

在讲解 make/makefile 之前,我们先写一个小 demo ,以这个 demo 为基准,对其进行讲解。

makefile 文件需要创建在当前工程的目录下,makefile 文件的名称可以为 makefile 或 Makefile

假设当前工程下已经有了一个 test.c ,我们直接开始 demo 的编写:

image-20230110135212483

这样 makefile 就编写好了,就两句话,这时编写的 makefile 可以完成对程序的编译。

我们返回终端,使用 make 就可以对 test.c 进行编译:

image-20230110135419371

使用 make 指令后,makefile 的第二行内容被打印在终端,并且生成了可执行程序 test ,test 程序也是可以执行的。

四、原理与规则

上面写了一个小 demo ,那么这个 demo 实现的原理我们还不清楚,所以接下来 a n d u i n anduin anduin 来讲一下这中间的原理和规则。

1、依赖关系和依赖方法

在 myfile 文件中,有这样一句话:

test:test.c

刚刚测试过我们知道 test 是目标文件,而 test.c 则是原始文件。

而 test.c 经过 gcc test.c -o test 生成 test 文件。

它们之间的关系

  • test 依赖 test.c 生成,所以 test.c 是 test 的依赖文件 。它们之间的关系被称为 依赖关系
  • test.c 生成 test 需要通过 gcc test.c -o test 指令,这条指令就是 依赖方法

① 感性理解

那么 依赖关系依赖方法 如何理解呢?我来举个例子来帮助大家 感性理解

例子1:

假设你在上大学,到月底了,你的生活费没了。那么这时你就打电话给你的老爹,来一个亲切的问候(要生活费)。

当你拨通电话,你说:“爸,我是你儿子!” 然后把电话挂掉。

这种情况能要到生活费吗?肯定不行,因为你只是向你的老爹 表明了你是谁(依赖关系) ,但是并没有告诉他 你要干什么(依赖方法) ,所以你的老爹一般是不会想着给你打钱,而是担心你的安全。

所以,只有依赖关系没用,还需要有依赖方法,依赖关系和依赖方法需要同时具备。

例子2

如果你在上大学,马上要考试了。你决定又给你的老爹打电话,说:“爸,我是你儿子!能不能帮我期中考?” 听完这句话,你的老爹思考了很久。于是决定连夜赶到你的学校,并请你体验了一顿 “七匹狼” 。

综上我们可以发现,你有依赖关系(表明了身份)你也有依赖方法(叫你老爹考试),但是 依赖方法错误 ,也没有达到目的,反而造成错误结果。

这就是典型的有正确的依赖关系但是依赖方法错误。

所以,正确的依赖关系必须具有正确的依赖方法。

例子3

如果你有一天,又到了月底?你随意拨通了一串号码,和陌生人说:"爸,我是你儿子!我没生活费了,给我打钱。"再以迅雷不及掩耳之势挂掉了电话。电话那头的人很懵。于是,当天晚上,你的梦里一直出现"功德-1,功德-1 … " 。

这个例子说明,你有了 正确的依赖方法(要生活费) ,但是你 没有正确的依赖关系(陌生人) ,这也办不成事。

所以,正确的依赖方法也需要正确的依赖关系。

通过以上三个例子,我们得出结论,依赖关系和依赖方法必须同时具备并正确,缺一不可

② 深层理解

我们基于上层的感性理解,再通过一个 makefile 深层理解一下:

image-20230110150254788

(实际上我们编译代码并不需要进行,这里只是为了理解 … ,平常就写成 demo 那样就可以)

在 makefile 文件中,共有四组依赖关系和依赖方法 ,当使用 make 调用 makefile 文件中内容时,便开始执行 makefile 中的内容:

  • test 依赖于 test.o ,但是 test.o 并不存在,跳转到下一组依赖关系
  • test.o 依赖于 test.s ,但是 test.s 并不存在,跳转到下一组依赖关系
  • test.s 依赖于 test.i ,但是 test.i 不存在,跳转到下一组依赖关系
  • test.i 依赖于 test.c ,test.c 存在,这时开始执行依赖方法
  • 由此开始,逐渐执行上面的依赖方法,一层层回退,逐渐生成 test.i 、test.s 、test.o ,最后生成可执行程序,make 执行完毕

我们发现,这一过程就像 数据结构的栈

当目标文件所依赖的文件不存在时,就会将依赖方法入栈,知道依赖关系匹配了,再执行相应的依赖方法,在按照栈的规则,逐渐将栈中的元素出栈,规则满足后进先出。

为了验证这些步骤是否都被执行,我们 make 一下看看:

image-20230110151502695

依赖方法对应的文件都产生了,这也说明我们讲解的步骤是正确无误的。

2、清理

平时写代码时,经常需要反复编译,执行代码。

而在下一次重新编译之前,需要清理一下上次生成的可执行程序。但是清理的时候可能清理错误,不小心把源文件删了,这时又造成了问题。

而上面的步骤,我们也生成了很多附加文件(如 test.i 等)。

所以我们基于 demo 增加一个清理功能:

image-20230110152700565

使用 make 测试一下:

image-20230110152825515

文件也都删除了。

这是为什么,新增语句是什么意思?继续讲解原理。

① .PHONY 伪目标

.PHONY 修饰的对象是伪目标,伪目标的特性是:总是被执行的。

.PHONY 修饰的一定能被反复执行,但是能被反复执行的不一定被 .PHONY 修饰。

多次执行 make 和 make clean 试试:

image-20230110153324568

(注:makefile 默认从上到下扫描只会执行第一组的依赖关系和依赖方法,所以默认执行第一组,这时使用 make 就可以;而 clean 为第二组,所以需要 make clean ,加上对应的关系。同理,对于第一组,使用 make test 也能执行。)

发现,第一组关系没有被 .PHONY 修饰,而不能重复执行。但是第二组 clean 可以重复执行

但是怎么证明 .PHONY 修饰对象之后,对象能被反复执行?口说无凭,所以,我们再验证一下:

给 test 加上修饰:

image-20230110153906181

image-20230110153930676

加上 .PHONY 修饰后,make 可以执行多次了,证明了 .PHONY 的作用。

但是能被反复执行的不一定被 .PHONY 修饰,就比如 clean

image-20230110163656271

image-20230110163725704

当 clean 去掉修饰之后,依然能被反复执行。

② .PHONY 的取舍

一般对于编译来说,是不加 .PHONY 修饰的。

因为编译是十分耗时间,特别是当工程量很大的时候,编译一两小时都不为过。所以防止对未修改的程序反复编译 ,一般编译时不加修饰。

但是 清理clean 是可以多次执行的,因为删除不太浪费时间,且可以反复清理,确认是否清理完毕。并且为了肯定清理可以被多次执行,所以通常用 .PHONY 修饰。

3、make 确定是否编译的方法

上面我们测试 make 时,发现当编译过一次后,继续使用 make 就无法继续编译了。但是 clean 是可以不加修饰反复执行的。原因我们也探讨过,但是 make 是如何确定是否要编译?

是这样的,对于程序来说,时间有两条线。第一条是源代码时间的一条线,第二条是形成的可执行程序的时间的一条线

而对于它们之间的次序,是先有源代码,再有可执行程序。

所以只要可执行程序的最近修改时间比源文件的修改时间晚,就认为当前可执行程序是最新的,为了减少时间和其他开销,于是不执行编译;否则执行编译

我们再重新生成可执行程序,并重复 make ,观察它们的时间:

观察时间,这里就要用到 stat 指令,它的 modify 就是最近修改时间 ,如果对 stat 指令不了解的小伙伴可以看这一篇:基本指令(一) (在 ls 指令部分)。

image-20230110165532255

可执行程序 test 的时间明显比 源代码 test.c 晚,所以 make 并不能起作用。

那么基于对这个概念的理解,我们能否钻空子来 欺骗一下 make

补充:

touch 指令为创建一个文件。若文件不存在则会创建一个文件;若文件存在则会把文件时间更新到最新。

使用 touch 更新一下 test.c 的时间,用 stat 观察时间,并反复 make 试试:

image-20230110170016287

image-20230110170131542

由此,我们发现可以使用 touch 来 “欺骗” make 来反复编译。这也侧面证明了: make 对于是否编译的决策是基于修改时间,而并不是基于文件内容是否修改

4、完整代码

test:test.c
	gcc test.c -o test 
.PHONY:clean                                                      
clean:
	rm -f test.i test.s test.o test

5、规则总结

  • 对于依赖关系而言,: 左边为目标文件,: 右边为依赖文件
  • 依赖方法前需要有一个 tab ,为固定格式
  • : 右边可以有多个依赖文件 ,: 右边通常被称为依赖文件列表
  • 对于 : 右边,目标文件对应的依赖文件列表可以为空 (例如 clean)
  • makefile 默认执行第一组的依赖关系和依赖方法,对于第一组可以直接使用 make 执行,后面则需要 make + 目标文件
  • .PHONY 修饰的 伪目标可反复执行但反复执行的不一定是伪目标

五、结语

到这里本篇博客就到此结束了。

实际开发中,好的 make/makefile 可以让项目开发事半功倍。所以构建自动化工具还是蛮重要的,可以大大提高开发的效率。

但是博主能力有限,对于博主当前,构建这么一个简单的工具就足够了。我们今天的重点是放在 make/makefile 的规则上。

感兴趣的小伙伴也可以往 makefile 中添砖加瓦,构建出自己开发的利器。

如果觉得 a n d u i n anduin anduin 写的不错的话,可以 点赞 + 收藏 + 评论 支持一下哦!我们下期见~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/152928.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

回顾 2022

前言回顾过去的 2022 年,感觉自己学习了不少,输出的不多。适合 Golang 新手入门的项目 go-gin-api[1],今年基本没更新,目前已收到 4.1k star,感谢大家的认可。关于健康今年室外跑步,跑了有 175 km&#xff…

什么蓝牙耳机音质好?发烧友力荐四款好音质蓝牙耳机

随着蓝牙技术的发展,现如今蓝牙耳机的音质越来越可以与有线耳机的音质相媲美了。音质作为选择蓝牙耳机的重要参考因素之一,在我们的日常生活中,不管是听歌、追剧、通话等,音质不一的蓝牙耳机都能带来不一样的听觉体验。下面&#…

提高1--数字三角形模型

1.摘花生 Hello Kitty想摘点花生送给她喜欢的米老鼠。 她来到一片有网格状道路的矩形花生地(如下图),从西北角进去,东南角出来。 地里每个道路的交叉点上都有种着一株花生苗,上面有若干颗花生,经过一株花生苗就能摘走该它上面所有的花生。 Hello Kitty只能向东或向南走,不…

Java基础知识复习

文章目录复习1. Java编译和运行2. Java运行环境3. Java注释4. Java中的打印5. 数据类型整形浮点型字符类型字节类型布尔类型数据类型转换强制类型转换隐式类型转换小结6. 变量命名规范7. 运算符算数运算符逻辑运算符移位运算符关系运算符8. switch9. 方法方法的重载可变参数变程…

http请求走私详解

前言 HTTP请求走私是一种干扰网站正常处理从一个或多个用户收到的HTTP请求的方法,请求走私漏洞在本质上通常很关键,允许攻击者绕过安全控制,获得对敏感数据的未授权访问,并直接危害其他应用程序的用户。 随便提一句:…

关系抽取概念及相关论文解读

1.信息抽取概述 信息抽取是构建知识图谱的必要条件。知识图谱中以(subject,relation,object)三元组的形式表示数据。信息抽取分为两大部分,一部分是命名实体识别,识别出文本中的实体,另外就是关…

【dp】排列问题——零钱兑换和组合总和IV

文章目录零钱兑换组合总和IV零钱兑换 很明显,本题使用完全背包算法,求解的是组合数,直接使用完全背包算法即可,为什么是组合数呢? 如果题目说 amount5,coins[1,2,5] 有9种方法,那就是排列数&am…

探秘SSD磨损均衡和闪存管理

一块2TB的SSD要进行3000PE耐久度测试,需要在顺序写入方式下(写入放大比接近1)写入2TB x 30006000TB的数据,这需要相当长的时间才能完成。有朋友可能会问:在SSD里划分一个1GB容量的小分区,往里面覆盖写入300…

2022年总结:责任驱动开源

文章目录概览阅读2022阅读清单2023 阅读目标文章2022文章列表2023 目标社区WeDataSpherecube-studio (aiops平台)社区展望小结概览 2022年一年过去,在此总结过往,展望未来,每年都写总结,我希望继往开来&am…

STC单片机基于Keil平台在线调试仿真

STC单片机基于Keil平台在线调试仿真⚡目前STC32G12K128型号的单片机仅支持需要通过SWD接口(STC-USB LINK 1D)进行调试,STC8H系列可以通过HID接口或者串口进行调试,其他不带HID功能型号的只能通过串口进行调试。 ✨初次调试&#x…

RHCE——编写playbook ansible

1.按照要求定义以下变量。(可以在多个位置下定义实现相应操作即可) | 变量名 | 值 | | --------- | ------- | | file_name | newfile | | user_name | devops | | pk_name | httpd | 2.编写任务模块在node1和node2主机中根据以上变量值创建对应文本文件…

IPC进程间通信-管道

🧸🧸🧸各位大佬大家好,我是猪皮兄弟🧸🧸🧸 文章目录一、了解进行间通信①进程间通信的必要性②进程间通信的技术背景二、管道①管道原理管道原理,三步走管道pipe②匿名管道进程间通…

CANoe-System And Communication Setup介绍

如同经典通信模式中,在Simulation Setup界面创建网络、添加节点、导入dbc数据库一样,新型通信模式使用System And Communication Setup完成这些配置 System And Communication Setup是通信配置的中心起点,在这里你可以配置: 应用层(application models)通信层(communic…

【自学Python】Python bytes类型

Python bytes类型 Python bytes类型教程 Python3 新增了 bytes 类型,用于代表字节序列。 字符串(string) 是一串字符组成的序列,字符串处理的基本单位是字符,bytes 类型是一串字节组成的序列,bytes 类型处理的基本单位是字节。…

三维重建基础: 坐标系 (更新中)

文章目录概述计算机视觉:投影矩阵M: 世界坐标系 -> 像平面坐标系相机坐标系 -> 像平面坐标系:世界坐标系 -> 相机坐标系计算机图形:MVP变换view/camera 变换常见坐标系定义colmap中的坐标系线性代数相关基础待办附录概述…

微信小程序安装 Vant 组件库与API Promise组件库

在项目内右键空白处选择在外部终端打开2、在终端窗口输入 npm init -y,创建package-lock.jsonnpm init -y3、在终端输入npm i vant/weapp1.3.3 -S --production,创建node_modules文件夹npm i vant/weapp1.3.3 -S --production4、详情-本地设置&#xff0…

安信证券资管清算重要业务在原生分布式数据库的创新实践

作者:安信证券信息技术委员会运维部系统平台室 刘盛彩、肖昭、张杰 来源:《金融电子化》 近日,《国务院关于数字经济发展情况的报告》(以下简称报告)提请十三届全国人大常委会第三十七次会议审议, 报告提出&#xff1a…

【Vue + Koa 前后端分离项目实战8】使用开源框架==>快速搭建后台管理系统 -- part8 【权限控制联调】

不要为了不值得的事和人感伤悲秋 本章主要关联章节:【Vue Koa 前后端分离项目实战3】使用开源框架>快速搭建后台管理系统 -- part3 权限控制行为日志_小白Rachel的博客-CSDN博客_koa权限管理 本章主要实现综合的系统权限管理 目录 一、权限控制说明 二…

【Linux】Linux常用指令(28个,以及热键,关机重启)

文章目录1、ls 指令2、pwd 命令3、cd 指令4、touch 指令5、mkdir 指令6、rmdir和rm 指令7、man 指令8、cp 指令9、mv 指令10、cat11、echo(输出,输入,追加重定向)12、wc13、more14、less15、head16、tail(以及管道 | )17、date18、cal19、find(which、wh…

KubeSphere使用外部ES进行日志收集(多行日志)

环境kubesphere : v3.3.1Docker:20.10.8Fluent-Bit:2.0.6-2.0.8ESKibana:7.9.3Docker日志示例{"log":"2023-01-10 11:32:50.021 - INFO --- [scheduling-1] traceId: p6spy : 1|conn-0|statement|SELECT fd_id A…