【Linux】项目自动化构建工具 —— make/Makefile

news2024/11/18 3:32:27

前言:

在上一期的博文中,我们对 Linux 下的编译器 - gcc/g++的使用进行了详细的讲解,今天我将给大家讲解的是关于 【Linux】项目自动化构建工具 —— make/Makefile  的详细使用教程!!


本文目录

(一)前情摘要

(二)背景介绍

1、Makefile 的基本认识

2、make 的基本认识

3、代码实现

(三)依赖关系与依赖方法

1、基本概念

 2、深入理解

(四)项目清理

1、代码演示

2、.PHONY原理

(五)三个时间

1、make的判断机制

(六)总结


(一)前情摘要

在上一期我们将 Linux编译器-gcc/g++使用 的使用的时候,我们通过对 C语言翻译的逐过程讲解知道了可执行程序 【a.out】的来历。

整体过程大概划分为以下几个步奏: 👇

  • 第一步,就是进行预处理:文件从【test.c】经过预编译最终形成【test.i
  • 第二步,经过预处理之后,文件从【test.i】经过编译最终形成 【test.s
  • 第三步,经过编译之后,文件从【test.s】经过汇编最终形成 【test.o
  • 第四步,经过汇编之后,文件从【test.o】经过链接最终形成 【a.out

 💨  以上经过编译 加上链接的过程,大家有没有觉得看着十分的复杂,对于学习初期拿来练练手了解一下原理还可以,但是若有一天大家到公司中,面对那些大型的工程项目,轻则成千上万行代码,重则十几万甚至更多。

 💨  若是一次编译完成之后又修改了源代码,接着又想进行编译,此时便需要重新敲入指令,那会使得工作量变得很大,还显得十分的低效。

因此,基于上述原因,我们就诞生出了本期将要介绍的 ——> 【make/Makefile】

  • 利用 make工具,我们可以将大型的开发项目分解成为多个更易于管理的模块,对于一个包括几百个源文件的应用程序,使用 make 和  makefile 工具可以十分简单的整理好各个源文件之间的关系。

💥 优势:

  1. 包含多个源文件的项目工程在编译时有很多复杂且指令较长的命令行,这时我们可以通过makefile 保存这些命令行来简化该工作;
  2. 其次,make 也可以减少重新编译所需要的时间,因为 make 可以精准的识别出项目中的文件有哪些是经过修改变化的;
  3. make 维护了当前项目中各文件的相关关系,从而可以在编译前检查是否可以找到所有的文件

接下来,我们就对 【make/Makefile】 进行详细的讲解!!!


(二)背景介绍

在正式应用之前,我们先对这两个进行概念上的理解!!!

1、Makefile 的基本认识

定义:

  • MakeFile 是一个GNU推出的 编译开发工具,能为一些编译过程提供服务。
  • Makefile 的本质其实是一个文件。它是一个工程文件的编译规则,它记录了原始信息如何编译的详细信息、描述了整个工程的编译链接等规则。

功能:

  • 一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的 规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂 的功能操作;
  • 举个例子,我们上述提到的文件翻译过程形成的那么多的文件,怎么把他们编译成最后能够执行的可执行程序呢?
  • 我们就可以用 MakeFile 来辅助你编译,用了MakeFile除了能提升效率,还能避免人为操作导致错误。

makefile基于两个重要关系:

  • 依赖关系:定义了如果你想要建立一个项目或者目标,有什么是必须要做的;
  • 时间关系:根据文件的属性时间。make 程序判断哪些文件在项目过程中发生了修改。然后根据上述的依赖关系,把依赖于这些修改文件的目标文件重新确立一份关系。
 

紧接着我们来看一下【MakeFile】的语法介绍:

<target1 > <target2>.... : <prerequisites1> <prerequisites2>...
[TAB] <command1>
[TAB] <command2>
...

命令解析:

  1. 【target】:可以是一个object file(目标文件),也可以是一个执行文件,还可以是一个标签(label);
  2. <prerequisites>:就是要生成的那个 target 所需要的文件或者目标;
  3. <command>:也就是 make 需要执行的命令;

很明显我们可以发现这是一个文件的依赖关系,<target> 这一个或多个的目标文件依赖于<prerequisites> 中的文件,其生成规则定义在 <command>中。

说白一点就是说,<prerequisites>中如果有一个以上的文件比<target>文件要新的话,<command>所定义的命令就会被执行。这就是makefile的规则。也就是makefile中最核心的内容。

小结:

  • makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编 译,极大的提高了软件开发的效率。因此,会不会写 makefile,从一个侧面说明了一个人是否具备完成大型工程的能力

2、make 的基本认识

定义:

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

3、代码实现

 有了以上概念的了解,接下来我们就手动的去实现一下这两个基本工具:

  • 第一步:上述我们可以知道 make 命令是用来解释 Makefile 中指令的,所以我们需要创建一个文件进行编辑,具体如下;

  •  第二步:我们再创建一个【myfile.c】的文件,作为源文件,然后开始往里面写内容

  •  第三步:回到命令行,我们执行一下make指令,它就是自动在当前源文件的所在路径下搜寻Makefile,并解释里面命令,最后生成myfile

  •  第四步:此时当我们想删除应该怎么办呢?还是跟之前一样一条条的去【rm】吗?答案是不用,此时我们在【Makefile】中在写入一段即可帮助我们完成这项工作:

综上所述:make是一条命令,makefile 是一个文件,两个搭配使用,完成项目自动化构建。


(三)依赖关系与依赖方法

在上述我们讲【Makefile】时,我提到过关于依赖关系这个话题,可能许多小伙伴就是满头雾水,什么是依赖关系呢?依赖关系在这里怎么体会的呢?接下来,我们就在仔细讲讲这个 “依赖关系”

1、基本概念

在上述我编写的 【Makefile】文件中,对于 myfile : myfile.c 

  • 对于冒号左侧:我们看做是目标文件;
  • 对于冒号右侧:我们可以看做是它的依赖文件;

因此我们就可以说它们之间存在一种【依赖关系】只有【myfile.c】存在才有【myfile】


 2、深入理解

上述,我们在【Makefile】中写入的是可以直接对文件进行编译,按照上节课我们所讲的在代码翻译为可执行程序的期间,经历好几个过程。

  • 因此,接下来我们在通过对其代码翻译的几个过程来深入理解——》【依赖关系和依赖方法】

因为可执行文件【myfile】是依赖于汇编后形成的目标文件 【myfile.o】,但是现在我们并没有这个文件,因此就要去倒推一下如何获取这个 【myfile.o】

  • 💨 对于【myfile.o】来说,它依赖于myfile.s这个经过编译之后文件,可是myfile.s此时也不不存在,所以跳转到下一条依赖关系
 myfile.o:myfile.s
    gcc -c -o myfile.o myfile.s

  • 💨 对于【myfile.s】来说,它依赖于myfile.i这个经过编译之后文件,可是myfile.i此时也不不存在,所以跳转到下一条依赖关系
myfile.s:myfile.i
     gcc -S -o myfile.s myfile.i
  • 💨 对于【myfile.i】来说,它依赖于myfile.c这个经过编译之后文件,此时源文件存在,执行gcc指令
myfile.i:myfile.c
     gcc -E -o myfile.i myfile.c

修改后如下图所示:

  •  💨最后,我们回到命令行,直接输入【make】指令即可完成所有的工作

 小结:

  • 【依赖关系】中,若是目标文件所依赖的文件不存在,就将这个【依赖方法】放入栈中,紧接着转到下一组 【依赖关系】,以此类推,直到找到当前目标文件所依赖的文件,此时就进行出栈操作,开始执行 【依赖方法】,最后获取得到的便是我们最初的目标文件!!!


(四)项目清理

1、代码演示

在上面的内容中我们已经对这个知识点进行应用了,这里我们在详细的展开说说

  • 当我们的【Makefile】文件中写入 删除【myfile】时,此时我们返回命令行,紧接着再去执行一下【make clean】之后,我们可以发现目录下的【myfile】文件已经被删除了

  •  当我们想删除代码翻译过程的形成的文件时,一样的,我们可以这样去做:
  • .PHONY:clean
       clean:
       rm -f myfile.i myfile.s myfile.o myfile   

  • 像clean这种,没有被第一个目标文件直接或间接关联,那么它后面所定义的命令将不会被自动执行, 不过,我们可以显示要make执行。即命令——“make clean”,以此来清除所有的目标文件,以便重编译。

2、.PHONY原理

但是一般我们这种clean的目标文件,我们将它设置为伪目标,【 .PHONY】 修饰,伪目标的特性是,总是被执行的

  • 首先回答一下什么叫做总是被执行呢?接下来,我举个例子来说明:

  •  此时上述这种现象即为不能总是被执行

  • 那么我们想总是被执行有没有什么方法呢?此时我们只需在前面加上【.PHONY】即可实现这样的操作,具体如下:

  •  此时,当我们再去执行上述操作时,我们就可以发现此时系统不会在提示了,可以编过:

 小结:

  • 【.PHONY】后面跟的目标都被称为伪目标,也就是说我们 make 命令后面跟的参数如果出现在.PHONY 定义的伪目标中,那就直接在Makefile中就执行伪目标的依赖和命令。
  • 不管Makefile同级目录下是否有该伪目标同名的文件,即使有也不会产生冲突。另一个就是提高执行makefile时的效率。

(五)三个时间

首先,我们先来看看以下现象,有了这个现象的产生,紧接着我在带大家理解一下 几个时间的概念!!!

  • 在上述删除操作中,我们可以发现,我们是把删除操作放在最后面的,那么此时我们放在最前面会发生什么呢?我们一起看看:

  • 可以看到,在调换了位置之后我们直接【make】的话获取的就是clean对象了,想要去使用gcc编译源文件生成可执行文件就需要用到【make myfile】。
  • 不过也并不是第一组依赖关系和依赖方法就一定要直接【make】,我们使用【make clean】也是可以用的


1、make的判断机制

此时,很多小伙伴就会好奇,它是怎么知道的呢?make究竟是如何知道我们的可执行文件是否需要重新编译呢?

接下来,我再给小伙伴们看一个现象:

  • 上述我们已经得知,当我们不加伪目标时,当执行完一次【make】获取 【myfile】之后,当我们再次执行【make】系统是会提示 “此时已经是最新文件”;
  • 但是,同样的,当我们执行完第一次【make】之后,此时去打开一下源文件,即【myfile.c】之后,我们在【make】一下,此时就又可以正常的编译通过了

 解析如下:

  • 我们可以将源文件和可执行文件当做是一条时间轴。对于可执行文件来说,它生成的时间一定是晚于源文件的【因为中间要经过一系列编译 + 链接的过程】

接下来我们可以通过【stat】这个指令来查看源文件和可执行文件的所有属性,具体如下:

  • Access time:表示最后一次访问(仅仅是访问,没有改动)文件的时间。
  • Change time:表示最后一次对文件属性改变的时间,包括权限,大小,属性等

  • Modify time:表示最后一次修改文件的时间(我们比较的是这个)

 从上我们可以看出 【20:43:00】是比【20:43:01】要早的。因此【make】指令才会不起作用

  • 因此我们得出结论它就是通过这个Modify时间来进行对比才能判断出是否需要重新编译


那我们能否可以去欺骗一下make呢?

答案是可以的,此处我们可以使用这个【touch】指令,它除了创建文件之外还有其他功能哟!

  • 若是要创建的这个文件存在,那就修改它的时间为最新的时间
  • 若是要创建的这个文件不存在,那就直接创建该文件即可

接下来我们来看看现象是怎么回事:

 

小结:

         1)当仅读取或访问文件时,access time 改变,而modify time ,change time 不会改变。

         2)当修改文件内容时,modify time ,change time 会改变,access time 不一定改变。

         3)当修改文件权限属性时,change time 改变,而access time ,modify time 不会改变。

这就是我们刚开始时将【Makefile】定义时提到的关于 时间关系 的详细解答!!!


(六)总结

到此,关于 make/Makefile 的知识点便全部讲解结束了。在后面的学习中,我们还将进一步的对其进行讲解!!!

接下来,我们来回顾一下本文都有哪些知识点:

  • 1、首先我们简单的回顾了一下代码翻译的过程,紧接着根据其在执行过程中存在的弊端,我们引出了 make/Makefile ;
  • 2、在正式介绍之前,我们了解了关于二者的背景知识,知道了【Makefile】它是一个文件,【make】它是一个命令;
  • 3、接着我们简单的实践了一下,再次基础上引出了 【依赖关系】和【依赖方法】,并对其进行了讲解;
  • 4、接下来,掌握了如何去清理项目中的文件的 小妙招,并知道了【 .PHONY】修饰的文件叫做【伪目标文件】;
  • 5、最后我们知道了【make】判断一个文件是否需要重新编译的原理,是基于比较源文件目标文件的【Modify】时间而定的。

以上便是本文的全部知识了,感谢大家的阅读!!!

 

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

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

相关文章

ai智能文章生成器-ai论文写作

在数字时代&#xff0c;营销推广策略已经向数字化方向发展。今天我们要介绍的是一款名为“智能ai写作免费”的软件&#xff0c;它可以让营销人员轻松地创作新的内容&#xff0c;并且其中不需要过多的技术知识或文学背景。这款软件可以为许多企业和机构带来创造性的帮助。 智能A…

婴幼儿口水巾围兜出口美国CPC认证

婴幼儿口水巾、围兜在美国销售需要办理CPC认证&#xff0c;即儿童安全产品证书。 什么是CPC认证&#xff1f;美国亚马逊儿童玩具和儿童用品CPC认证是指颁发书面儿童产品证书&#xff08;CPC、&#xff0c;其中制造商或进口商证明其儿童产品符合所有适用的儿童产品安全规则。儿童…

被盗的ChatGPT账户在暗网热销,ChatGPT的隐私和安全问题依旧值得关注

在过去的一个月&#xff0c;Check Point研究人员在暗网上观察到了与ChatGPT相关的各种讨论和交易。暗网上最新的活动包括泄露和免费发布ChatGPT账户的凭据&#xff0c;以及交易被盗的ChatGPT账户。 根据Check Point进行的一项研究&#xff0c;从今年3月以来&#xff0c;被盗的…

QPSK调制解调FPGA实现成果展示:

目录 QPSK调制解调使用参数&#xff1a; 调制&#xff1a; 解调&#xff1a; FPGA工程架构&#xff1a; 仿真参数&#xff1a; 仿真展示&#xff1a; 调制&#xff1a; 解调&#xff1a; MATLAB星座图展示&#xff1a; QPSK调制解调使用参数&#xff1a; 采样率为4M&…

Revit插件 | 建模助手下半年规划大揭秘!

​hi&#xff0c;这里是建模助手。 在上周推送『建模助手年度版本大更新』后&#xff0c;很多盆友和我们反馈这次的升级&#xff0c;真被细节打动。 版本的兼容性更给力了&#xff0c;避免了某版本的Revit不可用的现象&#xff1b; 有求必应板块更细致了&#xff0c;新增了阶…

breakpad编译指南(Windows)

在编译breakpad时候&#xff0c;遇到各种各样的问题&#xff0c;做些记录&#xff0c;以便后续参考 Windows下已有SetUnhandledExceptionFilter可以抓取dmp&#xff0c;为什么还要研究breakpad&#xff1f;因为breakpad是跨平台的&#xff0c;相关资料参考如下&#xff1a; 02…

迅为i.MX6ULL开发板I2C总线实现driver驱动

上一章节我们写了 client.c&#xff0c;并且我们已经成功地把它加载到内核里面。i2c 用非设备树实现&#xff0c;我们需要 用 i2c_board_info 这个结构体来描述我们的 i2c 设备&#xff0c;如果我们用设备树的方法来实现&#xff0c;我们直接在设备树 的节点下面添加创建对应设…

关于File.io的学习记录(读取文本)

可以通过字节流&#xff08;FileInputStream&#xff09;、字符流&#xff08;InputStreamReader&#xff09;、字符缓冲流&#xff08;BufferedReader&#xff09;读取文本中的数据。 1、FileInputStream读取文本 public void read(){String path "fileTest.txt";F…

JDK新增史上最无用提案!竟是为了简化Hello World?

前两天JDK 20更新了&#xff0c;很多人表示很失望&#xff0c;但是我万万没想到的是&#xff0c;还有更令人失望的。 OpenJDK最近又增加了一个新提案&#xff0c;JEP 445&#xff0c;这个提案的主要内容是要简化Hello World的写法。并且该新特性即将在Java 21中作为预览功能推出…

凌恩生物文献分享|多组学联合,病毒与宿主关联分析又升级了!

病毒是地球上数量最多的生物实体&#xff0c;在调控宿主群落组成、推动宿主进化及影响土壤元素的生物地球化学循环等方面起着非常重要的作用。病毒基因组结构简单&#xff0c;一般只含有单一核酸&#xff0c;探究病毒基因组在病毒的毒力系统、病毒的基因组进化与演变历程方面具…

FlinkX的安装与使用(异构数据同步工具——flinkx)

异构数据同步工具——flinkx - 知乎 一、概要简介 FlinkX是由袋鼠云开源基于Flink的分布式离线和实时相结合的数据同步框架&#xff0c;既可以采集静态的数据比如&#xff1a;MYSQL,HDFS等&#xff0c;也可以采集实时变化的数据比如&#xff1a;MYSQL BINLOG,KAFKA等。目前官方…

【1187. 使数组严格递增】

来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 描述&#xff1a; 给你两个整数数组 arr1 和 arr2&#xff0c;返回使 arr1 严格递增所需要的最小「操作」数&#xff08;可能为 0&#xff09;。 每一步「操作」中&#xff0c;你可以分别从 arr1 和 arr2 中各选出一个…

如何在苹果Mac OS系统中安装MT4电脑版软件?

很多投资者可能已经发现了&#xff0c;当前市面上绝大部分的正规交易平台&#xff0c;都在使用MT4电脑版软件作为平台专用的交易工具。而市面上的电脑系统主要又分为微软Windows系统和苹果Mac OS系统&#xff0c;对于微软Windows系统的MT4电脑版软件安装&#xff0c;想必大家都…

若依Linux环境部署

若依Linux环境部署 1.若依项目2.后端3.Linux环境配置4.环境部署附录 1.若依项目 若依官网若依文档前后端分离代码 2.后端 整体项目结构 自己创建好数据库&#xff0c;并在如下地方修改代码 日志路径也需要修改 数据库建表语句 同时redis也要启动并配置好 后端启动 3.…

高级篇三、用户与权限管理

第03章_用户与权限管理.pdf 1、用户管理 1.1 登录MySQL服务器 mysql -h xxx -P xxx -u xxx -p DatabaseName –e "SQL语句"下面详细介绍命令中的参数&#xff1a; -h参数 后面接主机名或者主机IP&#xff0c;hostname为主机&#xff0c;hostIP为主机IP。-P参数 后…

如何在Google Play设置置顶大图

应用的置顶大图是Google Play商店上ASO优化的重要一环&#xff0c;是我们保持品牌形象的重要场所。如果我们没有设置预览视频&#xff0c;那么Google Play 将会使用置顶大图&#xff0c;如果添加了预览的视频&#xff0c;那么视频会放置在置顶大图的顶部。 添加游戏或应用程序…

关于Redis的BigKey

文章目录 准备keys * 等命令的危害与避免不用keys * &#xff0c;应该用什么BigKey阿里云Redis开发规范多大算Big危害怎么产生的&#xff1f;怎么发现BigKey怎么删除String类型使用hscan每次获取少量field-value&#xff0c;再使用hdel删除每个field使用ltrim渐进式逐步删除&am…

管理后台项目-02-平台属性组件模块

目录 1-分类三级列表 2-属性列表 2.1-需求功能分析 2.2-功能代码实现数据获取和变动 2.3-动态渲染数据 3-新增修改属性 3.1-点击新增属性逻辑处理 3.2-点击列表页面修改属性逻辑 3.3-新增和修改属性 3.4-点击删除属性值 3.5-点击删除属性 1-分类三级列表 需求&…

通过案例来了解响应式开发的音频控件

目录 前言 一、视频控件的使用方法 1.语法 2、部分功能介绍 二、案例举例 三、播放效果 前言 1.本文讲解的响应式开发技术&#xff08;HTML5CSS3Bootstrap&#xff09;的HTML5表单等功能方法的代码&#xff0c;这也是很多教材的一个典型案例&#xff1b; 2.本文将讲解涉…

IMX6ULL裸机篇之中断实验-通用中断驱动说明二

一. 通用中断驱动 本文是 IMX6ULL 裸机篇---中断实验。旨在用 C 语言编写一套简单的中断驱动框架代码。 在 start.S 文件中&#xff0c;我们在中断服务函数 IRQ_Handler 中调用了 C 函数 system_irqhandler 来处 理具体的中断。 本实验会认识中断控制器&#xff1a;…