一文入门Makefile

今天我们来玩玩Makefile。

这边是借鉴的陈皓老师的《跟我一起写 Makefile》

pdf下载链接如下。

链接:https://pan.baidu.com/s/1woRq2nEkgzLv1o5uE0FZHg?pwd=mhrh 
提取码:mhrh

我们之前已经算是入门了gcc,那我们的下一站就是Makefile,上一篇我们也发现了,当我们的main.c包含了自己写的头文件,那么编译命令会变得很长,如果包含的头文件很多,那么光是敲编译命令都够花我们半条命了。

所以也有下面这个说法。

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

那么Makefile到底是做什么的呢?简单来说,Makefile通过定义一系列编译规则,指导make工具如何根据源文件的修改情况来执行增量编译,从而极大地提高了开发效率,Makefile写出来也就是一个脚本文件。

使用Makefile不仅使得编译过程自动化,还带来了诸多好处:

  • 自动化处理复杂的依赖关系:Makefile能够自动处理和解析项目中各文件之间的复杂依赖关系,确保只有必要的部分被重新编译。
  • 支持跨平台构建:通过编写与平台无关的Makefile,可以在不同操作系统间实现一致的构建过程。
  • 提高开发效率:开发者只需关注源代码的编写,编译过程的大部分操作都由Makefile自动完成,减少了手动操作的错误和时间消耗。

那么废话不多说,我们直接开始先写一个Makefile吧。

先别管这里的内容是什么意思,这内容我们写到Makefile里,Makefile就是这个脚本文件的名字,没有后缀,当然了也可以叫makefile和GNUmakefile(最好不要叫这个,因为只有GNU make才能识别)。

我们写好之后要执行Makefile,就直接执行一个make命令。

然后就可以看到执行了一堆命令,这些命令是我们写在Makefile里面的。

并且生成了下面一堆中间文件。

那其实大部分情况我们是不需要中间文件的,那么我们是不是也可以把删除中间文件的命令写进Makefile里呢?那当然是可以了,因为Makefile本质上就是脚本文件,自然是可以写的。

然后我们执行

make clean

可以看到执行了我们写在Makefile里面的最后的那个语句。

接下来我们开始揭秘Makefile。

我们先看第一行test:test.o,这实际上是一个规则。这段的含义是我们需要生成test(也就是目标文件),为了生成test首先需要拥有test.o(也就是依赖文件),通过执行下面首行缩进一个tab的那个命令来生成。

如果当前目录下没有依赖文件,那么就会在Makefile里找有没有目标文件是依赖文件的规则。

那么前四个规则大家应该就能看懂了。

但是第五个clean它不是简单的规则,它是一个特殊的目标,一般用来删除编译过程中产生的中间文件,并且我们看到它是没有依赖的。

执行的时候就是输入命令 make clean。

因为clean涉及到删除,而在Linux中删除就是真删除了,不像Window里还能从回收站里找到,因此编写clean的时候我们需要格外小心,但是保不齐我们会失误,那怎么办呢?

我们可以在make clean后面加个-n选项来模拟删除,然后再次检查是否出错,此时还不是真的删除,因此还有改正的机会。

如上可以看得出来当我们执行make clean命令加上-n选项之后,它会展示出执行的删除命令,但是并没有真正执行,这时我们就可以对make clean 进行检查了。

除了直接在Makefile里写clean,更稳健的写法是下面这样的。

 其中.PHONY表示后面的clean是一个伪目标,并不是正常的目标。并且在 rm 前面加上了一个 - ,这表示当我们删除某些不存在的文件的时候忽略报错,接着往后执行。

当然还有个潜规则就是clean放在Makefile的最后面。

接着再回到我们的第一版最简单的Makefile,它是由多个规则构成的,每个规则里有目标文件,依赖文件和执行命令。在Makefile里,它默认会把第一个规则中的目标文件当成是最终目标,也就是说它不一点会执行每个规则,而是只要能够生成最终目标就行。

要指定最终目标也是可以的,在Makefile开头加上ALL:最终目标即可。像下面这样,实际上不一定非得叫ALL,原理就是Makefile会以第一个规则为最终目标,那么我放在第一行的那就是最终目标。

上面就是Makefile最基础最基础的内容了。

那接下来我们就可以更进一步了。

既然Makefile是脚本文件,那么它有变量也是正常的对叭。

一般写在文件开头,格式就是  变量名 = 变量值 ,当我们使用时是这样的 $( 变量名 ),这边需要提的是Makefile中的变量比较接近于宏定义,也就是直接替换的,没有类型。

光讲可能比较抽象,我们直接看下面的例子。

上面例子把目标文件直接赋值给了变量targetFile,我们在规则中可以使用$(targetFile)的方式取出test的这个值去使用,当然了上面只是简单地做个示范,我们还可以拿变量去装一些更复杂的东西,比如说一堆的文件(没错,一个变量能装的可不止一个文件,用空格隔开可以装多个)

那我们要给变量赋值赋上一堆文件,除了一个个手敲上去,还有别的更省力的方式那就是通配符。

比如说我们需要给一个变量赋值当前目录下所有的 .c 文件,那么我们用通配符就可以非常方便的赋值。

大家想的是下面这样的对叭。

cfile = *.c

但是实际上这样是不行的,这样在Makefile中,cfile这个变量的值就是 *.c ,而不是我们想要的所有 .c 文件,这时候就需要用上函数了,既然Makefile是脚本文件,那么有个函数也是很正常的对叭。

正确的写法是下面这样的。

cfile = $(wildcard *.c)

这个函数就是 $(wildcard  ),在括号里,wildcard后写的通配符就会被展开,就会是我们想要的效果了。

除了上面我们自己去写变量,Makefile里有一些自动化变量我们可以直接去使用的,我们挑几个常用的来说。

$@ : 本条规则中的目标

$< : 本条规则中第一个依赖,如果第一个依赖代表多个文件(后面会说怎么实现),那么$<就表示为将这多个文件一个个取出来。

$^ :本条规则中的所有依赖,用空格分隔。

$* : 可以简单理解成目标文件的名字但是不含后缀名。

$? :比目标更新的依赖,也就是最后修改时间比目标更晚的依赖。

用了上面的自动化变量,那么我们第一版的Makefile可以写成下面这样了。

现在我们改一改我们的test.c,像下面这样。包含了自己写的头文件,用了里面的方法。

对应的在Makefile里也需要修改。我们添加了对于mytool1的编译。

是可以正常make的。

然后我们发现,后面两个规则中的命令是一样的,那么实际上我们是可以对他们进行一个合并操作的,也就是说在一个规则里,可以有多个目标。可以像下面这样改写,但是由于gcc -o选项只能输出一个文件,因此在命令里不能使用 $@,但是还好-c选项可以不指定输出文件,默认就是输入文件的后缀改成 .o当输出文件了。

也是可以正常make的。

这个例子主要是想说在Makefile中,在一个规则里可以有多个目标。然后同一个目标也可以有多个规则(这边不演示了)。

然后实际上我们还有种更简单的写法,那就是模式规则。

这边直接贴出怎么写的。

看不懂什么意思没关系,我们make一下,make之后会显示出它执行的命令,然后我们再分析分析。

看的出来Makefile直接帮我们把我们需要的依赖都执行了。

这是因为上面的%可以看成是任意的东西(类似于通配符中的*),当我们的目标的依赖不存在的时候,Makefile就会在规则中寻找是否目标的依赖和哪个规则的目标一致,找到就会执行这个规则中的命令。就比如说我们上面的Makefile,需要寻找目标为test.o以及目标为mytool1.o的依赖,最终都找到了 %.o:%.c 这个规则,因为%代表任意东西,那么自然是可以匹配上的,这就是模式规则强大的地方,我们可以少写很多东西了。

不过上面这种写法有一个可以改进的点,那就是将$^改成$<,我们来看看二者的区别。

我们上面的例子简单,所以没什么差别,但是当我们的依赖变多之后,依赖嵌套依赖之后,我们就得用$<了。

使用模式规则也有个弊端,那就是模式规则会匹配所有符合条件的,因为%可以匹配任意东西,因此只要后缀符合,都会被我们的模式规则匹配到,如果我只有一部分的文件需要用模式规则中的命令怎么办呢?

这就要用到我们的静态模式规则了。

在模式规则的基础上,在前面加上需要匹配到的目标即可。

可能有小伙伴会说,这样不就又绕回去了嘛,还是要把目标的名字都写出来。那其实我们可以再静态模式规则的后面再写个模式规则,而只在静态模式规则中写上需要特殊处理的目标即可,剩下的可以都丢给后面的模式规则去兜底。

一般情况下我们不会将工具文件和我们的主文件放在同一级目录下,因此我新建个文件夹my_tools,然后将工具文件放进去,接着我们就毫不意外地发现Makefile找不到库文件了。

我们可以在Makefile中指定头文件目录,指定的方式也很简单,就像下面这样。

VPATH = 路径1:路径2……

可以指定多个路径,用冒号:隔开即可。

指定完之后仍然报错,但这就是链接阶段报的错误了。

我们需要在规则的命令中添加头文件路径。

最终是下面这样的。

可以正常运行,在Makefile执行规则命令的时候也帮我们把头文件路径加上去了。

VPATH是一个特殊变量,指明了这个特殊变量,当Makefile找不到依赖文件,也没有目标是依赖的规则,那么Makefile就会到VPATH指明的路径去寻找。

除了VPATH还有vpath,也就是小写的,但是vpath不是变量,而是关键字,使用可以参考下面这样。

看的出来vpath一次只能添加一条路径,但是和VPATH相比的话,vpath可以更细致地指定路径,可以给不同后缀的文件指定不同的路径。

至此我们就算是入门Makefile成功了,相信各位小伙伴已经能够给自己以前写的小项目编写(相对)高效的Makefile了。

如果想进一步了解Makefile的话,最好的方法是去看官方文档,当然了,也可以看看陈皓老师写的《跟我一起写 Makefile》,下载链接在本文的开头。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

每日一题——Python实现PAT甲级1144 The Missing Number(举一反三+思想解读+逐步优化)四千字好文

一个认为一切根源都是“自己不够强”的INTJ 个人主页&#xff1a;用哲学编程-CSDN博客专栏&#xff1a;每日一题——举一反三Python编程学习Python内置函数 Python-3.12.0文档解读 目录 我的写法 时间复杂度分析 空间复杂度分析 总体空间复杂度&#xff1a;O(N) 总结 我…

洗地机什么牌子耐用?四款高品质洗地机型号强烈安利

在快节奏的现代生活中&#xff0c;保持家庭清洁成为了许多人的挑战。传统的清洁方式不仅耗时费力&#xff0c;还难以彻底清洁地板上的污渍和毛发。特别是对于有宠物的家庭&#xff0c;毛发的清理更是让人头疼。如果有一款洗地机&#xff0c;既能高效清洁又能省时省力&#xff0…

Matlab|风光及负荷多场景随机生成与缩减

目录 1 主要内容 计算模型 场景生成与聚类方法应用 2 部分程序 3 程序结果 4 下载链接 1 主要内容 该程序方法复现了《融合多场景分析的交直流混合微电网多时间尺度随机优化调度策略》3.1节基于多场景技术的随机性建模部分&#xff0c;该部分是随机优化调度的重要组成部分…

从0到1构建自己的短链接系统

1. 短链系统简介 1.1 短链系统的定义与用途 短链系统是指将一个较长的URL地址&#xff0c;通过特定的算法生成一个较短的、具备唯一性的URL地址。这种系统广泛应用于社交网络、短信、邮件营销等场景&#xff0c;它能帮助用户在字数受限的情况下分享链接&#xff0c;并且还具有…

6-47选择整数计算

整数计算&#xff1a; 用swing组件来实现整数计算&#xff0c;需要对整数计算的值进行校验。 import javax.swing.*; import java.awt.*; import java.awt.event.*;public class IntegerCalculator extends JFrame implements ActionListener {private JCheckBox[] checkBoxe…

老杨说运维 | 基于业务全链路的端到端排障分析(文末附现场视频)

前言 青城山脚下的滔滔江水奔涌而过&#xff0c;承载着擎创一往无前的势头&#xff0c;共同去向未来。2024年6月&#xff0c;双态IT成都用户大会擎创科技“数智化可观测赋能双态运维”专场迎来了完满的收尾。 本期回顾来自擎创科技产品总监殷传旺的现场演讲&#xff1a;云原生…

封装了一个iOS联动滚动效果

效果图 实现逻辑和原理 就是在 didEndDisplayingCell 方法中通过indexPathsForVisibleItems 接口获取当前可见的cell对应的indexPath&#xff0c; 然后获取到item最小的那一个&#xff0c;即可&#xff0c;同时&#xff0c;还要在 willDisplayCell 方法中直接设置标题的选中属…

代码随想录算法训练营第三十四天|56. 合并区间、738.单调递增的数字、968.监控二叉树

56. 合并区间 题目链接&#xff1a;56. 合并区间 文档讲解&#xff1a;代码随想录 状态&#xff1a;无语&#xff0c;这题从右边界排序做不了&#xff01; 思路&#xff1a; 排序&#xff1a;按照区间的起始位置进行排序&#xff0c;这样后面处理时可以顺序合并重叠区间。合并…

Cortex-M Fault

Cortex-M CPU 会在系统发生故障时引发异常。非法内存写入和读取、访问未通电的外设、执行无效指令、除以零以及其他问题都可能导致此类异常。通常在所有情况下都会引发 HardFault 异常。对于某些故障&#xff0c;可以启用不同的异常来专门处理这些情况。 Cortex-M 故障异常 …

剪画小程序:视频文案提取神器:制作爆款视频的第一步!

在这个信息爆炸的时代&#xff0c;视频成为了我们获取知识和娱乐的重要途径。 但有时候&#xff0c;我们想要的不仅仅是观看视频&#xff0c;而是能够将其中精彩的文案提取出来&#xff0c;为自己的创作添砖加瓦。 现在&#xff0c;有一款神奇的工具应运而生&#xff0c;为您…

Linux-笔记 全志T113移植正点4.3寸RGB屏幕笔记

目录 前言 线序整理 软件 显示调试 触摸调试 背光调试 前言 由于手头有一块4.3寸的RGB屏幕(触摸IC为GT1151)&#xff0c;正好开发板上也有40Pin的RGB接口&#xff0c;就想着给移植一下&#xff0c;前期准备工作主要是整理好线序&#xff0c;然后用转接板与杜邦线连接验证好…

Vitis Accelerated Libraries 学习笔记--Vision 库的组织结构

1. 简介 Vision 库的组织结构如下&#xff1a; ├── L1/ │ ├── README.md │ ├── examples/ │ ├── include/ │ ├── lib/ │ └── tests/ ├── L2/ │ ├── README.md │ ├── examples/ │ └── tests/ ├── L3/ │ ├── R…

突破架构瓶颈:克服软件系统中的漂移和侵蚀

一种常见但不完美的比喻是将软件系统中的架构漂移和侵蚀与物理建筑的架构相比。虽然这个比喻很直观&#xff0c;但它存在一个根本性的误解&#xff0c;这也常常引发软件开发中的架构问题。 试想一下&#xff0c;一个设计良好的摩天大楼或房屋建成后&#xff0c;我们期望它基本保…

本地电脑配置不足,对工业仿真计算有哪些影响?

工业仿真计算对电脑的要求相对较高&#xff0c;这主要是因为仿真过程涉及到大量的数据处理和复杂的计算任务。一个高效的工业仿真系统需要强大的计算能力和稳定的运行环境&#xff0c;以确保仿真的准确性和实时性。 工业仿真对电脑配置有哪些要求 首先&#xff0c;工业仿真计算…

Prompt 提示词工程:翻译提示

近期在对计算机学习时&#xff0c;许多内容需要看原始的英文论文&#xff0c;对于我这种学渣来说特别不友好&#xff0c;&#x1f937;&#x1f3fb;‍♀️无奈只能一边看翻译&#xff0c;一边学习。 之前有搜到过专门的翻译工具&#xff0c;无奈都是按照字数算费用的&#xf…

都2024年了,现在互联网行情怎样?

都2024年了&#xff0c;互联网行情是怎样的&#xff1f; 很直白的说&#xff0c;依旧是差得很&#xff0c;怎么说&#xff1f; 我刚在掘金上看到一个掘友写的文章&#xff0c;他是四月领了大礼包&#xff0c;据他的描述如下&#xff1a; 互联网行情依旧是差得很&#xff0c;很…

自编码器笔记

编码器解码器自编码器 先压缩特征&#xff0c;再通过特征还原。 判断还原的和原来的是否相等 encode data 在一个“潜在空间”里。它的用途是“深度学习”的核心-学习数据的特征并简化数据表示形式以寻找模式。 变分自编码器&#xff1a; 1. 首先、假设输入数据是符合正态分布…

DDL-表操作-数据类型

一.DDL-表操作-数据类型 MySQL中的数据类型有很多,主要分为三类:数值类型,字符串类型,日期类型。 二.关系表 注意: 无符号和有符号的取值范围不是一样的,无符号需要加上UNSIGNED范围。 BLOB&#xff1a;用来描述二进制数据 TEXT:用来描述字符串 三.定长字符串和变长字符串 c…

【UE5.3】笔记1

内容浏览器&#xff1a;存放项目中所有的资源&#xff1a;关卡、蓝图类...... 关卡--Map 至少有一个关卡&#xff0c;可以有多个关卡 -漫游 视野漫游&#xff1a;鼠标右键WASD QE 鼠标滑轮控制摄像机速度 运行&#xff0c;ESC退出运行,快捷键F8不停止运行单独弹出功能 -创…

2024年第十五届蓝桥杯青少组大赛8月24日开启

据蓝桥杯青少组官网显示&#xff0c;2024年第十五届蓝桥杯青少组大赛8月24日开启。 蓝桥杯青少组历届题库地址&#xff1a;http://www.6547.cn/question/cat/2 蓝桥杯青少组历届真题下载&#xff1a;http://www.6547.cn/wenku/list/10